github.com/ryanslade/nomad@v0.2.4-0.20160128061903-fc95782f2089/nomad/node_endpoint_test.go (about) 1 package nomad 2 3 import ( 4 "reflect" 5 "testing" 6 "time" 7 8 "github.com/hashicorp/net-rpc-msgpackrpc" 9 "github.com/hashicorp/nomad/nomad/mock" 10 "github.com/hashicorp/nomad/nomad/structs" 11 "github.com/hashicorp/nomad/testutil" 12 ) 13 14 func TestClientEndpoint_Register(t *testing.T) { 15 s1 := testServer(t, nil) 16 defer s1.Shutdown() 17 codec := rpcClient(t, s1) 18 testutil.WaitForLeader(t, s1.RPC) 19 20 // Create the register request 21 node := mock.Node() 22 req := &structs.NodeRegisterRequest{ 23 Node: node, 24 WriteRequest: structs.WriteRequest{Region: "global"}, 25 } 26 27 // Fetch the response 28 var resp structs.GenericResponse 29 if err := msgpackrpc.CallWithCodec(codec, "Node.Register", req, &resp); err != nil { 30 t.Fatalf("err: %v", err) 31 } 32 if resp.Index == 0 { 33 t.Fatalf("bad index: %d", resp.Index) 34 } 35 36 // Check for the node in the FSM 37 state := s1.fsm.State() 38 out, err := state.NodeByID(node.ID) 39 if err != nil { 40 t.Fatalf("err: %v", err) 41 } 42 if out == nil { 43 t.Fatalf("expected node") 44 } 45 if out.CreateIndex != resp.Index { 46 t.Fatalf("index mis-match") 47 } 48 if out.ComputedClass == 0 { 49 t.Fatal("ComputedClass not set") 50 } 51 } 52 53 func TestClientEndpoint_Deregister(t *testing.T) { 54 s1 := testServer(t, nil) 55 defer s1.Shutdown() 56 codec := rpcClient(t, s1) 57 testutil.WaitForLeader(t, s1.RPC) 58 59 // Create the register request 60 node := mock.Node() 61 reg := &structs.NodeRegisterRequest{ 62 Node: node, 63 WriteRequest: structs.WriteRequest{Region: "global"}, 64 } 65 66 // Fetch the response 67 var resp structs.GenericResponse 68 if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil { 69 t.Fatalf("err: %v", err) 70 } 71 72 // Deregister 73 dereg := &structs.NodeDeregisterRequest{ 74 NodeID: node.ID, 75 WriteRequest: structs.WriteRequest{Region: "global"}, 76 } 77 var resp2 structs.GenericResponse 78 if err := msgpackrpc.CallWithCodec(codec, "Node.Deregister", dereg, &resp2); err != nil { 79 t.Fatalf("err: %v", err) 80 } 81 if resp2.Index == 0 { 82 t.Fatalf("bad index: %d", resp2.Index) 83 } 84 85 // Check for the node in the FSM 86 state := s1.fsm.State() 87 out, err := state.NodeByID(node.ID) 88 if err != nil { 89 t.Fatalf("err: %v", err) 90 } 91 if out != nil { 92 t.Fatalf("unexpected node") 93 } 94 } 95 96 func TestClientEndpoint_UpdateStatus(t *testing.T) { 97 s1 := testServer(t, nil) 98 defer s1.Shutdown() 99 codec := rpcClient(t, s1) 100 testutil.WaitForLeader(t, s1.RPC) 101 102 // Create the register request 103 node := mock.Node() 104 reg := &structs.NodeRegisterRequest{ 105 Node: node, 106 WriteRequest: structs.WriteRequest{Region: "global"}, 107 } 108 109 // Fetch the response 110 var resp structs.NodeUpdateResponse 111 if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil { 112 t.Fatalf("err: %v", err) 113 } 114 115 // Check for heartbeat interval 116 ttl := resp.HeartbeatTTL 117 if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL { 118 t.Fatalf("bad: %#v", ttl) 119 } 120 121 // Update the status 122 dereg := &structs.NodeUpdateStatusRequest{ 123 NodeID: node.ID, 124 Status: structs.NodeStatusInit, 125 WriteRequest: structs.WriteRequest{Region: "global"}, 126 } 127 var resp2 structs.NodeUpdateResponse 128 if err := msgpackrpc.CallWithCodec(codec, "Node.UpdateStatus", dereg, &resp2); err != nil { 129 t.Fatalf("err: %v", err) 130 } 131 if resp2.Index == 0 { 132 t.Fatalf("bad index: %d", resp2.Index) 133 } 134 135 // Check for heartbeat interval 136 ttl = resp2.HeartbeatTTL 137 if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL { 138 t.Fatalf("bad: %#v", ttl) 139 } 140 141 // Check for the node in the FSM 142 state := s1.fsm.State() 143 out, err := state.NodeByID(node.ID) 144 if err != nil { 145 t.Fatalf("err: %v", err) 146 } 147 if out == nil { 148 t.Fatalf("expected node") 149 } 150 if out.ModifyIndex != resp2.Index { 151 t.Fatalf("index mis-match") 152 } 153 } 154 155 func TestClientEndpoint_UpdateStatus_GetEvals(t *testing.T) { 156 s1 := testServer(t, nil) 157 defer s1.Shutdown() 158 codec := rpcClient(t, s1) 159 testutil.WaitForLeader(t, s1.RPC) 160 161 // Register a system job. 162 job := mock.SystemJob() 163 state := s1.fsm.State() 164 if err := state.UpsertJob(1, job); err != nil { 165 t.Fatalf("err: %v", err) 166 } 167 168 // Create the register request 169 node := mock.Node() 170 node.Status = structs.NodeStatusInit 171 reg := &structs.NodeRegisterRequest{ 172 Node: node, 173 WriteRequest: structs.WriteRequest{Region: "global"}, 174 } 175 176 // Fetch the response 177 var resp structs.NodeUpdateResponse 178 if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil { 179 t.Fatalf("err: %v", err) 180 } 181 182 // Check for heartbeat interval 183 ttl := resp.HeartbeatTTL 184 if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL { 185 t.Fatalf("bad: %#v", ttl) 186 } 187 188 // Update the status 189 update := &structs.NodeUpdateStatusRequest{ 190 NodeID: node.ID, 191 Status: structs.NodeStatusReady, 192 WriteRequest: structs.WriteRequest{Region: "global"}, 193 } 194 var resp2 structs.NodeUpdateResponse 195 if err := msgpackrpc.CallWithCodec(codec, "Node.UpdateStatus", update, &resp2); err != nil { 196 t.Fatalf("err: %v", err) 197 } 198 if resp2.Index == 0 { 199 t.Fatalf("bad index: %d", resp2.Index) 200 } 201 202 // Check for an eval caused by the system job. 203 if len(resp2.EvalIDs) != 1 { 204 t.Fatalf("expected one eval; got %#v", resp2.EvalIDs) 205 } 206 207 evalID := resp2.EvalIDs[0] 208 eval, err := state.EvalByID(evalID) 209 if err != nil { 210 t.Fatalf("could not get eval %v", evalID) 211 } 212 213 if eval.Type != "system" { 214 t.Fatalf("unexpected eval type; got %v; want %q", eval.Type, "system") 215 } 216 217 // Check for heartbeat interval 218 ttl = resp2.HeartbeatTTL 219 if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL { 220 t.Fatalf("bad: %#v", ttl) 221 } 222 223 // Check for the node in the FSM 224 out, err := state.NodeByID(node.ID) 225 if err != nil { 226 t.Fatalf("err: %v", err) 227 } 228 if out == nil { 229 t.Fatalf("expected node") 230 } 231 if out.ModifyIndex != resp2.Index { 232 t.Fatalf("index mis-match") 233 } 234 } 235 236 func TestClientEndpoint_UpdateStatus_HeartbeatOnly(t *testing.T) { 237 s1 := testServer(t, nil) 238 defer s1.Shutdown() 239 codec := rpcClient(t, s1) 240 testutil.WaitForLeader(t, s1.RPC) 241 242 // Create the register request 243 node := mock.Node() 244 reg := &structs.NodeRegisterRequest{ 245 Node: node, 246 WriteRequest: structs.WriteRequest{Region: "global"}, 247 } 248 249 // Fetch the response 250 var resp structs.NodeUpdateResponse 251 if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil { 252 t.Fatalf("err: %v", err) 253 } 254 255 // Check for heartbeat interval 256 ttl := resp.HeartbeatTTL 257 if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL { 258 t.Fatalf("bad: %#v", ttl) 259 } 260 261 // Update the status, static state 262 dereg := &structs.NodeUpdateStatusRequest{ 263 NodeID: node.ID, 264 Status: node.Status, 265 WriteRequest: structs.WriteRequest{Region: "global"}, 266 } 267 var resp2 structs.NodeUpdateResponse 268 if err := msgpackrpc.CallWithCodec(codec, "Node.UpdateStatus", dereg, &resp2); err != nil { 269 t.Fatalf("err: %v", err) 270 } 271 if resp2.Index != 0 { 272 t.Fatalf("bad index: %d", resp2.Index) 273 } 274 275 // Check for heartbeat interval 276 ttl = resp2.HeartbeatTTL 277 if ttl < s1.config.MinHeartbeatTTL || ttl > 2*s1.config.MinHeartbeatTTL { 278 t.Fatalf("bad: %#v", ttl) 279 } 280 } 281 282 func TestClientEndpoint_UpdateDrain(t *testing.T) { 283 s1 := testServer(t, nil) 284 defer s1.Shutdown() 285 codec := rpcClient(t, s1) 286 testutil.WaitForLeader(t, s1.RPC) 287 288 // Create the register request 289 node := mock.Node() 290 reg := &structs.NodeRegisterRequest{ 291 Node: node, 292 WriteRequest: structs.WriteRequest{Region: "global"}, 293 } 294 295 // Fetch the response 296 var resp structs.NodeUpdateResponse 297 if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil { 298 t.Fatalf("err: %v", err) 299 } 300 301 // Update the status 302 dereg := &structs.NodeUpdateDrainRequest{ 303 NodeID: node.ID, 304 Drain: true, 305 WriteRequest: structs.WriteRequest{Region: "global"}, 306 } 307 var resp2 structs.NodeDrainUpdateResponse 308 if err := msgpackrpc.CallWithCodec(codec, "Node.UpdateDrain", dereg, &resp2); err != nil { 309 t.Fatalf("err: %v", err) 310 } 311 if resp2.Index == 0 { 312 t.Fatalf("bad index: %d", resp2.Index) 313 } 314 315 // Check for the node in the FSM 316 state := s1.fsm.State() 317 out, err := state.NodeByID(node.ID) 318 if err != nil { 319 t.Fatalf("err: %v", err) 320 } 321 if !out.Drain { 322 t.Fatalf("bad: %#v", out) 323 } 324 } 325 326 func TestClientEndpoint_GetNode(t *testing.T) { 327 s1 := testServer(t, nil) 328 defer s1.Shutdown() 329 codec := rpcClient(t, s1) 330 testutil.WaitForLeader(t, s1.RPC) 331 332 // Create the register request 333 node := mock.Node() 334 reg := &structs.NodeRegisterRequest{ 335 Node: node, 336 WriteRequest: structs.WriteRequest{Region: "global"}, 337 } 338 339 // Fetch the response 340 var resp structs.GenericResponse 341 if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil { 342 t.Fatalf("err: %v", err) 343 } 344 node.CreateIndex = resp.Index 345 node.ModifyIndex = resp.Index 346 347 // Lookup the node 348 get := &structs.NodeSpecificRequest{ 349 NodeID: node.ID, 350 QueryOptions: structs.QueryOptions{Region: "global"}, 351 } 352 var resp2 structs.SingleNodeResponse 353 if err := msgpackrpc.CallWithCodec(codec, "Node.GetNode", get, &resp2); err != nil { 354 t.Fatalf("err: %v", err) 355 } 356 if resp2.Index != resp.Index { 357 t.Fatalf("Bad index: %d %d", resp2.Index, resp.Index) 358 } 359 360 if resp2.Node.ComputedClass == 0 { 361 t.Fatalf("bad ComputedClass: %#v", resp2.Node) 362 } 363 364 if !reflect.DeepEqual(node, resp2.Node) { 365 t.Fatalf("bad: %#v %#v", node, resp2.Node) 366 } 367 368 // Lookup non-existing node 369 get.NodeID = "12345678-abcd-efab-cdef-123456789abc" 370 if err := msgpackrpc.CallWithCodec(codec, "Node.GetNode", get, &resp2); err != nil { 371 t.Fatalf("err: %v", err) 372 } 373 if resp2.Index != resp.Index { 374 t.Fatalf("Bad index: %d %d", resp2.Index, resp.Index) 375 } 376 if resp2.Node != nil { 377 t.Fatalf("unexpected node") 378 } 379 } 380 381 func TestClientEndpoint_GetNode_Blocking(t *testing.T) { 382 s1 := testServer(t, nil) 383 defer s1.Shutdown() 384 state := s1.fsm.State() 385 codec := rpcClient(t, s1) 386 testutil.WaitForLeader(t, s1.RPC) 387 388 // Create the node 389 node1 := mock.Node() 390 node2 := mock.Node() 391 392 // First create an unrelated node. 393 time.AfterFunc(100*time.Millisecond, func() { 394 if err := state.UpsertNode(100, node1); err != nil { 395 t.Fatalf("err: %v", err) 396 } 397 }) 398 399 // Upsert the node we are watching later 400 time.AfterFunc(200*time.Millisecond, func() { 401 if err := state.UpsertNode(200, node2); err != nil { 402 t.Fatalf("err: %v", err) 403 } 404 }) 405 406 // Lookup the node 407 req := &structs.NodeSpecificRequest{ 408 NodeID: node2.ID, 409 QueryOptions: structs.QueryOptions{ 410 Region: "global", 411 MinQueryIndex: 50, 412 }, 413 } 414 var resp structs.SingleNodeResponse 415 start := time.Now() 416 if err := msgpackrpc.CallWithCodec(codec, "Node.GetNode", req, &resp); err != nil { 417 t.Fatalf("err: %v", err) 418 } 419 420 if elapsed := time.Since(start); elapsed < 200*time.Millisecond { 421 t.Fatalf("should block (returned in %s) %#v", elapsed, resp) 422 } 423 if resp.Index != 200 { 424 t.Fatalf("Bad index: %d %d", resp.Index, 200) 425 } 426 if resp.Node == nil || resp.Node.ID != node2.ID { 427 t.Fatalf("bad: %#v", resp.Node) 428 } 429 430 // Node update triggers watches 431 time.AfterFunc(100*time.Millisecond, func() { 432 nodeUpdate := mock.Node() 433 nodeUpdate.ID = node2.ID 434 nodeUpdate.Status = structs.NodeStatusDown 435 if err := state.UpsertNode(300, nodeUpdate); err != nil { 436 t.Fatalf("err: %v", err) 437 } 438 }) 439 440 req.QueryOptions.MinQueryIndex = 250 441 var resp2 structs.SingleNodeResponse 442 start = time.Now() 443 if err := msgpackrpc.CallWithCodec(codec, "Node.GetNode", req, &resp2); err != nil { 444 t.Fatalf("err: %v", err) 445 } 446 447 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 448 t.Fatalf("should block (returned in %s) %#v", elapsed, resp) 449 } 450 if resp2.Index != 300 { 451 t.Fatalf("Bad index: %d %d", resp2.Index, 300) 452 } 453 if resp2.Node == nil || resp2.Node.Status != structs.NodeStatusDown { 454 t.Fatalf("bad: %#v", resp2.Node) 455 } 456 457 // Node delete triggers watches 458 time.AfterFunc(100*time.Millisecond, func() { 459 if err := state.DeleteNode(400, node2.ID); err != nil { 460 t.Fatalf("err: %v", err) 461 } 462 }) 463 464 req.QueryOptions.MinQueryIndex = 350 465 var resp3 structs.SingleNodeResponse 466 start = time.Now() 467 if err := msgpackrpc.CallWithCodec(codec, "Node.GetNode", req, &resp3); err != nil { 468 t.Fatalf("err: %v", err) 469 } 470 471 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 472 t.Fatalf("should block (returned in %s) %#v", elapsed, resp) 473 } 474 if resp3.Index != 400 { 475 t.Fatalf("Bad index: %d %d", resp2.Index, 400) 476 } 477 if resp3.Node != nil { 478 t.Fatalf("bad: %#v", resp3.Node) 479 } 480 } 481 482 func TestClientEndpoint_GetAllocs(t *testing.T) { 483 s1 := testServer(t, nil) 484 defer s1.Shutdown() 485 codec := rpcClient(t, s1) 486 testutil.WaitForLeader(t, s1.RPC) 487 488 // Create the register request 489 node := mock.Node() 490 reg := &structs.NodeRegisterRequest{ 491 Node: node, 492 WriteRequest: structs.WriteRequest{Region: "global"}, 493 } 494 495 // Fetch the response 496 var resp structs.GenericResponse 497 if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil { 498 t.Fatalf("err: %v", err) 499 } 500 node.CreateIndex = resp.Index 501 node.ModifyIndex = resp.Index 502 503 // Inject fake evaluations 504 alloc := mock.Alloc() 505 alloc.NodeID = node.ID 506 state := s1.fsm.State() 507 err := state.UpsertAllocs(100, []*structs.Allocation{alloc}) 508 if err != nil { 509 t.Fatalf("err: %v", err) 510 } 511 512 // Lookup the allocs 513 get := &structs.NodeSpecificRequest{ 514 NodeID: node.ID, 515 QueryOptions: structs.QueryOptions{Region: "global"}, 516 } 517 var resp2 structs.NodeAllocsResponse 518 if err := msgpackrpc.CallWithCodec(codec, "Node.GetAllocs", get, &resp2); err != nil { 519 t.Fatalf("err: %v", err) 520 } 521 if resp2.Index != 100 { 522 t.Fatalf("Bad index: %d %d", resp2.Index, 100) 523 } 524 525 if len(resp2.Allocs) != 1 || resp2.Allocs[0].ID != alloc.ID { 526 t.Fatalf("bad: %#v", resp2.Allocs) 527 } 528 529 // Lookup non-existing node 530 get.NodeID = "foobarbaz" 531 if err := msgpackrpc.CallWithCodec(codec, "Node.GetAllocs", get, &resp2); err != nil { 532 t.Fatalf("err: %v", err) 533 } 534 if resp2.Index != 100 { 535 t.Fatalf("Bad index: %d %d", resp2.Index, 100) 536 } 537 if len(resp2.Allocs) != 0 { 538 t.Fatalf("unexpected node") 539 } 540 } 541 542 func TestClientEndpoint_GetAllocs_Blocking(t *testing.T) { 543 s1 := testServer(t, nil) 544 defer s1.Shutdown() 545 codec := rpcClient(t, s1) 546 testutil.WaitForLeader(t, s1.RPC) 547 548 // Create the register request 549 node := mock.Node() 550 reg := &structs.NodeRegisterRequest{ 551 Node: node, 552 WriteRequest: structs.WriteRequest{Region: "global"}, 553 } 554 555 // Fetch the response 556 var resp structs.GenericResponse 557 if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil { 558 t.Fatalf("err: %v", err) 559 } 560 node.CreateIndex = resp.Index 561 node.ModifyIndex = resp.Index 562 563 // Inject fake evaluations async 564 alloc := mock.Alloc() 565 alloc.NodeID = node.ID 566 state := s1.fsm.State() 567 start := time.Now() 568 time.AfterFunc(100*time.Millisecond, func() { 569 err := state.UpsertAllocs(100, []*structs.Allocation{alloc}) 570 if err != nil { 571 t.Fatalf("err: %v", err) 572 } 573 }) 574 575 // Lookup the allocs in a blocking query 576 req := &structs.NodeSpecificRequest{ 577 NodeID: node.ID, 578 QueryOptions: structs.QueryOptions{ 579 Region: "global", 580 MinQueryIndex: 50, 581 MaxQueryTime: time.Second, 582 }, 583 } 584 var resp2 structs.NodeAllocsResponse 585 if err := msgpackrpc.CallWithCodec(codec, "Node.GetAllocs", req, &resp2); err != nil { 586 t.Fatalf("err: %v", err) 587 } 588 589 // Should block at least 100ms 590 if time.Since(start) < 100*time.Millisecond { 591 t.Fatalf("too fast") 592 } 593 594 if resp2.Index != 100 { 595 t.Fatalf("Bad index: %d %d", resp2.Index, 100) 596 } 597 598 if len(resp2.Allocs) != 1 || resp2.Allocs[0].ID != alloc.ID { 599 t.Fatalf("bad: %#v", resp2.Allocs) 600 } 601 602 // Alloc updates fire watches 603 time.AfterFunc(100*time.Millisecond, func() { 604 allocUpdate := mock.Alloc() 605 allocUpdate.NodeID = alloc.NodeID 606 allocUpdate.ID = alloc.ID 607 allocUpdate.ClientStatus = structs.AllocClientStatusRunning 608 err := state.UpdateAllocFromClient(200, allocUpdate) 609 if err != nil { 610 t.Fatalf("err: %v", err) 611 } 612 }) 613 614 req.QueryOptions.MinQueryIndex = 150 615 var resp3 structs.NodeAllocsResponse 616 if err := msgpackrpc.CallWithCodec(codec, "Node.GetAllocs", req, &resp3); err != nil { 617 t.Fatalf("err: %v", err) 618 } 619 620 if time.Since(start) < 100*time.Millisecond { 621 t.Fatalf("too fast") 622 } 623 if resp3.Index != 200 { 624 t.Fatalf("Bad index: %d %d", resp3.Index, 200) 625 } 626 if len(resp3.Allocs) != 1 || resp3.Allocs[0].ClientStatus != structs.AllocClientStatusRunning { 627 t.Fatalf("bad: %#v", resp3.Allocs[0]) 628 } 629 } 630 631 func TestClientEndpoint_UpdateAlloc(t *testing.T) { 632 s1 := testServer(t, nil) 633 defer s1.Shutdown() 634 codec := rpcClient(t, s1) 635 testutil.WaitForLeader(t, s1.RPC) 636 637 // Create the register request 638 node := mock.Node() 639 reg := &structs.NodeRegisterRequest{ 640 Node: node, 641 WriteRequest: structs.WriteRequest{Region: "global"}, 642 } 643 644 // Fetch the response 645 var resp structs.GenericResponse 646 if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil { 647 t.Fatalf("err: %v", err) 648 } 649 650 // Inject fake evaluations 651 alloc := mock.Alloc() 652 alloc.NodeID = node.ID 653 state := s1.fsm.State() 654 err := state.UpsertAllocs(100, []*structs.Allocation{alloc}) 655 if err != nil { 656 t.Fatalf("err: %v", err) 657 } 658 659 // Attempt update 660 clientAlloc := new(structs.Allocation) 661 *clientAlloc = *alloc 662 clientAlloc.ClientStatus = structs.AllocClientStatusFailed 663 664 // Update the alloc 665 update := &structs.AllocUpdateRequest{ 666 Alloc: []*structs.Allocation{clientAlloc}, 667 WriteRequest: structs.WriteRequest{Region: "global"}, 668 } 669 var resp2 structs.NodeAllocsResponse 670 if err := msgpackrpc.CallWithCodec(codec, "Node.UpdateAlloc", update, &resp2); err != nil { 671 t.Fatalf("err: %v", err) 672 } 673 if resp2.Index == 0 { 674 t.Fatalf("Bad index: %d", resp2.Index) 675 } 676 677 // Lookup the alloc 678 out, err := state.AllocByID(alloc.ID) 679 if err != nil { 680 t.Fatalf("err: %v", err) 681 } 682 if out.ClientStatus != structs.AllocClientStatusFailed { 683 t.Fatalf("Bad: %#v", out) 684 } 685 } 686 687 func TestClientEndpoint_CreateNodeEvals(t *testing.T) { 688 s1 := testServer(t, nil) 689 defer s1.Shutdown() 690 testutil.WaitForLeader(t, s1.RPC) 691 692 // Inject fake evaluations 693 alloc := mock.Alloc() 694 state := s1.fsm.State() 695 if err := state.UpsertAllocs(1, []*structs.Allocation{alloc}); err != nil { 696 t.Fatalf("err: %v", err) 697 } 698 699 // Inject a fake system job. 700 job := mock.SystemJob() 701 if err := state.UpsertJob(1, job); err != nil { 702 t.Fatalf("err: %v", err) 703 } 704 705 // Create some evaluations 706 ids, index, err := s1.endpoints.Node.createNodeEvals(alloc.NodeID, 1) 707 if err != nil { 708 t.Fatalf("err: %v", err) 709 } 710 if index == 0 { 711 t.Fatalf("bad: %d", index) 712 } 713 if len(ids) != 2 { 714 t.Fatalf("bad: %s", ids) 715 } 716 717 // Lookup the evaluations 718 evalByType := make(map[string]*structs.Evaluation, 2) 719 for _, id := range ids { 720 eval, err := state.EvalByID(id) 721 if err != nil { 722 t.Fatalf("err: %v", err) 723 } 724 if eval == nil { 725 t.Fatalf("expected eval") 726 } 727 728 if old, ok := evalByType[eval.Type]; ok { 729 t.Fatalf("multiple evals of the same type: %v and %v", old, eval) 730 } 731 732 evalByType[eval.Type] = eval 733 } 734 735 if len(evalByType) != 2 { 736 t.Fatalf("Expected a service and system job; got %#v", evalByType) 737 } 738 739 // Ensure the evals are correct. 740 for schedType, eval := range evalByType { 741 expPriority := alloc.Job.Priority 742 expJobID := alloc.JobID 743 if schedType == "system" { 744 expPriority = job.Priority 745 expJobID = job.ID 746 } 747 748 if eval.CreateIndex != index { 749 t.Fatalf("CreateIndex mis-match on type %v: %#v", schedType, eval) 750 } 751 if eval.TriggeredBy != structs.EvalTriggerNodeUpdate { 752 t.Fatalf("TriggeredBy incorrect on type %v: %#v", schedType, eval) 753 } 754 if eval.NodeID != alloc.NodeID { 755 t.Fatalf("NodeID incorrect on type %v: %#v", schedType, eval) 756 } 757 if eval.NodeModifyIndex != 1 { 758 t.Fatalf("NodeModifyIndex incorrect on type %v: %#v", schedType, eval) 759 } 760 if eval.Status != structs.EvalStatusPending { 761 t.Fatalf("Status incorrect on type %v: %#v", schedType, eval) 762 } 763 if eval.Priority != expPriority { 764 t.Fatalf("Priority incorrect on type %v: %#v", schedType, eval) 765 } 766 if eval.JobID != expJobID { 767 t.Fatalf("JobID incorrect on type %v: %#v", schedType, eval) 768 } 769 } 770 } 771 772 func TestClientEndpoint_Evaluate(t *testing.T) { 773 s1 := testServer(t, func(c *Config) { 774 c.NumSchedulers = 0 // Prevent automatic dequeue 775 }) 776 defer s1.Shutdown() 777 codec := rpcClient(t, s1) 778 testutil.WaitForLeader(t, s1.RPC) 779 780 // Inject fake evaluations 781 alloc := mock.Alloc() 782 node := mock.Node() 783 node.ID = alloc.NodeID 784 state := s1.fsm.State() 785 err := state.UpsertNode(1, node) 786 if err != nil { 787 t.Fatalf("err: %v", err) 788 } 789 err = state.UpsertAllocs(2, []*structs.Allocation{alloc}) 790 if err != nil { 791 t.Fatalf("err: %v", err) 792 } 793 794 // Re-evaluate 795 req := &structs.NodeEvaluateRequest{ 796 NodeID: alloc.NodeID, 797 WriteRequest: structs.WriteRequest{Region: "global"}, 798 } 799 800 // Fetch the response 801 var resp structs.NodeUpdateResponse 802 if err := msgpackrpc.CallWithCodec(codec, "Node.Evaluate", req, &resp); err != nil { 803 t.Fatalf("err: %v", err) 804 } 805 if resp.Index == 0 { 806 t.Fatalf("bad index: %d", resp.Index) 807 } 808 809 // Create some evaluations 810 ids := resp.EvalIDs 811 if len(ids) != 1 { 812 t.Fatalf("bad: %s", ids) 813 } 814 815 // Lookup the evaluation 816 eval, err := state.EvalByID(ids[0]) 817 if err != nil { 818 t.Fatalf("err: %v", err) 819 } 820 if eval == nil { 821 t.Fatalf("expected eval") 822 } 823 if eval.CreateIndex != resp.Index { 824 t.Fatalf("index mis-match") 825 } 826 827 if eval.Priority != alloc.Job.Priority { 828 t.Fatalf("bad: %#v", eval) 829 } 830 if eval.Type != alloc.Job.Type { 831 t.Fatalf("bad: %#v", eval) 832 } 833 if eval.TriggeredBy != structs.EvalTriggerNodeUpdate { 834 t.Fatalf("bad: %#v", eval) 835 } 836 if eval.JobID != alloc.JobID { 837 t.Fatalf("bad: %#v", eval) 838 } 839 if eval.NodeID != alloc.NodeID { 840 t.Fatalf("bad: %#v", eval) 841 } 842 if eval.NodeModifyIndex != 1 { 843 t.Fatalf("bad: %#v", eval) 844 } 845 if eval.Status != structs.EvalStatusPending { 846 t.Fatalf("bad: %#v", eval) 847 } 848 } 849 850 func TestClientEndpoint_ListNodes(t *testing.T) { 851 s1 := testServer(t, nil) 852 defer s1.Shutdown() 853 codec := rpcClient(t, s1) 854 testutil.WaitForLeader(t, s1.RPC) 855 856 // Create the register request 857 node := mock.Node() 858 reg := &structs.NodeRegisterRequest{ 859 Node: node, 860 WriteRequest: structs.WriteRequest{Region: "global"}, 861 } 862 863 // Fetch the response 864 var resp structs.GenericResponse 865 if err := msgpackrpc.CallWithCodec(codec, "Node.Register", reg, &resp); err != nil { 866 t.Fatalf("err: %v", err) 867 } 868 node.CreateIndex = resp.Index 869 node.ModifyIndex = resp.Index 870 871 // Lookup the node 872 get := &structs.NodeListRequest{ 873 QueryOptions: structs.QueryOptions{Region: "global"}, 874 } 875 var resp2 structs.NodeListResponse 876 if err := msgpackrpc.CallWithCodec(codec, "Node.List", get, &resp2); err != nil { 877 t.Fatalf("err: %v", err) 878 } 879 if resp2.Index != resp.Index { 880 t.Fatalf("Bad index: %d %d", resp2.Index, resp.Index) 881 } 882 883 if len(resp2.Nodes) != 1 { 884 t.Fatalf("bad: %#v", resp2.Nodes) 885 } 886 if resp2.Nodes[0].ID != node.ID { 887 t.Fatalf("bad: %#v", resp2.Nodes[0]) 888 } 889 890 // Lookup the node with prefix 891 get = &structs.NodeListRequest{ 892 QueryOptions: structs.QueryOptions{Region: "global", Prefix: node.ID[:4]}, 893 } 894 var resp3 structs.NodeListResponse 895 if err := msgpackrpc.CallWithCodec(codec, "Node.List", get, &resp3); err != nil { 896 t.Fatalf("err: %v", err) 897 } 898 if resp3.Index != resp.Index { 899 t.Fatalf("Bad index: %d %d", resp3.Index, resp2.Index) 900 } 901 902 if len(resp3.Nodes) != 1 { 903 t.Fatalf("bad: %#v", resp3.Nodes) 904 } 905 if resp3.Nodes[0].ID != node.ID { 906 t.Fatalf("bad: %#v", resp3.Nodes[0]) 907 } 908 } 909 910 func TestClientEndpoint_ListNodes_Blocking(t *testing.T) { 911 s1 := testServer(t, nil) 912 defer s1.Shutdown() 913 state := s1.fsm.State() 914 codec := rpcClient(t, s1) 915 testutil.WaitForLeader(t, s1.RPC) 916 917 // Create the node 918 node := mock.Node() 919 920 // Node upsert triggers watches 921 time.AfterFunc(100*time.Millisecond, func() { 922 if err := state.UpsertNode(2, node); err != nil { 923 t.Fatalf("err: %v", err) 924 } 925 }) 926 927 req := &structs.NodeListRequest{ 928 QueryOptions: structs.QueryOptions{ 929 Region: "global", 930 MinQueryIndex: 1, 931 }, 932 } 933 start := time.Now() 934 var resp structs.NodeListResponse 935 if err := msgpackrpc.CallWithCodec(codec, "Node.List", req, &resp); err != nil { 936 t.Fatalf("err: %v", err) 937 } 938 939 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 940 t.Fatalf("should block (returned in %s) %#v", elapsed, resp) 941 } 942 if resp.Index != 2 { 943 t.Fatalf("Bad index: %d %d", resp.Index, 2) 944 } 945 if len(resp.Nodes) != 1 || resp.Nodes[0].ID != node.ID { 946 t.Fatalf("bad: %#v", resp.Nodes) 947 } 948 949 // Node drain updates trigger watches. 950 time.AfterFunc(100*time.Millisecond, func() { 951 if err := state.UpdateNodeDrain(3, node.ID, true); err != nil { 952 t.Fatalf("err: %v", err) 953 } 954 }) 955 956 req.MinQueryIndex = 2 957 var resp2 structs.NodeListResponse 958 start = time.Now() 959 if err := msgpackrpc.CallWithCodec(codec, "Node.List", req, &resp2); err != nil { 960 t.Fatalf("err: %v", err) 961 } 962 963 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 964 t.Fatalf("should block (returned in %s) %#v", elapsed, resp2) 965 } 966 if resp2.Index != 3 { 967 t.Fatalf("Bad index: %d %d", resp2.Index, 3) 968 } 969 if len(resp2.Nodes) != 1 || !resp2.Nodes[0].Drain { 970 t.Fatalf("bad: %#v", resp2.Nodes) 971 } 972 973 // Node status update triggers watches 974 time.AfterFunc(100*time.Millisecond, func() { 975 if err := state.UpdateNodeStatus(4, node.ID, structs.NodeStatusDown); err != nil { 976 t.Fatalf("err: %v", err) 977 } 978 }) 979 980 req.MinQueryIndex = 3 981 var resp3 structs.NodeListResponse 982 start = time.Now() 983 if err := msgpackrpc.CallWithCodec(codec, "Node.List", req, &resp3); err != nil { 984 t.Fatalf("err: %v", err) 985 } 986 987 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 988 t.Fatalf("should block (returned in %s) %#v", elapsed, resp3) 989 } 990 if resp3.Index != 4 { 991 t.Fatalf("Bad index: %d %d", resp3.Index, 4) 992 } 993 if len(resp3.Nodes) != 1 || resp3.Nodes[0].Status != structs.NodeStatusDown { 994 t.Fatalf("bad: %#v", resp3.Nodes) 995 } 996 997 // Node delete triggers watches. 998 time.AfterFunc(100*time.Millisecond, func() { 999 if err := state.DeleteNode(5, node.ID); err != nil { 1000 t.Fatalf("err: %v", err) 1001 } 1002 }) 1003 1004 req.MinQueryIndex = 4 1005 var resp4 structs.NodeListResponse 1006 start = time.Now() 1007 if err := msgpackrpc.CallWithCodec(codec, "Node.List", req, &resp4); err != nil { 1008 t.Fatalf("err: %v", err) 1009 } 1010 1011 if elapsed := time.Since(start); elapsed < 100*time.Millisecond { 1012 t.Fatalf("should block (returned in %s) %#v", elapsed, resp4) 1013 } 1014 if resp4.Index != 5 { 1015 t.Fatalf("Bad index: %d %d", resp4.Index, 5) 1016 } 1017 if len(resp4.Nodes) != 0 { 1018 t.Fatalf("bad: %#v", resp4.Nodes) 1019 } 1020 }