github.com/dkerwin/nomad@v0.3.3-0.20160525181927-74554135514b/nomad/fsm_test.go (about) 1 package nomad 2 3 import ( 4 "bytes" 5 "fmt" 6 "os" 7 "reflect" 8 "testing" 9 "time" 10 11 "github.com/hashicorp/nomad/nomad/mock" 12 "github.com/hashicorp/nomad/nomad/state" 13 "github.com/hashicorp/nomad/nomad/structs" 14 "github.com/hashicorp/nomad/testutil" 15 "github.com/hashicorp/raft" 16 ) 17 18 type MockSink struct { 19 *bytes.Buffer 20 cancel bool 21 } 22 23 func (m *MockSink) ID() string { 24 return "Mock" 25 } 26 27 func (m *MockSink) Cancel() error { 28 m.cancel = true 29 return nil 30 } 31 32 func (m *MockSink) Close() error { 33 return nil 34 } 35 36 func testStateStore(t *testing.T) *state.StateStore { 37 state, err := state.NewStateStore(os.Stderr) 38 if err != nil { 39 t.Fatalf("err: %v", err) 40 } 41 if state == nil { 42 t.Fatalf("missing state") 43 } 44 return state 45 } 46 47 func testFSM(t *testing.T) *nomadFSM { 48 p, _ := testPeriodicDispatcher() 49 broker := testBroker(t, 0) 50 blocked := NewBlockedEvals(broker) 51 fsm, err := NewFSM(broker, p, blocked, os.Stderr) 52 if err != nil { 53 t.Fatalf("err: %v", err) 54 } 55 if fsm == nil { 56 t.Fatalf("missing fsm") 57 } 58 return fsm 59 } 60 61 func makeLog(buf []byte) *raft.Log { 62 return &raft.Log{ 63 Index: 1, 64 Term: 1, 65 Type: raft.LogCommand, 66 Data: buf, 67 } 68 } 69 70 func TestFSM_UpsertNode(t *testing.T) { 71 fsm := testFSM(t) 72 fsm.blockedEvals.SetEnabled(true) 73 74 node := mock.Node() 75 76 // Mark an eval as blocked. 77 eval := mock.Eval() 78 eval.ClassEligibility = map[string]bool{node.ComputedClass: true} 79 fsm.blockedEvals.Block(eval) 80 81 req := structs.NodeRegisterRequest{ 82 Node: node, 83 } 84 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 85 if err != nil { 86 t.Fatalf("err: %v", err) 87 } 88 89 resp := fsm.Apply(makeLog(buf)) 90 if resp != nil { 91 t.Fatalf("resp: %v", resp) 92 } 93 94 // Verify we are registered 95 n, err := fsm.State().NodeByID(req.Node.ID) 96 if err != nil { 97 t.Fatalf("err: %v", err) 98 } 99 if n == nil { 100 t.Fatalf("not found!") 101 } 102 if n.CreateIndex != 1 { 103 t.Fatalf("bad index: %d", node.CreateIndex) 104 } 105 106 tt := fsm.TimeTable() 107 index := tt.NearestIndex(time.Now().UTC()) 108 if index != 1 { 109 t.Fatalf("bad: %d", index) 110 } 111 112 // Verify the eval was unblocked. 113 testutil.WaitForResult(func() (bool, error) { 114 bStats := fsm.blockedEvals.Stats() 115 if bStats.TotalBlocked != 0 { 116 return false, fmt.Errorf("bad: %#v", bStats) 117 } 118 return true, nil 119 }, func(err error) { 120 t.Fatalf("err: %s", err) 121 }) 122 123 } 124 125 func TestFSM_DeregisterNode(t *testing.T) { 126 fsm := testFSM(t) 127 128 node := mock.Node() 129 req := structs.NodeRegisterRequest{ 130 Node: node, 131 } 132 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 133 if err != nil { 134 t.Fatalf("err: %v", err) 135 } 136 137 resp := fsm.Apply(makeLog(buf)) 138 if resp != nil { 139 t.Fatalf("resp: %v", resp) 140 } 141 142 req2 := structs.NodeDeregisterRequest{ 143 NodeID: node.ID, 144 } 145 buf, err = structs.Encode(structs.NodeDeregisterRequestType, req2) 146 if err != nil { 147 t.Fatalf("err: %v", err) 148 } 149 150 resp = fsm.Apply(makeLog(buf)) 151 if resp != nil { 152 t.Fatalf("resp: %v", resp) 153 } 154 155 // Verify we are NOT registered 156 node, err = fsm.State().NodeByID(req.Node.ID) 157 if err != nil { 158 t.Fatalf("err: %v", err) 159 } 160 if node != nil { 161 t.Fatalf("node found!") 162 } 163 } 164 165 func TestFSM_UpdateNodeStatus(t *testing.T) { 166 fsm := testFSM(t) 167 fsm.blockedEvals.SetEnabled(true) 168 169 node := mock.Node() 170 req := structs.NodeRegisterRequest{ 171 Node: node, 172 } 173 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 174 if err != nil { 175 t.Fatalf("err: %v", err) 176 } 177 178 resp := fsm.Apply(makeLog(buf)) 179 if resp != nil { 180 t.Fatalf("resp: %v", resp) 181 } 182 183 // Mark an eval as blocked. 184 eval := mock.Eval() 185 eval.ClassEligibility = map[string]bool{node.ComputedClass: true} 186 fsm.blockedEvals.Block(eval) 187 188 req2 := structs.NodeUpdateStatusRequest{ 189 NodeID: node.ID, 190 Status: structs.NodeStatusReady, 191 } 192 buf, err = structs.Encode(structs.NodeUpdateStatusRequestType, req2) 193 if err != nil { 194 t.Fatalf("err: %v", err) 195 } 196 197 resp = fsm.Apply(makeLog(buf)) 198 if resp != nil { 199 t.Fatalf("resp: %v", resp) 200 } 201 202 // Verify the status is ready. 203 node, err = fsm.State().NodeByID(req.Node.ID) 204 if err != nil { 205 t.Fatalf("err: %v", err) 206 } 207 if node.Status != structs.NodeStatusReady { 208 t.Fatalf("bad node: %#v", node) 209 } 210 211 // Verify the eval was unblocked. 212 testutil.WaitForResult(func() (bool, error) { 213 bStats := fsm.blockedEvals.Stats() 214 if bStats.TotalBlocked != 0 { 215 return false, fmt.Errorf("bad: %#v", bStats) 216 } 217 return true, nil 218 }, func(err error) { 219 t.Fatalf("err: %s", err) 220 }) 221 } 222 223 func TestFSM_UpdateNodeDrain(t *testing.T) { 224 fsm := testFSM(t) 225 226 node := mock.Node() 227 req := structs.NodeRegisterRequest{ 228 Node: node, 229 } 230 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 231 if err != nil { 232 t.Fatalf("err: %v", err) 233 } 234 235 resp := fsm.Apply(makeLog(buf)) 236 if resp != nil { 237 t.Fatalf("resp: %v", resp) 238 } 239 240 req2 := structs.NodeUpdateDrainRequest{ 241 NodeID: node.ID, 242 Drain: true, 243 } 244 buf, err = structs.Encode(structs.NodeUpdateDrainRequestType, req2) 245 if err != nil { 246 t.Fatalf("err: %v", err) 247 } 248 249 resp = fsm.Apply(makeLog(buf)) 250 if resp != nil { 251 t.Fatalf("resp: %v", resp) 252 } 253 254 // Verify we are NOT registered 255 node, err = fsm.State().NodeByID(req.Node.ID) 256 if err != nil { 257 t.Fatalf("err: %v", err) 258 } 259 if !node.Drain { 260 t.Fatalf("bad node: %#v", node) 261 } 262 } 263 264 func TestFSM_RegisterJob(t *testing.T) { 265 fsm := testFSM(t) 266 267 job := mock.PeriodicJob() 268 req := structs.JobRegisterRequest{ 269 Job: job, 270 } 271 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 272 if err != nil { 273 t.Fatalf("err: %v", err) 274 } 275 276 resp := fsm.Apply(makeLog(buf)) 277 if resp != nil { 278 t.Fatalf("resp: %v", resp) 279 } 280 281 // Verify we are registered 282 jobOut, err := fsm.State().JobByID(req.Job.ID) 283 if err != nil { 284 t.Fatalf("err: %v", err) 285 } 286 if jobOut == nil { 287 t.Fatalf("not found!") 288 } 289 if jobOut.CreateIndex != 1 { 290 t.Fatalf("bad index: %d", jobOut.CreateIndex) 291 } 292 293 // Verify it was added to the periodic runner. 294 if _, ok := fsm.periodicDispatcher.tracked[job.ID]; !ok { 295 t.Fatal("job not added to periodic runner") 296 } 297 298 // Verify the launch time was tracked. 299 launchOut, err := fsm.State().PeriodicLaunchByID(req.Job.ID) 300 if err != nil { 301 t.Fatalf("err: %v", err) 302 } 303 if launchOut == nil { 304 t.Fatalf("not found!") 305 } 306 if launchOut.Launch.IsZero() { 307 t.Fatalf("bad launch time: %v", launchOut.Launch) 308 } 309 } 310 311 func TestFSM_DeregisterJob(t *testing.T) { 312 fsm := testFSM(t) 313 314 job := mock.PeriodicJob() 315 req := structs.JobRegisterRequest{ 316 Job: job, 317 } 318 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 319 if err != nil { 320 t.Fatalf("err: %v", err) 321 } 322 323 resp := fsm.Apply(makeLog(buf)) 324 if resp != nil { 325 t.Fatalf("resp: %v", resp) 326 } 327 328 req2 := structs.JobDeregisterRequest{ 329 JobID: job.ID, 330 } 331 buf, err = structs.Encode(structs.JobDeregisterRequestType, req2) 332 if err != nil { 333 t.Fatalf("err: %v", err) 334 } 335 336 resp = fsm.Apply(makeLog(buf)) 337 if resp != nil { 338 t.Fatalf("resp: %v", resp) 339 } 340 341 // Verify we are NOT registered 342 jobOut, err := fsm.State().JobByID(req.Job.ID) 343 if err != nil { 344 t.Fatalf("err: %v", err) 345 } 346 if jobOut != nil { 347 t.Fatalf("job found!") 348 } 349 350 // Verify it was removed from the periodic runner. 351 if _, ok := fsm.periodicDispatcher.tracked[job.ID]; ok { 352 t.Fatal("job not removed from periodic runner") 353 } 354 355 // Verify it was removed from the periodic launch table. 356 launchOut, err := fsm.State().PeriodicLaunchByID(req.Job.ID) 357 if err != nil { 358 t.Fatalf("err: %v", err) 359 } 360 if launchOut != nil { 361 t.Fatalf("launch found!") 362 } 363 } 364 365 func TestFSM_UpdateEval(t *testing.T) { 366 fsm := testFSM(t) 367 fsm.evalBroker.SetEnabled(true) 368 369 req := structs.EvalUpdateRequest{ 370 Evals: []*structs.Evaluation{mock.Eval()}, 371 } 372 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 373 if err != nil { 374 t.Fatalf("err: %v", err) 375 } 376 377 resp := fsm.Apply(makeLog(buf)) 378 if resp != nil { 379 t.Fatalf("resp: %v", resp) 380 } 381 382 // Verify we are registered 383 eval, err := fsm.State().EvalByID(req.Evals[0].ID) 384 if err != nil { 385 t.Fatalf("err: %v", err) 386 } 387 if eval == nil { 388 t.Fatalf("not found!") 389 } 390 if eval.CreateIndex != 1 { 391 t.Fatalf("bad index: %d", eval.CreateIndex) 392 } 393 394 // Verify enqueued 395 stats := fsm.evalBroker.Stats() 396 if stats.TotalReady != 1 { 397 t.Fatalf("bad: %#v %#v", stats, eval) 398 } 399 } 400 401 func TestFSM_UpdateEval_Blocked(t *testing.T) { 402 fsm := testFSM(t) 403 fsm.evalBroker.SetEnabled(true) 404 fsm.blockedEvals.SetEnabled(true) 405 406 // Create a blocked eval. 407 eval := mock.Eval() 408 eval.Status = structs.EvalStatusBlocked 409 410 req := structs.EvalUpdateRequest{ 411 Evals: []*structs.Evaluation{eval}, 412 } 413 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 414 if err != nil { 415 t.Fatalf("err: %v", err) 416 } 417 418 resp := fsm.Apply(makeLog(buf)) 419 if resp != nil { 420 t.Fatalf("resp: %v", resp) 421 } 422 423 // Verify we are registered 424 out, err := fsm.State().EvalByID(eval.ID) 425 if err != nil { 426 t.Fatalf("err: %v", err) 427 } 428 if out == nil { 429 t.Fatalf("not found!") 430 } 431 if out.CreateIndex != 1 { 432 t.Fatalf("bad index: %d", out.CreateIndex) 433 } 434 435 // Verify the eval wasn't enqueued 436 stats := fsm.evalBroker.Stats() 437 if stats.TotalReady != 0 { 438 t.Fatalf("bad: %#v %#v", stats, out) 439 } 440 441 // Verify the eval was added to the blocked tracker. 442 bStats := fsm.blockedEvals.Stats() 443 if bStats.TotalBlocked != 1 { 444 t.Fatalf("bad: %#v %#v", bStats, out) 445 } 446 } 447 448 func TestFSM_DeleteEval(t *testing.T) { 449 fsm := testFSM(t) 450 451 eval := mock.Eval() 452 req := structs.EvalUpdateRequest{ 453 Evals: []*structs.Evaluation{eval}, 454 } 455 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 456 if err != nil { 457 t.Fatalf("err: %v", err) 458 } 459 460 resp := fsm.Apply(makeLog(buf)) 461 if resp != nil { 462 t.Fatalf("resp: %v", resp) 463 } 464 465 req2 := structs.EvalDeleteRequest{ 466 Evals: []string{eval.ID}, 467 } 468 buf, err = structs.Encode(structs.EvalDeleteRequestType, req2) 469 if err != nil { 470 t.Fatalf("err: %v", err) 471 } 472 473 resp = fsm.Apply(makeLog(buf)) 474 if resp != nil { 475 t.Fatalf("resp: %v", resp) 476 } 477 478 // Verify we are NOT registered 479 eval, err = fsm.State().EvalByID(req.Evals[0].ID) 480 if err != nil { 481 t.Fatalf("err: %v", err) 482 } 483 if eval != nil { 484 t.Fatalf("eval found!") 485 } 486 } 487 488 func TestFSM_UpsertAllocs(t *testing.T) { 489 fsm := testFSM(t) 490 491 alloc := mock.Alloc() 492 req := structs.AllocUpdateRequest{ 493 Alloc: []*structs.Allocation{alloc}, 494 } 495 buf, err := structs.Encode(structs.AllocUpdateRequestType, req) 496 if err != nil { 497 t.Fatalf("err: %v", err) 498 } 499 500 resp := fsm.Apply(makeLog(buf)) 501 if resp != nil { 502 t.Fatalf("resp: %v", resp) 503 } 504 505 // Verify we are registered 506 out, err := fsm.State().AllocByID(alloc.ID) 507 if err != nil { 508 t.Fatalf("err: %v", err) 509 } 510 alloc.CreateIndex = out.CreateIndex 511 alloc.ModifyIndex = out.ModifyIndex 512 alloc.AllocModifyIndex = out.AllocModifyIndex 513 if !reflect.DeepEqual(alloc, out) { 514 t.Fatalf("bad: %#v %#v", alloc, out) 515 } 516 517 evictAlloc := new(structs.Allocation) 518 *evictAlloc = *alloc 519 evictAlloc.DesiredStatus = structs.AllocDesiredStatusEvict 520 req2 := structs.AllocUpdateRequest{ 521 Alloc: []*structs.Allocation{evictAlloc}, 522 } 523 buf, err = structs.Encode(structs.AllocUpdateRequestType, req2) 524 if err != nil { 525 t.Fatalf("err: %v", err) 526 } 527 528 resp = fsm.Apply(makeLog(buf)) 529 if resp != nil { 530 t.Fatalf("resp: %v", resp) 531 } 532 533 // Verify we are evicted 534 out, err = fsm.State().AllocByID(alloc.ID) 535 if err != nil { 536 t.Fatalf("err: %v", err) 537 } 538 if out.DesiredStatus != structs.AllocDesiredStatusEvict { 539 t.Fatalf("alloc found!") 540 } 541 } 542 543 func TestFSM_UpsertAllocs_SharedJob(t *testing.T) { 544 fsm := testFSM(t) 545 546 alloc := mock.Alloc() 547 job := alloc.Job 548 alloc.Job = nil 549 req := structs.AllocUpdateRequest{ 550 Job: job, 551 Alloc: []*structs.Allocation{alloc}, 552 } 553 buf, err := structs.Encode(structs.AllocUpdateRequestType, req) 554 if err != nil { 555 t.Fatalf("err: %v", err) 556 } 557 558 resp := fsm.Apply(makeLog(buf)) 559 if resp != nil { 560 t.Fatalf("resp: %v", resp) 561 } 562 563 // Verify we are registered 564 out, err := fsm.State().AllocByID(alloc.ID) 565 if err != nil { 566 t.Fatalf("err: %v", err) 567 } 568 alloc.CreateIndex = out.CreateIndex 569 alloc.ModifyIndex = out.ModifyIndex 570 alloc.AllocModifyIndex = out.AllocModifyIndex 571 572 // Job should be re-attached 573 alloc.Job = job 574 if !reflect.DeepEqual(alloc, out) { 575 t.Fatalf("bad: %#v %#v", alloc, out) 576 } 577 578 evictAlloc := new(structs.Allocation) 579 *evictAlloc = *alloc 580 job = evictAlloc.Job 581 evictAlloc.Job = nil 582 evictAlloc.DesiredStatus = structs.AllocDesiredStatusEvict 583 req2 := structs.AllocUpdateRequest{ 584 Job: job, 585 Alloc: []*structs.Allocation{evictAlloc}, 586 } 587 buf, err = structs.Encode(structs.AllocUpdateRequestType, req2) 588 if err != nil { 589 t.Fatalf("err: %v", err) 590 } 591 592 resp = fsm.Apply(makeLog(buf)) 593 if resp != nil { 594 t.Fatalf("resp: %v", resp) 595 } 596 597 // Verify we are evicted 598 out, err = fsm.State().AllocByID(alloc.ID) 599 if err != nil { 600 t.Fatalf("err: %v", err) 601 } 602 if out.DesiredStatus != structs.AllocDesiredStatusEvict { 603 t.Fatalf("alloc found!") 604 } 605 if out.Job == nil { 606 t.Fatalf("missing job") 607 } 608 } 609 610 func TestFSM_UpsertAllocs_StrippedResources(t *testing.T) { 611 fsm := testFSM(t) 612 613 alloc := mock.Alloc() 614 job := alloc.Job 615 resources := alloc.Resources 616 alloc.Resources = nil 617 req := structs.AllocUpdateRequest{ 618 Job: job, 619 Alloc: []*structs.Allocation{alloc}, 620 } 621 buf, err := structs.Encode(structs.AllocUpdateRequestType, req) 622 if err != nil { 623 t.Fatalf("err: %v", err) 624 } 625 626 resp := fsm.Apply(makeLog(buf)) 627 if resp != nil { 628 t.Fatalf("resp: %v", resp) 629 } 630 631 // Verify we are registered 632 out, err := fsm.State().AllocByID(alloc.ID) 633 if err != nil { 634 t.Fatalf("err: %v", err) 635 } 636 alloc.CreateIndex = out.CreateIndex 637 alloc.ModifyIndex = out.ModifyIndex 638 alloc.AllocModifyIndex = out.AllocModifyIndex 639 640 // Resources should be recomputed 641 alloc.Resources = resources 642 if !reflect.DeepEqual(alloc, out) { 643 t.Fatalf("bad: %#v %#v", alloc, out) 644 } 645 } 646 647 func TestFSM_UpdateAllocFromClient_Unblock(t *testing.T) { 648 fsm := testFSM(t) 649 fsm.blockedEvals.SetEnabled(true) 650 state := fsm.State() 651 652 node := mock.Node() 653 state.UpsertNode(1, node) 654 655 // Mark an eval as blocked. 656 eval := mock.Eval() 657 eval.ClassEligibility = map[string]bool{node.ComputedClass: true} 658 fsm.blockedEvals.Block(eval) 659 660 bStats := fsm.blockedEvals.Stats() 661 if bStats.TotalBlocked != 1 { 662 t.Fatalf("bad: %#v", bStats) 663 } 664 665 // Create a completed eval 666 alloc := mock.Alloc() 667 alloc.NodeID = node.ID 668 alloc2 := mock.Alloc() 669 alloc2.NodeID = node.ID 670 state.UpsertAllocs(1, []*structs.Allocation{alloc, alloc2}) 671 672 clientAlloc := new(structs.Allocation) 673 *clientAlloc = *alloc 674 clientAlloc.ClientStatus = structs.AllocClientStatusComplete 675 update2 := &structs.Allocation{ 676 ID: alloc2.ID, 677 ClientStatus: structs.AllocClientStatusRunning, 678 } 679 680 req := structs.AllocUpdateRequest{ 681 Alloc: []*structs.Allocation{clientAlloc, update2}, 682 } 683 buf, err := structs.Encode(structs.AllocClientUpdateRequestType, req) 684 if err != nil { 685 t.Fatalf("err: %v", err) 686 } 687 688 resp := fsm.Apply(makeLog(buf)) 689 if resp != nil { 690 t.Fatalf("resp: %v", resp) 691 } 692 693 // Verify we are updated 694 out, err := fsm.State().AllocByID(alloc.ID) 695 if err != nil { 696 t.Fatalf("err: %v", err) 697 } 698 clientAlloc.CreateIndex = out.CreateIndex 699 clientAlloc.ModifyIndex = out.ModifyIndex 700 if !reflect.DeepEqual(clientAlloc, out) { 701 t.Fatalf("bad: %#v %#v", clientAlloc, out) 702 } 703 704 out, err = fsm.State().AllocByID(alloc2.ID) 705 if err != nil { 706 t.Fatalf("err: %v", err) 707 } 708 alloc2.CreateIndex = out.CreateIndex 709 alloc2.ModifyIndex = out.ModifyIndex 710 alloc2.ClientStatus = structs.AllocClientStatusRunning 711 alloc2.TaskStates = nil 712 if !reflect.DeepEqual(alloc2, out) { 713 t.Fatalf("bad: %#v %#v", alloc2, out) 714 } 715 716 // Verify the eval was unblocked. 717 testutil.WaitForResult(func() (bool, error) { 718 bStats = fsm.blockedEvals.Stats() 719 if bStats.TotalBlocked != 0 { 720 return false, fmt.Errorf("bad: %#v %#v", bStats, out) 721 } 722 return true, nil 723 }, func(err error) { 724 t.Fatalf("err: %s", err) 725 }) 726 } 727 728 func TestFSM_UpdateAllocFromClient(t *testing.T) { 729 fsm := testFSM(t) 730 state := fsm.State() 731 732 alloc := mock.Alloc() 733 state.UpsertAllocs(1, []*structs.Allocation{alloc}) 734 735 clientAlloc := new(structs.Allocation) 736 *clientAlloc = *alloc 737 clientAlloc.ClientStatus = structs.AllocClientStatusFailed 738 739 req := structs.AllocUpdateRequest{ 740 Alloc: []*structs.Allocation{clientAlloc}, 741 } 742 buf, err := structs.Encode(structs.AllocClientUpdateRequestType, req) 743 if err != nil { 744 t.Fatalf("err: %v", err) 745 } 746 747 resp := fsm.Apply(makeLog(buf)) 748 if resp != nil { 749 t.Fatalf("resp: %v", resp) 750 } 751 752 // Verify we are registered 753 out, err := fsm.State().AllocByID(alloc.ID) 754 if err != nil { 755 t.Fatalf("err: %v", err) 756 } 757 clientAlloc.CreateIndex = out.CreateIndex 758 clientAlloc.ModifyIndex = out.ModifyIndex 759 if !reflect.DeepEqual(clientAlloc, out) { 760 t.Fatalf("bad: %#v %#v", clientAlloc, out) 761 } 762 } 763 764 func testSnapshotRestore(t *testing.T, fsm *nomadFSM) *nomadFSM { 765 // Snapshot 766 snap, err := fsm.Snapshot() 767 if err != nil { 768 t.Fatalf("err: %v", err) 769 } 770 defer snap.Release() 771 772 // Persist 773 buf := bytes.NewBuffer(nil) 774 sink := &MockSink{buf, false} 775 if err := snap.Persist(sink); err != nil { 776 t.Fatalf("err: %v", err) 777 } 778 779 // Try to restore on a new FSM 780 fsm2 := testFSM(t) 781 782 // Do a restore 783 if err := fsm2.Restore(sink); err != nil { 784 t.Fatalf("err: %v", err) 785 } 786 return fsm2 787 } 788 789 func TestFSM_SnapshotRestore_Nodes(t *testing.T) { 790 // Add some state 791 fsm := testFSM(t) 792 state := fsm.State() 793 node1 := mock.Node() 794 state.UpsertNode(1000, node1) 795 node2 := mock.Node() 796 state.UpsertNode(1001, node2) 797 798 // Verify the contents 799 fsm2 := testSnapshotRestore(t, fsm) 800 state2 := fsm2.State() 801 out1, _ := state2.NodeByID(node1.ID) 802 out2, _ := state2.NodeByID(node2.ID) 803 if !reflect.DeepEqual(node1, out1) { 804 t.Fatalf("bad: \n%#v\n%#v", out1, node1) 805 } 806 if !reflect.DeepEqual(node2, out2) { 807 t.Fatalf("bad: \n%#v\n%#v", out2, node2) 808 } 809 } 810 811 func TestFSM_SnapshotRestore_Jobs(t *testing.T) { 812 // Add some state 813 fsm := testFSM(t) 814 state := fsm.State() 815 job1 := mock.Job() 816 state.UpsertJob(1000, job1) 817 job2 := mock.Job() 818 state.UpsertJob(1001, job2) 819 820 // Verify the contents 821 fsm2 := testSnapshotRestore(t, fsm) 822 state2 := fsm2.State() 823 out1, _ := state2.JobByID(job1.ID) 824 out2, _ := state2.JobByID(job2.ID) 825 if !reflect.DeepEqual(job1, out1) { 826 t.Fatalf("bad: \n%#v\n%#v", out1, job1) 827 } 828 if !reflect.DeepEqual(job2, out2) { 829 t.Fatalf("bad: \n%#v\n%#v", out2, job2) 830 } 831 } 832 833 func TestFSM_SnapshotRestore_Evals(t *testing.T) { 834 // Add some state 835 fsm := testFSM(t) 836 state := fsm.State() 837 eval1 := mock.Eval() 838 state.UpsertEvals(1000, []*structs.Evaluation{eval1}) 839 eval2 := mock.Eval() 840 state.UpsertEvals(1001, []*structs.Evaluation{eval2}) 841 842 // Verify the contents 843 fsm2 := testSnapshotRestore(t, fsm) 844 state2 := fsm2.State() 845 out1, _ := state2.EvalByID(eval1.ID) 846 out2, _ := state2.EvalByID(eval2.ID) 847 if !reflect.DeepEqual(eval1, out1) { 848 t.Fatalf("bad: \n%#v\n%#v", out1, eval1) 849 } 850 if !reflect.DeepEqual(eval2, out2) { 851 t.Fatalf("bad: \n%#v\n%#v", out2, eval2) 852 } 853 } 854 855 func TestFSM_SnapshotRestore_Allocs(t *testing.T) { 856 // Add some state 857 fsm := testFSM(t) 858 state := fsm.State() 859 alloc1 := mock.Alloc() 860 state.UpsertAllocs(1000, []*structs.Allocation{alloc1}) 861 alloc2 := mock.Alloc() 862 state.UpsertAllocs(1001, []*structs.Allocation{alloc2}) 863 864 // Verify the contents 865 fsm2 := testSnapshotRestore(t, fsm) 866 state2 := fsm2.State() 867 out1, _ := state2.AllocByID(alloc1.ID) 868 out2, _ := state2.AllocByID(alloc2.ID) 869 if !reflect.DeepEqual(alloc1, out1) { 870 t.Fatalf("bad: \n%#v\n%#v", out1, alloc1) 871 } 872 if !reflect.DeepEqual(alloc2, out2) { 873 t.Fatalf("bad: \n%#v\n%#v", out2, alloc2) 874 } 875 } 876 877 func TestFSM_SnapshotRestore_Indexes(t *testing.T) { 878 // Add some state 879 fsm := testFSM(t) 880 state := fsm.State() 881 node1 := mock.Node() 882 state.UpsertNode(1000, node1) 883 884 // Verify the contents 885 fsm2 := testSnapshotRestore(t, fsm) 886 state2 := fsm2.State() 887 888 index, err := state2.Index("nodes") 889 if err != nil { 890 t.Fatalf("err: %v", err) 891 } 892 if index != 1000 { 893 t.Fatalf("bad: %d", index) 894 } 895 } 896 897 func TestFSM_SnapshotRestore_TimeTable(t *testing.T) { 898 // Add some state 899 fsm := testFSM(t) 900 901 tt := fsm.TimeTable() 902 start := time.Now().UTC() 903 tt.Witness(1000, start) 904 tt.Witness(2000, start.Add(10*time.Minute)) 905 906 // Verify the contents 907 fsm2 := testSnapshotRestore(t, fsm) 908 909 tt2 := fsm2.TimeTable() 910 if tt2.NearestTime(1500) != start { 911 t.Fatalf("bad") 912 } 913 if tt2.NearestIndex(start.Add(15*time.Minute)) != 2000 { 914 t.Fatalf("bad") 915 } 916 } 917 918 func TestFSM_SnapshotRestore_PeriodicLaunches(t *testing.T) { 919 // Add some state 920 fsm := testFSM(t) 921 state := fsm.State() 922 job1 := mock.Job() 923 launch1 := &structs.PeriodicLaunch{ID: job1.ID, Launch: time.Now()} 924 state.UpsertPeriodicLaunch(1000, launch1) 925 job2 := mock.Job() 926 launch2 := &structs.PeriodicLaunch{ID: job2.ID, Launch: time.Now()} 927 state.UpsertPeriodicLaunch(1001, launch2) 928 929 // Verify the contents 930 fsm2 := testSnapshotRestore(t, fsm) 931 state2 := fsm2.State() 932 out1, _ := state2.PeriodicLaunchByID(launch1.ID) 933 out2, _ := state2.PeriodicLaunchByID(launch2.ID) 934 if !reflect.DeepEqual(launch1, out1) { 935 t.Fatalf("bad: \n%#v\n%#v", out1, job1) 936 } 937 if !reflect.DeepEqual(launch2, out2) { 938 t.Fatalf("bad: \n%#v\n%#v", out2, job2) 939 } 940 }