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