github.com/jrxfive/nomad@v0.6.1-0.20170802162750-1fef470e89bf/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 memdb "github.com/hashicorp/go-memdb" 12 "github.com/hashicorp/nomad/helper" 13 "github.com/hashicorp/nomad/nomad/mock" 14 "github.com/hashicorp/nomad/nomad/state" 15 "github.com/hashicorp/nomad/nomad/structs" 16 "github.com/hashicorp/nomad/testutil" 17 "github.com/hashicorp/raft" 18 "github.com/kr/pretty" 19 ) 20 21 type MockSink struct { 22 *bytes.Buffer 23 cancel bool 24 } 25 26 func (m *MockSink) ID() string { 27 return "Mock" 28 } 29 30 func (m *MockSink) Cancel() error { 31 m.cancel = true 32 return nil 33 } 34 35 func (m *MockSink) Close() error { 36 return nil 37 } 38 39 func testStateStore(t *testing.T) *state.StateStore { 40 state, err := state.NewStateStore(os.Stderr) 41 if err != nil { 42 t.Fatalf("err: %v", err) 43 } 44 if state == nil { 45 t.Fatalf("missing state") 46 } 47 return state 48 } 49 50 func testFSM(t *testing.T) *nomadFSM { 51 p, _ := testPeriodicDispatcher() 52 broker := testBroker(t, 0) 53 blocked := NewBlockedEvals(broker) 54 fsm, err := NewFSM(broker, p, blocked, os.Stderr) 55 if err != nil { 56 t.Fatalf("err: %v", err) 57 } 58 if fsm == nil { 59 t.Fatalf("missing fsm") 60 } 61 return fsm 62 } 63 64 func makeLog(buf []byte) *raft.Log { 65 return &raft.Log{ 66 Index: 1, 67 Term: 1, 68 Type: raft.LogCommand, 69 Data: buf, 70 } 71 } 72 73 func TestFSM_UpsertNode(t *testing.T) { 74 t.Parallel() 75 fsm := testFSM(t) 76 fsm.blockedEvals.SetEnabled(true) 77 78 node := mock.Node() 79 80 // Mark an eval as blocked. 81 eval := mock.Eval() 82 eval.ClassEligibility = map[string]bool{node.ComputedClass: true} 83 fsm.blockedEvals.Block(eval) 84 85 req := structs.NodeRegisterRequest{ 86 Node: node, 87 } 88 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 89 if err != nil { 90 t.Fatalf("err: %v", err) 91 } 92 93 resp := fsm.Apply(makeLog(buf)) 94 if resp != nil { 95 t.Fatalf("resp: %v", resp) 96 } 97 98 // Verify we are registered 99 ws := memdb.NewWatchSet() 100 n, err := fsm.State().NodeByID(ws, req.Node.ID) 101 if err != nil { 102 t.Fatalf("err: %v", err) 103 } 104 if n == nil { 105 t.Fatalf("not found!") 106 } 107 if n.CreateIndex != 1 { 108 t.Fatalf("bad index: %d", node.CreateIndex) 109 } 110 111 tt := fsm.TimeTable() 112 index := tt.NearestIndex(time.Now().UTC()) 113 if index != 1 { 114 t.Fatalf("bad: %d", index) 115 } 116 117 // Verify the eval was unblocked. 118 testutil.WaitForResult(func() (bool, error) { 119 bStats := fsm.blockedEvals.Stats() 120 if bStats.TotalBlocked != 0 { 121 return false, fmt.Errorf("bad: %#v", bStats) 122 } 123 return true, nil 124 }, func(err error) { 125 t.Fatalf("err: %s", err) 126 }) 127 128 } 129 130 func TestFSM_DeregisterNode(t *testing.T) { 131 t.Parallel() 132 fsm := testFSM(t) 133 134 node := mock.Node() 135 req := structs.NodeRegisterRequest{ 136 Node: node, 137 } 138 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 139 if err != nil { 140 t.Fatalf("err: %v", err) 141 } 142 143 resp := fsm.Apply(makeLog(buf)) 144 if resp != nil { 145 t.Fatalf("resp: %v", resp) 146 } 147 148 req2 := structs.NodeDeregisterRequest{ 149 NodeID: node.ID, 150 } 151 buf, err = structs.Encode(structs.NodeDeregisterRequestType, req2) 152 if err != nil { 153 t.Fatalf("err: %v", err) 154 } 155 156 resp = fsm.Apply(makeLog(buf)) 157 if resp != nil { 158 t.Fatalf("resp: %v", resp) 159 } 160 161 // Verify we are NOT registered 162 ws := memdb.NewWatchSet() 163 node, err = fsm.State().NodeByID(ws, req.Node.ID) 164 if err != nil { 165 t.Fatalf("err: %v", err) 166 } 167 if node != nil { 168 t.Fatalf("node found!") 169 } 170 } 171 172 func TestFSM_UpdateNodeStatus(t *testing.T) { 173 t.Parallel() 174 fsm := testFSM(t) 175 fsm.blockedEvals.SetEnabled(true) 176 177 node := mock.Node() 178 req := structs.NodeRegisterRequest{ 179 Node: node, 180 } 181 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 182 if err != nil { 183 t.Fatalf("err: %v", err) 184 } 185 186 resp := fsm.Apply(makeLog(buf)) 187 if resp != nil { 188 t.Fatalf("resp: %v", resp) 189 } 190 191 // Mark an eval as blocked. 192 eval := mock.Eval() 193 eval.ClassEligibility = map[string]bool{node.ComputedClass: true} 194 fsm.blockedEvals.Block(eval) 195 196 req2 := structs.NodeUpdateStatusRequest{ 197 NodeID: node.ID, 198 Status: structs.NodeStatusReady, 199 } 200 buf, err = structs.Encode(structs.NodeUpdateStatusRequestType, req2) 201 if err != nil { 202 t.Fatalf("err: %v", err) 203 } 204 205 resp = fsm.Apply(makeLog(buf)) 206 if resp != nil { 207 t.Fatalf("resp: %v", resp) 208 } 209 210 // Verify the status is ready. 211 ws := memdb.NewWatchSet() 212 node, err = fsm.State().NodeByID(ws, req.Node.ID) 213 if err != nil { 214 t.Fatalf("err: %v", err) 215 } 216 if node.Status != structs.NodeStatusReady { 217 t.Fatalf("bad node: %#v", node) 218 } 219 220 // Verify the eval was unblocked. 221 testutil.WaitForResult(func() (bool, error) { 222 bStats := fsm.blockedEvals.Stats() 223 if bStats.TotalBlocked != 0 { 224 return false, fmt.Errorf("bad: %#v", bStats) 225 } 226 return true, nil 227 }, func(err error) { 228 t.Fatalf("err: %s", err) 229 }) 230 } 231 232 func TestFSM_UpdateNodeDrain(t *testing.T) { 233 t.Parallel() 234 fsm := testFSM(t) 235 236 node := mock.Node() 237 req := structs.NodeRegisterRequest{ 238 Node: node, 239 } 240 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 241 if err != nil { 242 t.Fatalf("err: %v", err) 243 } 244 245 resp := fsm.Apply(makeLog(buf)) 246 if resp != nil { 247 t.Fatalf("resp: %v", resp) 248 } 249 250 req2 := structs.NodeUpdateDrainRequest{ 251 NodeID: node.ID, 252 Drain: true, 253 } 254 buf, err = structs.Encode(structs.NodeUpdateDrainRequestType, req2) 255 if err != nil { 256 t.Fatalf("err: %v", err) 257 } 258 259 resp = fsm.Apply(makeLog(buf)) 260 if resp != nil { 261 t.Fatalf("resp: %v", resp) 262 } 263 264 // Verify we are NOT registered 265 ws := memdb.NewWatchSet() 266 node, err = fsm.State().NodeByID(ws, req.Node.ID) 267 if err != nil { 268 t.Fatalf("err: %v", err) 269 } 270 if !node.Drain { 271 t.Fatalf("bad node: %#v", node) 272 } 273 } 274 275 func TestFSM_RegisterJob(t *testing.T) { 276 t.Parallel() 277 fsm := testFSM(t) 278 279 job := mock.PeriodicJob() 280 req := structs.JobRegisterRequest{ 281 Job: job, 282 } 283 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 284 if err != nil { 285 t.Fatalf("err: %v", err) 286 } 287 288 resp := fsm.Apply(makeLog(buf)) 289 if resp != nil { 290 t.Fatalf("resp: %v", resp) 291 } 292 293 // Verify we are registered 294 ws := memdb.NewWatchSet() 295 jobOut, err := fsm.State().JobByID(ws, req.Job.ID) 296 if err != nil { 297 t.Fatalf("err: %v", err) 298 } 299 if jobOut == nil { 300 t.Fatalf("not found!") 301 } 302 if jobOut.CreateIndex != 1 { 303 t.Fatalf("bad index: %d", jobOut.CreateIndex) 304 } 305 306 // Verify it was added to the periodic runner. 307 if _, ok := fsm.periodicDispatcher.tracked[job.ID]; !ok { 308 t.Fatal("job not added to periodic runner") 309 } 310 311 // Verify the launch time was tracked. 312 launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Job.ID) 313 if err != nil { 314 t.Fatalf("err: %v", err) 315 } 316 if launchOut == nil { 317 t.Fatalf("not found!") 318 } 319 if launchOut.Launch.IsZero() { 320 t.Fatalf("bad launch time: %v", launchOut.Launch) 321 } 322 } 323 324 func TestFSM_DeregisterJob_Purge(t *testing.T) { 325 t.Parallel() 326 fsm := testFSM(t) 327 328 job := mock.PeriodicJob() 329 req := structs.JobRegisterRequest{ 330 Job: job, 331 } 332 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 333 if err != nil { 334 t.Fatalf("err: %v", err) 335 } 336 337 resp := fsm.Apply(makeLog(buf)) 338 if resp != nil { 339 t.Fatalf("resp: %v", resp) 340 } 341 342 req2 := structs.JobDeregisterRequest{ 343 JobID: job.ID, 344 Purge: true, 345 } 346 buf, err = structs.Encode(structs.JobDeregisterRequestType, req2) 347 if err != nil { 348 t.Fatalf("err: %v", err) 349 } 350 351 resp = fsm.Apply(makeLog(buf)) 352 if resp != nil { 353 t.Fatalf("resp: %v", resp) 354 } 355 356 // Verify we are NOT registered 357 ws := memdb.NewWatchSet() 358 jobOut, err := fsm.State().JobByID(ws, req.Job.ID) 359 if err != nil { 360 t.Fatalf("err: %v", err) 361 } 362 if jobOut != nil { 363 t.Fatalf("job found!") 364 } 365 366 // Verify it was removed from the periodic runner. 367 if _, ok := fsm.periodicDispatcher.tracked[job.ID]; ok { 368 t.Fatal("job not removed from periodic runner") 369 } 370 371 // Verify it was removed from the periodic launch table. 372 launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Job.ID) 373 if err != nil { 374 t.Fatalf("err: %v", err) 375 } 376 if launchOut != nil { 377 t.Fatalf("launch found!") 378 } 379 } 380 381 func TestFSM_DeregisterJob_NoPurge(t *testing.T) { 382 t.Parallel() 383 fsm := testFSM(t) 384 385 job := mock.PeriodicJob() 386 req := structs.JobRegisterRequest{ 387 Job: job, 388 } 389 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 390 if err != nil { 391 t.Fatalf("err: %v", err) 392 } 393 394 resp := fsm.Apply(makeLog(buf)) 395 if resp != nil { 396 t.Fatalf("resp: %v", resp) 397 } 398 399 req2 := structs.JobDeregisterRequest{ 400 JobID: job.ID, 401 Purge: false, 402 } 403 buf, err = structs.Encode(structs.JobDeregisterRequestType, req2) 404 if err != nil { 405 t.Fatalf("err: %v", err) 406 } 407 408 resp = fsm.Apply(makeLog(buf)) 409 if resp != nil { 410 t.Fatalf("resp: %v", resp) 411 } 412 413 // Verify we are NOT registered 414 ws := memdb.NewWatchSet() 415 jobOut, err := fsm.State().JobByID(ws, req.Job.ID) 416 if err != nil { 417 t.Fatalf("err: %v", err) 418 } 419 if jobOut == nil { 420 t.Fatalf("job not found!") 421 } 422 if !jobOut.Stop { 423 t.Fatalf("job not stopped found!") 424 } 425 426 // Verify it was removed from the periodic runner. 427 if _, ok := fsm.periodicDispatcher.tracked[job.ID]; ok { 428 t.Fatal("job not removed from periodic runner") 429 } 430 431 // Verify it was removed from the periodic launch table. 432 launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Job.ID) 433 if err != nil { 434 t.Fatalf("err: %v", err) 435 } 436 if launchOut == nil { 437 t.Fatalf("launch not found!") 438 } 439 } 440 441 func TestFSM_UpdateEval(t *testing.T) { 442 t.Parallel() 443 fsm := testFSM(t) 444 fsm.evalBroker.SetEnabled(true) 445 446 req := structs.EvalUpdateRequest{ 447 Evals: []*structs.Evaluation{mock.Eval()}, 448 } 449 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 450 if err != nil { 451 t.Fatalf("err: %v", err) 452 } 453 454 resp := fsm.Apply(makeLog(buf)) 455 if resp != nil { 456 t.Fatalf("resp: %v", resp) 457 } 458 459 // Verify we are registered 460 ws := memdb.NewWatchSet() 461 eval, err := fsm.State().EvalByID(ws, req.Evals[0].ID) 462 if err != nil { 463 t.Fatalf("err: %v", err) 464 } 465 if eval == nil { 466 t.Fatalf("not found!") 467 } 468 if eval.CreateIndex != 1 { 469 t.Fatalf("bad index: %d", eval.CreateIndex) 470 } 471 472 // Verify enqueued 473 stats := fsm.evalBroker.Stats() 474 if stats.TotalReady != 1 { 475 t.Fatalf("bad: %#v %#v", stats, eval) 476 } 477 } 478 479 func TestFSM_UpdateEval_Blocked(t *testing.T) { 480 t.Parallel() 481 fsm := testFSM(t) 482 fsm.evalBroker.SetEnabled(true) 483 fsm.blockedEvals.SetEnabled(true) 484 485 // Create a blocked eval. 486 eval := mock.Eval() 487 eval.Status = structs.EvalStatusBlocked 488 489 req := structs.EvalUpdateRequest{ 490 Evals: []*structs.Evaluation{eval}, 491 } 492 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 493 if err != nil { 494 t.Fatalf("err: %v", err) 495 } 496 497 resp := fsm.Apply(makeLog(buf)) 498 if resp != nil { 499 t.Fatalf("resp: %v", resp) 500 } 501 502 // Verify we are registered 503 ws := memdb.NewWatchSet() 504 out, err := fsm.State().EvalByID(ws, eval.ID) 505 if err != nil { 506 t.Fatalf("err: %v", err) 507 } 508 if out == nil { 509 t.Fatalf("not found!") 510 } 511 if out.CreateIndex != 1 { 512 t.Fatalf("bad index: %d", out.CreateIndex) 513 } 514 515 // Verify the eval wasn't enqueued 516 stats := fsm.evalBroker.Stats() 517 if stats.TotalReady != 0 { 518 t.Fatalf("bad: %#v %#v", stats, out) 519 } 520 521 // Verify the eval was added to the blocked tracker. 522 bStats := fsm.blockedEvals.Stats() 523 if bStats.TotalBlocked != 1 { 524 t.Fatalf("bad: %#v %#v", bStats, out) 525 } 526 } 527 528 func TestFSM_UpdateEval_Untrack(t *testing.T) { 529 t.Parallel() 530 fsm := testFSM(t) 531 fsm.evalBroker.SetEnabled(true) 532 fsm.blockedEvals.SetEnabled(true) 533 534 // Mark an eval as blocked. 535 bEval := mock.Eval() 536 bEval.ClassEligibility = map[string]bool{"v1:123": true} 537 fsm.blockedEvals.Block(bEval) 538 539 // Create a successful eval for the same job 540 eval := mock.Eval() 541 eval.JobID = bEval.JobID 542 eval.Status = structs.EvalStatusComplete 543 544 req := structs.EvalUpdateRequest{ 545 Evals: []*structs.Evaluation{eval}, 546 } 547 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 548 if err != nil { 549 t.Fatalf("err: %v", err) 550 } 551 552 resp := fsm.Apply(makeLog(buf)) 553 if resp != nil { 554 t.Fatalf("resp: %v", resp) 555 } 556 557 // Verify we are registered 558 ws := memdb.NewWatchSet() 559 out, err := fsm.State().EvalByID(ws, eval.ID) 560 if err != nil { 561 t.Fatalf("err: %v", err) 562 } 563 if out == nil { 564 t.Fatalf("not found!") 565 } 566 if out.CreateIndex != 1 { 567 t.Fatalf("bad index: %d", out.CreateIndex) 568 } 569 570 // Verify the eval wasn't enqueued 571 stats := fsm.evalBroker.Stats() 572 if stats.TotalReady != 0 { 573 t.Fatalf("bad: %#v %#v", stats, out) 574 } 575 576 // Verify the eval was untracked in the blocked tracker. 577 bStats := fsm.blockedEvals.Stats() 578 if bStats.TotalBlocked != 0 { 579 t.Fatalf("bad: %#v %#v", bStats, out) 580 } 581 } 582 583 func TestFSM_UpdateEval_NoUntrack(t *testing.T) { 584 t.Parallel() 585 fsm := testFSM(t) 586 fsm.evalBroker.SetEnabled(true) 587 fsm.blockedEvals.SetEnabled(true) 588 589 // Mark an eval as blocked. 590 bEval := mock.Eval() 591 bEval.ClassEligibility = map[string]bool{"v1:123": true} 592 fsm.blockedEvals.Block(bEval) 593 594 // Create a successful eval for the same job but with placement failures 595 eval := mock.Eval() 596 eval.JobID = bEval.JobID 597 eval.Status = structs.EvalStatusComplete 598 eval.FailedTGAllocs = make(map[string]*structs.AllocMetric) 599 eval.FailedTGAllocs["test"] = new(structs.AllocMetric) 600 601 req := structs.EvalUpdateRequest{ 602 Evals: []*structs.Evaluation{eval}, 603 } 604 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 605 if err != nil { 606 t.Fatalf("err: %v", err) 607 } 608 609 resp := fsm.Apply(makeLog(buf)) 610 if resp != nil { 611 t.Fatalf("resp: %v", resp) 612 } 613 614 // Verify we are registered 615 ws := memdb.NewWatchSet() 616 out, err := fsm.State().EvalByID(ws, eval.ID) 617 if err != nil { 618 t.Fatalf("err: %v", err) 619 } 620 if out == nil { 621 t.Fatalf("not found!") 622 } 623 if out.CreateIndex != 1 { 624 t.Fatalf("bad index: %d", out.CreateIndex) 625 } 626 627 // Verify the eval wasn't enqueued 628 stats := fsm.evalBroker.Stats() 629 if stats.TotalReady != 0 { 630 t.Fatalf("bad: %#v %#v", stats, out) 631 } 632 633 // Verify the eval was not untracked in the blocked tracker. 634 bStats := fsm.blockedEvals.Stats() 635 if bStats.TotalBlocked != 1 { 636 t.Fatalf("bad: %#v %#v", bStats, out) 637 } 638 } 639 640 func TestFSM_DeleteEval(t *testing.T) { 641 t.Parallel() 642 fsm := testFSM(t) 643 644 eval := mock.Eval() 645 req := structs.EvalUpdateRequest{ 646 Evals: []*structs.Evaluation{eval}, 647 } 648 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 649 if err != nil { 650 t.Fatalf("err: %v", err) 651 } 652 653 resp := fsm.Apply(makeLog(buf)) 654 if resp != nil { 655 t.Fatalf("resp: %v", resp) 656 } 657 658 req2 := structs.EvalDeleteRequest{ 659 Evals: []string{eval.ID}, 660 } 661 buf, err = structs.Encode(structs.EvalDeleteRequestType, req2) 662 if err != nil { 663 t.Fatalf("err: %v", err) 664 } 665 666 resp = fsm.Apply(makeLog(buf)) 667 if resp != nil { 668 t.Fatalf("resp: %v", resp) 669 } 670 671 // Verify we are NOT registered 672 ws := memdb.NewWatchSet() 673 eval, err = fsm.State().EvalByID(ws, req.Evals[0].ID) 674 if err != nil { 675 t.Fatalf("err: %v", err) 676 } 677 if eval != nil { 678 t.Fatalf("eval found!") 679 } 680 } 681 682 func TestFSM_UpsertAllocs(t *testing.T) { 683 t.Parallel() 684 fsm := testFSM(t) 685 686 alloc := mock.Alloc() 687 fsm.State().UpsertJobSummary(1, mock.JobSummary(alloc.JobID)) 688 req := structs.AllocUpdateRequest{ 689 Alloc: []*structs.Allocation{alloc}, 690 } 691 buf, err := structs.Encode(structs.AllocUpdateRequestType, req) 692 if err != nil { 693 t.Fatalf("err: %v", err) 694 } 695 696 resp := fsm.Apply(makeLog(buf)) 697 if resp != nil { 698 t.Fatalf("resp: %v", resp) 699 } 700 701 // Verify we are registered 702 ws := memdb.NewWatchSet() 703 out, err := fsm.State().AllocByID(ws, alloc.ID) 704 if err != nil { 705 t.Fatalf("err: %v", err) 706 } 707 alloc.CreateIndex = out.CreateIndex 708 alloc.ModifyIndex = out.ModifyIndex 709 alloc.AllocModifyIndex = out.AllocModifyIndex 710 if !reflect.DeepEqual(alloc, out) { 711 t.Fatalf("bad: %#v %#v", alloc, out) 712 } 713 714 evictAlloc := new(structs.Allocation) 715 *evictAlloc = *alloc 716 evictAlloc.DesiredStatus = structs.AllocDesiredStatusEvict 717 req2 := structs.AllocUpdateRequest{ 718 Alloc: []*structs.Allocation{evictAlloc}, 719 } 720 buf, err = structs.Encode(structs.AllocUpdateRequestType, req2) 721 if err != nil { 722 t.Fatalf("err: %v", err) 723 } 724 725 resp = fsm.Apply(makeLog(buf)) 726 if resp != nil { 727 t.Fatalf("resp: %v", resp) 728 } 729 730 // Verify we are evicted 731 out, err = fsm.State().AllocByID(ws, alloc.ID) 732 if err != nil { 733 t.Fatalf("err: %v", err) 734 } 735 if out.DesiredStatus != structs.AllocDesiredStatusEvict { 736 t.Fatalf("alloc found!") 737 } 738 } 739 740 func TestFSM_UpsertAllocs_SharedJob(t *testing.T) { 741 t.Parallel() 742 fsm := testFSM(t) 743 744 alloc := mock.Alloc() 745 fsm.State().UpsertJobSummary(1, mock.JobSummary(alloc.JobID)) 746 job := alloc.Job 747 alloc.Job = nil 748 req := structs.AllocUpdateRequest{ 749 Job: job, 750 Alloc: []*structs.Allocation{alloc}, 751 } 752 buf, err := structs.Encode(structs.AllocUpdateRequestType, req) 753 if err != nil { 754 t.Fatalf("err: %v", err) 755 } 756 757 resp := fsm.Apply(makeLog(buf)) 758 if resp != nil { 759 t.Fatalf("resp: %v", resp) 760 } 761 762 // Verify we are registered 763 ws := memdb.NewWatchSet() 764 out, err := fsm.State().AllocByID(ws, alloc.ID) 765 if err != nil { 766 t.Fatalf("err: %v", err) 767 } 768 alloc.CreateIndex = out.CreateIndex 769 alloc.ModifyIndex = out.ModifyIndex 770 alloc.AllocModifyIndex = out.AllocModifyIndex 771 772 // Job should be re-attached 773 alloc.Job = job 774 if !reflect.DeepEqual(alloc, out) { 775 t.Fatalf("bad: %#v %#v", alloc, out) 776 } 777 778 // Ensure that the original job is used 779 evictAlloc := new(structs.Allocation) 780 *evictAlloc = *alloc 781 job = mock.Job() 782 job.Priority = 123 783 784 evictAlloc.Job = nil 785 evictAlloc.DesiredStatus = structs.AllocDesiredStatusEvict 786 req2 := structs.AllocUpdateRequest{ 787 Job: job, 788 Alloc: []*structs.Allocation{evictAlloc}, 789 } 790 buf, err = structs.Encode(structs.AllocUpdateRequestType, req2) 791 if err != nil { 792 t.Fatalf("err: %v", err) 793 } 794 795 resp = fsm.Apply(makeLog(buf)) 796 if resp != nil { 797 t.Fatalf("resp: %v", resp) 798 } 799 800 // Verify we are evicted 801 out, err = fsm.State().AllocByID(ws, alloc.ID) 802 if err != nil { 803 t.Fatalf("err: %v", err) 804 } 805 if out.DesiredStatus != structs.AllocDesiredStatusEvict { 806 t.Fatalf("alloc found!") 807 } 808 if out.Job == nil || out.Job.Priority == 123 { 809 t.Fatalf("bad job") 810 } 811 } 812 813 func TestFSM_UpsertAllocs_StrippedResources(t *testing.T) { 814 t.Parallel() 815 fsm := testFSM(t) 816 817 alloc := mock.Alloc() 818 fsm.State().UpsertJobSummary(1, mock.JobSummary(alloc.JobID)) 819 job := alloc.Job 820 resources := alloc.Resources 821 alloc.Resources = nil 822 req := structs.AllocUpdateRequest{ 823 Job: job, 824 Alloc: []*structs.Allocation{alloc}, 825 } 826 buf, err := structs.Encode(structs.AllocUpdateRequestType, req) 827 if err != nil { 828 t.Fatalf("err: %v", err) 829 } 830 831 resp := fsm.Apply(makeLog(buf)) 832 if resp != nil { 833 t.Fatalf("resp: %v", resp) 834 } 835 836 // Verify we are registered 837 ws := memdb.NewWatchSet() 838 out, err := fsm.State().AllocByID(ws, alloc.ID) 839 if err != nil { 840 t.Fatalf("err: %v", err) 841 } 842 alloc.CreateIndex = out.CreateIndex 843 alloc.ModifyIndex = out.ModifyIndex 844 alloc.AllocModifyIndex = out.AllocModifyIndex 845 846 // Resources should be recomputed 847 resources.DiskMB = alloc.Job.TaskGroups[0].EphemeralDisk.SizeMB 848 alloc.Resources = resources 849 if !reflect.DeepEqual(alloc, out) { 850 t.Fatalf("bad: %#v %#v", alloc, out) 851 } 852 } 853 854 func TestFSM_UpdateAllocFromClient_Unblock(t *testing.T) { 855 t.Parallel() 856 fsm := testFSM(t) 857 fsm.blockedEvals.SetEnabled(true) 858 state := fsm.State() 859 860 node := mock.Node() 861 state.UpsertNode(1, node) 862 863 // Mark an eval as blocked. 864 eval := mock.Eval() 865 eval.ClassEligibility = map[string]bool{node.ComputedClass: true} 866 fsm.blockedEvals.Block(eval) 867 868 bStats := fsm.blockedEvals.Stats() 869 if bStats.TotalBlocked != 1 { 870 t.Fatalf("bad: %#v", bStats) 871 } 872 873 // Create a completed eval 874 alloc := mock.Alloc() 875 alloc.NodeID = node.ID 876 alloc2 := mock.Alloc() 877 alloc2.NodeID = node.ID 878 state.UpsertJobSummary(8, mock.JobSummary(alloc.JobID)) 879 state.UpsertJobSummary(9, mock.JobSummary(alloc2.JobID)) 880 state.UpsertAllocs(10, []*structs.Allocation{alloc, alloc2}) 881 882 clientAlloc := new(structs.Allocation) 883 *clientAlloc = *alloc 884 clientAlloc.ClientStatus = structs.AllocClientStatusComplete 885 update2 := &structs.Allocation{ 886 ID: alloc2.ID, 887 ClientStatus: structs.AllocClientStatusRunning, 888 } 889 890 req := structs.AllocUpdateRequest{ 891 Alloc: []*structs.Allocation{clientAlloc, update2}, 892 } 893 buf, err := structs.Encode(structs.AllocClientUpdateRequestType, req) 894 if err != nil { 895 t.Fatalf("err: %v", err) 896 } 897 898 resp := fsm.Apply(makeLog(buf)) 899 if resp != nil { 900 t.Fatalf("resp: %v", resp) 901 } 902 903 // Verify we are updated 904 ws := memdb.NewWatchSet() 905 out, err := fsm.State().AllocByID(ws, alloc.ID) 906 if err != nil { 907 t.Fatalf("err: %v", err) 908 } 909 clientAlloc.CreateIndex = out.CreateIndex 910 clientAlloc.ModifyIndex = out.ModifyIndex 911 if !reflect.DeepEqual(clientAlloc, out) { 912 t.Fatalf("bad: %#v %#v", clientAlloc, out) 913 } 914 915 out, err = fsm.State().AllocByID(ws, alloc2.ID) 916 if err != nil { 917 t.Fatalf("err: %v", err) 918 } 919 alloc2.CreateIndex = out.CreateIndex 920 alloc2.ModifyIndex = out.ModifyIndex 921 alloc2.ClientStatus = structs.AllocClientStatusRunning 922 alloc2.TaskStates = nil 923 if !reflect.DeepEqual(alloc2, out) { 924 t.Fatalf("bad: %#v %#v", alloc2, out) 925 } 926 927 // Verify the eval was unblocked. 928 testutil.WaitForResult(func() (bool, error) { 929 bStats = fsm.blockedEvals.Stats() 930 if bStats.TotalBlocked != 0 { 931 return false, fmt.Errorf("bad: %#v %#v", bStats, out) 932 } 933 return true, nil 934 }, func(err error) { 935 t.Fatalf("err: %s", err) 936 }) 937 } 938 939 func TestFSM_UpdateAllocFromClient(t *testing.T) { 940 t.Parallel() 941 fsm := testFSM(t) 942 state := fsm.State() 943 944 alloc := mock.Alloc() 945 state.UpsertJobSummary(9, mock.JobSummary(alloc.JobID)) 946 state.UpsertAllocs(10, []*structs.Allocation{alloc}) 947 948 clientAlloc := new(structs.Allocation) 949 *clientAlloc = *alloc 950 clientAlloc.ClientStatus = structs.AllocClientStatusFailed 951 952 req := structs.AllocUpdateRequest{ 953 Alloc: []*structs.Allocation{clientAlloc}, 954 } 955 buf, err := structs.Encode(structs.AllocClientUpdateRequestType, req) 956 if err != nil { 957 t.Fatalf("err: %v", err) 958 } 959 960 resp := fsm.Apply(makeLog(buf)) 961 if resp != nil { 962 t.Fatalf("resp: %v", resp) 963 } 964 965 // Verify we are registered 966 ws := memdb.NewWatchSet() 967 out, err := fsm.State().AllocByID(ws, alloc.ID) 968 if err != nil { 969 t.Fatalf("err: %v", err) 970 } 971 clientAlloc.CreateIndex = out.CreateIndex 972 clientAlloc.ModifyIndex = out.ModifyIndex 973 if !reflect.DeepEqual(clientAlloc, out) { 974 t.Fatalf("err: %#v,%#v", clientAlloc, out) 975 } 976 } 977 978 func TestFSM_UpsertVaultAccessor(t *testing.T) { 979 t.Parallel() 980 fsm := testFSM(t) 981 fsm.blockedEvals.SetEnabled(true) 982 983 va := mock.VaultAccessor() 984 va2 := mock.VaultAccessor() 985 req := structs.VaultAccessorsRequest{ 986 Accessors: []*structs.VaultAccessor{va, va2}, 987 } 988 buf, err := structs.Encode(structs.VaultAccessorRegisterRequestType, req) 989 if err != nil { 990 t.Fatalf("err: %v", err) 991 } 992 993 resp := fsm.Apply(makeLog(buf)) 994 if resp != nil { 995 t.Fatalf("resp: %v", resp) 996 } 997 998 // Verify we are registered 999 ws := memdb.NewWatchSet() 1000 out1, err := fsm.State().VaultAccessor(ws, va.Accessor) 1001 if err != nil { 1002 t.Fatalf("err: %v", err) 1003 } 1004 if out1 == nil { 1005 t.Fatalf("not found!") 1006 } 1007 if out1.CreateIndex != 1 { 1008 t.Fatalf("bad index: %d", out1.CreateIndex) 1009 } 1010 out2, err := fsm.State().VaultAccessor(ws, va2.Accessor) 1011 if err != nil { 1012 t.Fatalf("err: %v", err) 1013 } 1014 if out2 == nil { 1015 t.Fatalf("not found!") 1016 } 1017 if out1.CreateIndex != 1 { 1018 t.Fatalf("bad index: %d", out2.CreateIndex) 1019 } 1020 1021 tt := fsm.TimeTable() 1022 index := tt.NearestIndex(time.Now().UTC()) 1023 if index != 1 { 1024 t.Fatalf("bad: %d", index) 1025 } 1026 } 1027 1028 func TestFSM_DeregisterVaultAccessor(t *testing.T) { 1029 t.Parallel() 1030 fsm := testFSM(t) 1031 fsm.blockedEvals.SetEnabled(true) 1032 1033 va := mock.VaultAccessor() 1034 va2 := mock.VaultAccessor() 1035 accessors := []*structs.VaultAccessor{va, va2} 1036 1037 // Insert the accessors 1038 if err := fsm.State().UpsertVaultAccessor(1000, accessors); err != nil { 1039 t.Fatalf("bad: %v", err) 1040 } 1041 1042 req := structs.VaultAccessorsRequest{ 1043 Accessors: accessors, 1044 } 1045 buf, err := structs.Encode(structs.VaultAccessorDegisterRequestType, req) 1046 if err != nil { 1047 t.Fatalf("err: %v", err) 1048 } 1049 1050 resp := fsm.Apply(makeLog(buf)) 1051 if resp != nil { 1052 t.Fatalf("resp: %v", resp) 1053 } 1054 1055 ws := memdb.NewWatchSet() 1056 out1, err := fsm.State().VaultAccessor(ws, va.Accessor) 1057 if err != nil { 1058 t.Fatalf("err: %v", err) 1059 } 1060 if out1 != nil { 1061 t.Fatalf("not deleted!") 1062 } 1063 1064 tt := fsm.TimeTable() 1065 index := tt.NearestIndex(time.Now().UTC()) 1066 if index != 1 { 1067 t.Fatalf("bad: %d", index) 1068 } 1069 } 1070 1071 func TestFSM_ApplyPlanResults(t *testing.T) { 1072 t.Parallel() 1073 fsm := testFSM(t) 1074 1075 // Create the request and create a deployment 1076 alloc := mock.Alloc() 1077 job := alloc.Job 1078 alloc.Job = nil 1079 1080 d := mock.Deployment() 1081 d.JobID = job.ID 1082 d.JobModifyIndex = job.ModifyIndex 1083 d.JobVersion = job.Version 1084 1085 alloc.DeploymentID = d.ID 1086 1087 fsm.State().UpsertJobSummary(1, mock.JobSummary(alloc.JobID)) 1088 req := structs.ApplyPlanResultsRequest{ 1089 AllocUpdateRequest: structs.AllocUpdateRequest{ 1090 Job: job, 1091 Alloc: []*structs.Allocation{alloc}, 1092 }, 1093 Deployment: d, 1094 } 1095 buf, err := structs.Encode(structs.ApplyPlanResultsRequestType, req) 1096 if err != nil { 1097 t.Fatalf("err: %v", err) 1098 } 1099 1100 resp := fsm.Apply(makeLog(buf)) 1101 if resp != nil { 1102 t.Fatalf("resp: %v", resp) 1103 } 1104 1105 // Verify the allocation is registered 1106 ws := memdb.NewWatchSet() 1107 out, err := fsm.State().AllocByID(ws, alloc.ID) 1108 if err != nil { 1109 t.Fatalf("err: %v", err) 1110 } 1111 alloc.CreateIndex = out.CreateIndex 1112 alloc.ModifyIndex = out.ModifyIndex 1113 alloc.AllocModifyIndex = out.AllocModifyIndex 1114 1115 // Job should be re-attached 1116 alloc.Job = job 1117 if !reflect.DeepEqual(alloc, out) { 1118 t.Fatalf("bad: %#v %#v", alloc, out) 1119 } 1120 1121 dout, err := fsm.State().DeploymentByID(ws, d.ID) 1122 if err != nil { 1123 t.Fatalf("err: %v", err) 1124 } 1125 if tg, ok := dout.TaskGroups[alloc.TaskGroup]; !ok || tg.PlacedAllocs != 1 { 1126 t.Fatalf("err: %v %v", tg, err) 1127 } 1128 1129 // Ensure that the original job is used 1130 evictAlloc := alloc.Copy() 1131 job = mock.Job() 1132 job.Priority = 123 1133 1134 evictAlloc.Job = nil 1135 evictAlloc.DesiredStatus = structs.AllocDesiredStatusEvict 1136 req2 := structs.ApplyPlanResultsRequest{ 1137 AllocUpdateRequest: structs.AllocUpdateRequest{ 1138 Job: job, 1139 Alloc: []*structs.Allocation{evictAlloc}, 1140 }, 1141 } 1142 buf, err = structs.Encode(structs.ApplyPlanResultsRequestType, req2) 1143 if err != nil { 1144 t.Fatalf("err: %v", err) 1145 } 1146 1147 resp = fsm.Apply(makeLog(buf)) 1148 if resp != nil { 1149 t.Fatalf("resp: %v", resp) 1150 } 1151 1152 // Verify we are evicted 1153 out, err = fsm.State().AllocByID(ws, alloc.ID) 1154 if err != nil { 1155 t.Fatalf("err: %v", err) 1156 } 1157 if out.DesiredStatus != structs.AllocDesiredStatusEvict { 1158 t.Fatalf("alloc found!") 1159 } 1160 if out.Job == nil || out.Job.Priority == 123 { 1161 t.Fatalf("bad job") 1162 } 1163 } 1164 1165 func TestFSM_DeploymentStatusUpdate(t *testing.T) { 1166 t.Parallel() 1167 fsm := testFSM(t) 1168 fsm.evalBroker.SetEnabled(true) 1169 state := fsm.State() 1170 1171 // Upsert a deployment 1172 d := mock.Deployment() 1173 if err := state.UpsertDeployment(1, d); err != nil { 1174 t.Fatalf("bad: %v", err) 1175 } 1176 1177 // Create a request to update the deployment, create an eval and job 1178 e := mock.Eval() 1179 j := mock.Job() 1180 status, desc := structs.DeploymentStatusFailed, "foo" 1181 req := &structs.DeploymentStatusUpdateRequest{ 1182 DeploymentUpdate: &structs.DeploymentStatusUpdate{ 1183 DeploymentID: d.ID, 1184 Status: status, 1185 StatusDescription: desc, 1186 }, 1187 Job: j, 1188 Eval: e, 1189 } 1190 buf, err := structs.Encode(structs.DeploymentStatusUpdateRequestType, req) 1191 if err != nil { 1192 t.Fatalf("err: %v", err) 1193 } 1194 resp := fsm.Apply(makeLog(buf)) 1195 if resp != nil { 1196 t.Fatalf("resp: %v", resp) 1197 } 1198 1199 // Check that the status was updated properly 1200 ws := memdb.NewWatchSet() 1201 dout, err := state.DeploymentByID(ws, d.ID) 1202 if err != nil { 1203 t.Fatalf("bad: %v", err) 1204 } 1205 if dout.Status != status || dout.StatusDescription != desc { 1206 t.Fatalf("bad: %#v", dout) 1207 } 1208 1209 // Check that the evaluation was created 1210 eout, _ := state.EvalByID(ws, e.ID) 1211 if err != nil { 1212 t.Fatalf("bad: %v", err) 1213 } 1214 if eout == nil { 1215 t.Fatalf("bad: %#v", eout) 1216 } 1217 1218 // Check that the job was created 1219 jout, _ := state.JobByID(ws, j.ID) 1220 if err != nil { 1221 t.Fatalf("bad: %v", err) 1222 } 1223 if jout == nil { 1224 t.Fatalf("bad: %#v", jout) 1225 } 1226 1227 // Assert the eval was enqueued 1228 stats := fsm.evalBroker.Stats() 1229 if stats.TotalReady != 1 { 1230 t.Fatalf("bad: %#v %#v", stats, e) 1231 } 1232 } 1233 1234 func TestFSM_JobStabilityUpdate(t *testing.T) { 1235 t.Parallel() 1236 fsm := testFSM(t) 1237 fsm.evalBroker.SetEnabled(true) 1238 state := fsm.State() 1239 1240 // Upsert a deployment 1241 job := mock.Job() 1242 if err := state.UpsertJob(1, job); err != nil { 1243 t.Fatalf("bad: %v", err) 1244 } 1245 1246 // Create a request to update the job to stable 1247 req := &structs.JobStabilityRequest{ 1248 JobID: job.ID, 1249 JobVersion: job.Version, 1250 Stable: true, 1251 } 1252 buf, err := structs.Encode(structs.JobStabilityRequestType, req) 1253 if err != nil { 1254 t.Fatalf("err: %v", err) 1255 } 1256 resp := fsm.Apply(makeLog(buf)) 1257 if resp != nil { 1258 t.Fatalf("resp: %v", resp) 1259 } 1260 1261 // Check that the stability was updated properly 1262 ws := memdb.NewWatchSet() 1263 jout, _ := state.JobByIDAndVersion(ws, job.ID, job.Version) 1264 if err != nil { 1265 t.Fatalf("bad: %v", err) 1266 } 1267 if jout == nil || !jout.Stable { 1268 t.Fatalf("bad: %#v", jout) 1269 } 1270 } 1271 1272 func TestFSM_DeploymentPromotion(t *testing.T) { 1273 t.Parallel() 1274 fsm := testFSM(t) 1275 fsm.evalBroker.SetEnabled(true) 1276 state := fsm.State() 1277 1278 // Create a job with two task groups 1279 j := mock.Job() 1280 tg1 := j.TaskGroups[0] 1281 tg2 := tg1.Copy() 1282 tg2.Name = "foo" 1283 j.TaskGroups = append(j.TaskGroups, tg2) 1284 if err := state.UpsertJob(1, j); err != nil { 1285 t.Fatalf("bad: %v", err) 1286 } 1287 1288 // Create a deployment 1289 d := mock.Deployment() 1290 d.JobID = j.ID 1291 d.TaskGroups = map[string]*structs.DeploymentState{ 1292 "web": &structs.DeploymentState{ 1293 DesiredTotal: 10, 1294 DesiredCanaries: 1, 1295 }, 1296 "foo": &structs.DeploymentState{ 1297 DesiredTotal: 10, 1298 DesiredCanaries: 1, 1299 }, 1300 } 1301 if err := state.UpsertDeployment(2, d); err != nil { 1302 t.Fatalf("bad: %v", err) 1303 } 1304 1305 // Create a set of allocations 1306 c1 := mock.Alloc() 1307 c1.JobID = j.ID 1308 c1.DeploymentID = d.ID 1309 d.TaskGroups[c1.TaskGroup].PlacedCanaries = append(d.TaskGroups[c1.TaskGroup].PlacedCanaries, c1.ID) 1310 c1.DeploymentStatus = &structs.AllocDeploymentStatus{ 1311 Healthy: helper.BoolToPtr(true), 1312 } 1313 c2 := mock.Alloc() 1314 c2.JobID = j.ID 1315 c2.DeploymentID = d.ID 1316 d.TaskGroups[c2.TaskGroup].PlacedCanaries = append(d.TaskGroups[c2.TaskGroup].PlacedCanaries, c2.ID) 1317 c2.TaskGroup = tg2.Name 1318 c2.DeploymentStatus = &structs.AllocDeploymentStatus{ 1319 Healthy: helper.BoolToPtr(true), 1320 } 1321 1322 if err := state.UpsertAllocs(3, []*structs.Allocation{c1, c2}); err != nil { 1323 t.Fatalf("err: %v", err) 1324 } 1325 1326 // Create an eval 1327 e := mock.Eval() 1328 1329 // Promote the canaries 1330 req := &structs.ApplyDeploymentPromoteRequest{ 1331 DeploymentPromoteRequest: structs.DeploymentPromoteRequest{ 1332 DeploymentID: d.ID, 1333 All: true, 1334 }, 1335 Eval: e, 1336 } 1337 buf, err := structs.Encode(structs.DeploymentPromoteRequestType, req) 1338 if err != nil { 1339 t.Fatalf("err: %v", err) 1340 } 1341 resp := fsm.Apply(makeLog(buf)) 1342 if resp != nil { 1343 t.Fatalf("resp: %v", resp) 1344 } 1345 1346 // Check that the status per task group was updated properly 1347 ws := memdb.NewWatchSet() 1348 dout, err := state.DeploymentByID(ws, d.ID) 1349 if err != nil { 1350 t.Fatalf("bad: %v", err) 1351 } 1352 if len(dout.TaskGroups) != 2 { 1353 t.Fatalf("bad: %#v", dout.TaskGroups) 1354 } 1355 for tg, state := range dout.TaskGroups { 1356 if !state.Promoted { 1357 t.Fatalf("bad: group %q not promoted %#v", tg, state) 1358 } 1359 } 1360 1361 // Check that the evaluation was created 1362 eout, _ := state.EvalByID(ws, e.ID) 1363 if err != nil { 1364 t.Fatalf("bad: %v", err) 1365 } 1366 if eout == nil { 1367 t.Fatalf("bad: %#v", eout) 1368 } 1369 1370 // Assert the eval was enqueued 1371 stats := fsm.evalBroker.Stats() 1372 if stats.TotalReady != 1 { 1373 t.Fatalf("bad: %#v %#v", stats, e) 1374 } 1375 } 1376 1377 func TestFSM_DeploymentAllocHealth(t *testing.T) { 1378 t.Parallel() 1379 fsm := testFSM(t) 1380 fsm.evalBroker.SetEnabled(true) 1381 state := fsm.State() 1382 1383 // Insert a deployment 1384 d := mock.Deployment() 1385 if err := state.UpsertDeployment(1, d); err != nil { 1386 t.Fatalf("bad: %v", err) 1387 } 1388 1389 // Insert two allocations 1390 a1 := mock.Alloc() 1391 a1.DeploymentID = d.ID 1392 a2 := mock.Alloc() 1393 a2.DeploymentID = d.ID 1394 if err := state.UpsertAllocs(2, []*structs.Allocation{a1, a2}); err != nil { 1395 t.Fatalf("bad: %v", err) 1396 } 1397 1398 // Create a job to roll back to 1399 j := mock.Job() 1400 1401 // Create an eval that should be upserted 1402 e := mock.Eval() 1403 1404 // Create a status update for the deployment 1405 status, desc := structs.DeploymentStatusFailed, "foo" 1406 u := &structs.DeploymentStatusUpdate{ 1407 DeploymentID: d.ID, 1408 Status: status, 1409 StatusDescription: desc, 1410 } 1411 1412 // Set health against the deployment 1413 req := &structs.ApplyDeploymentAllocHealthRequest{ 1414 DeploymentAllocHealthRequest: structs.DeploymentAllocHealthRequest{ 1415 DeploymentID: d.ID, 1416 HealthyAllocationIDs: []string{a1.ID}, 1417 UnhealthyAllocationIDs: []string{a2.ID}, 1418 }, 1419 Job: j, 1420 Eval: e, 1421 DeploymentUpdate: u, 1422 } 1423 buf, err := structs.Encode(structs.DeploymentAllocHealthRequestType, req) 1424 if err != nil { 1425 t.Fatalf("err: %v", err) 1426 } 1427 resp := fsm.Apply(makeLog(buf)) 1428 if resp != nil { 1429 t.Fatalf("resp: %v", resp) 1430 } 1431 1432 // Check that the status was updated properly 1433 ws := memdb.NewWatchSet() 1434 dout, err := state.DeploymentByID(ws, d.ID) 1435 if err != nil { 1436 t.Fatalf("bad: %v", err) 1437 } 1438 if dout.Status != status || dout.StatusDescription != desc { 1439 t.Fatalf("bad: %#v", dout) 1440 } 1441 1442 // Check that the evaluation was created 1443 eout, _ := state.EvalByID(ws, e.ID) 1444 if err != nil { 1445 t.Fatalf("bad: %v", err) 1446 } 1447 if eout == nil { 1448 t.Fatalf("bad: %#v", eout) 1449 } 1450 1451 // Check that the job was created 1452 jout, _ := state.JobByID(ws, j.ID) 1453 if err != nil { 1454 t.Fatalf("bad: %v", err) 1455 } 1456 if jout == nil { 1457 t.Fatalf("bad: %#v", jout) 1458 } 1459 1460 // Check the status of the allocs 1461 out1, err := state.AllocByID(ws, a1.ID) 1462 if err != nil { 1463 t.Fatalf("err: %v", err) 1464 } 1465 out2, err := state.AllocByID(ws, a2.ID) 1466 if err != nil { 1467 t.Fatalf("err: %v", err) 1468 } 1469 1470 if !out1.DeploymentStatus.IsHealthy() { 1471 t.Fatalf("bad: alloc %q not healthy", out1.ID) 1472 } 1473 if !out2.DeploymentStatus.IsUnhealthy() { 1474 t.Fatalf("bad: alloc %q not unhealthy", out2.ID) 1475 } 1476 1477 // Assert the eval was enqueued 1478 stats := fsm.evalBroker.Stats() 1479 if stats.TotalReady != 1 { 1480 t.Fatalf("bad: %#v %#v", stats, e) 1481 } 1482 } 1483 1484 func TestFSM_DeleteDeployment(t *testing.T) { 1485 t.Parallel() 1486 fsm := testFSM(t) 1487 state := fsm.State() 1488 1489 // Upsert a deployments 1490 d := mock.Deployment() 1491 if err := state.UpsertDeployment(1, d); err != nil { 1492 t.Fatalf("bad: %v", err) 1493 } 1494 1495 req := structs.DeploymentDeleteRequest{ 1496 Deployments: []string{d.ID}, 1497 } 1498 buf, err := structs.Encode(structs.DeploymentDeleteRequestType, req) 1499 if err != nil { 1500 t.Fatalf("err: %v", err) 1501 } 1502 1503 resp := fsm.Apply(makeLog(buf)) 1504 if resp != nil { 1505 t.Fatalf("resp: %v", resp) 1506 } 1507 1508 // Verify we are NOT registered 1509 ws := memdb.NewWatchSet() 1510 deployment, err := state.DeploymentByID(ws, d.ID) 1511 if err != nil { 1512 t.Fatalf("err: %v", err) 1513 } 1514 if deployment != nil { 1515 t.Fatalf("deployment found!") 1516 } 1517 } 1518 1519 func testSnapshotRestore(t *testing.T, fsm *nomadFSM) *nomadFSM { 1520 // Snapshot 1521 snap, err := fsm.Snapshot() 1522 if err != nil { 1523 t.Fatalf("err: %v", err) 1524 } 1525 defer snap.Release() 1526 1527 // Persist 1528 buf := bytes.NewBuffer(nil) 1529 sink := &MockSink{buf, false} 1530 if err := snap.Persist(sink); err != nil { 1531 t.Fatalf("err: %v", err) 1532 } 1533 1534 // Try to restore on a new FSM 1535 fsm2 := testFSM(t) 1536 snap, err = fsm2.Snapshot() 1537 if err != nil { 1538 t.Fatalf("err: %v", err) 1539 } 1540 defer snap.Release() 1541 1542 abandonCh := fsm2.State().AbandonCh() 1543 1544 // Do a restore 1545 if err := fsm2.Restore(sink); err != nil { 1546 t.Fatalf("err: %v", err) 1547 } 1548 1549 select { 1550 case <-abandonCh: 1551 default: 1552 t.Fatalf("bad") 1553 } 1554 1555 return fsm2 1556 } 1557 1558 func TestFSM_SnapshotRestore_Nodes(t *testing.T) { 1559 t.Parallel() 1560 // Add some state 1561 fsm := testFSM(t) 1562 state := fsm.State() 1563 node1 := mock.Node() 1564 state.UpsertNode(1000, node1) 1565 node2 := mock.Node() 1566 state.UpsertNode(1001, node2) 1567 1568 // Verify the contents 1569 fsm2 := testSnapshotRestore(t, fsm) 1570 state2 := fsm2.State() 1571 ws := memdb.NewWatchSet() 1572 out1, _ := state2.NodeByID(ws, node1.ID) 1573 out2, _ := state2.NodeByID(ws, node2.ID) 1574 if !reflect.DeepEqual(node1, out1) { 1575 t.Fatalf("bad: \n%#v\n%#v", out1, node1) 1576 } 1577 if !reflect.DeepEqual(node2, out2) { 1578 t.Fatalf("bad: \n%#v\n%#v", out2, node2) 1579 } 1580 } 1581 1582 func TestFSM_SnapshotRestore_Jobs(t *testing.T) { 1583 t.Parallel() 1584 // Add some state 1585 fsm := testFSM(t) 1586 state := fsm.State() 1587 job1 := mock.Job() 1588 state.UpsertJob(1000, job1) 1589 job2 := mock.Job() 1590 state.UpsertJob(1001, job2) 1591 1592 // Verify the contents 1593 ws := memdb.NewWatchSet() 1594 fsm2 := testSnapshotRestore(t, fsm) 1595 state2 := fsm2.State() 1596 out1, _ := state2.JobByID(ws, job1.ID) 1597 out2, _ := state2.JobByID(ws, job2.ID) 1598 if !reflect.DeepEqual(job1, out1) { 1599 t.Fatalf("bad: \n%#v\n%#v", out1, job1) 1600 } 1601 if !reflect.DeepEqual(job2, out2) { 1602 t.Fatalf("bad: \n%#v\n%#v", out2, job2) 1603 } 1604 } 1605 1606 func TestFSM_SnapshotRestore_Evals(t *testing.T) { 1607 t.Parallel() 1608 // Add some state 1609 fsm := testFSM(t) 1610 state := fsm.State() 1611 eval1 := mock.Eval() 1612 state.UpsertEvals(1000, []*structs.Evaluation{eval1}) 1613 eval2 := mock.Eval() 1614 state.UpsertEvals(1001, []*structs.Evaluation{eval2}) 1615 1616 // Verify the contents 1617 fsm2 := testSnapshotRestore(t, fsm) 1618 state2 := fsm2.State() 1619 ws := memdb.NewWatchSet() 1620 out1, _ := state2.EvalByID(ws, eval1.ID) 1621 out2, _ := state2.EvalByID(ws, eval2.ID) 1622 if !reflect.DeepEqual(eval1, out1) { 1623 t.Fatalf("bad: \n%#v\n%#v", out1, eval1) 1624 } 1625 if !reflect.DeepEqual(eval2, out2) { 1626 t.Fatalf("bad: \n%#v\n%#v", out2, eval2) 1627 } 1628 } 1629 1630 func TestFSM_SnapshotRestore_Allocs(t *testing.T) { 1631 t.Parallel() 1632 // Add some state 1633 fsm := testFSM(t) 1634 state := fsm.State() 1635 alloc1 := mock.Alloc() 1636 alloc2 := mock.Alloc() 1637 state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID)) 1638 state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID)) 1639 state.UpsertAllocs(1000, []*structs.Allocation{alloc1}) 1640 state.UpsertAllocs(1001, []*structs.Allocation{alloc2}) 1641 1642 // Verify the contents 1643 fsm2 := testSnapshotRestore(t, fsm) 1644 state2 := fsm2.State() 1645 ws := memdb.NewWatchSet() 1646 out1, _ := state2.AllocByID(ws, alloc1.ID) 1647 out2, _ := state2.AllocByID(ws, alloc2.ID) 1648 if !reflect.DeepEqual(alloc1, out1) { 1649 t.Fatalf("bad: \n%#v\n%#v", out1, alloc1) 1650 } 1651 if !reflect.DeepEqual(alloc2, out2) { 1652 t.Fatalf("bad: \n%#v\n%#v", out2, alloc2) 1653 } 1654 } 1655 1656 func TestFSM_SnapshotRestore_Allocs_NoSharedResources(t *testing.T) { 1657 t.Parallel() 1658 // Add some state 1659 fsm := testFSM(t) 1660 state := fsm.State() 1661 alloc1 := mock.Alloc() 1662 alloc2 := mock.Alloc() 1663 alloc1.SharedResources = nil 1664 alloc2.SharedResources = nil 1665 state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID)) 1666 state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID)) 1667 state.UpsertAllocs(1000, []*structs.Allocation{alloc1}) 1668 state.UpsertAllocs(1001, []*structs.Allocation{alloc2}) 1669 1670 // Verify the contents 1671 fsm2 := testSnapshotRestore(t, fsm) 1672 state2 := fsm2.State() 1673 ws := memdb.NewWatchSet() 1674 out1, _ := state2.AllocByID(ws, alloc1.ID) 1675 out2, _ := state2.AllocByID(ws, alloc2.ID) 1676 alloc1.SharedResources = &structs.Resources{DiskMB: 150} 1677 alloc2.SharedResources = &structs.Resources{DiskMB: 150} 1678 1679 if !reflect.DeepEqual(alloc1, out1) { 1680 t.Fatalf("bad: \n%#v\n%#v", out1, alloc1) 1681 } 1682 if !reflect.DeepEqual(alloc2, out2) { 1683 t.Fatalf("bad: \n%#v\n%#v", out2, alloc2) 1684 } 1685 } 1686 1687 func TestFSM_SnapshotRestore_Indexes(t *testing.T) { 1688 t.Parallel() 1689 // Add some state 1690 fsm := testFSM(t) 1691 state := fsm.State() 1692 node1 := mock.Node() 1693 state.UpsertNode(1000, node1) 1694 1695 // Verify the contents 1696 fsm2 := testSnapshotRestore(t, fsm) 1697 state2 := fsm2.State() 1698 1699 index, err := state2.Index("nodes") 1700 if err != nil { 1701 t.Fatalf("err: %v", err) 1702 } 1703 if index != 1000 { 1704 t.Fatalf("bad: %d", index) 1705 } 1706 } 1707 1708 func TestFSM_SnapshotRestore_TimeTable(t *testing.T) { 1709 t.Parallel() 1710 // Add some state 1711 fsm := testFSM(t) 1712 1713 tt := fsm.TimeTable() 1714 start := time.Now().UTC() 1715 tt.Witness(1000, start) 1716 tt.Witness(2000, start.Add(10*time.Minute)) 1717 1718 // Verify the contents 1719 fsm2 := testSnapshotRestore(t, fsm) 1720 1721 tt2 := fsm2.TimeTable() 1722 if tt2.NearestTime(1500) != start { 1723 t.Fatalf("bad") 1724 } 1725 if tt2.NearestIndex(start.Add(15*time.Minute)) != 2000 { 1726 t.Fatalf("bad") 1727 } 1728 } 1729 1730 func TestFSM_SnapshotRestore_PeriodicLaunches(t *testing.T) { 1731 t.Parallel() 1732 // Add some state 1733 fsm := testFSM(t) 1734 state := fsm.State() 1735 job1 := mock.Job() 1736 launch1 := &structs.PeriodicLaunch{ID: job1.ID, Launch: time.Now()} 1737 state.UpsertPeriodicLaunch(1000, launch1) 1738 job2 := mock.Job() 1739 launch2 := &structs.PeriodicLaunch{ID: job2.ID, Launch: time.Now()} 1740 state.UpsertPeriodicLaunch(1001, launch2) 1741 1742 // Verify the contents 1743 fsm2 := testSnapshotRestore(t, fsm) 1744 state2 := fsm2.State() 1745 ws := memdb.NewWatchSet() 1746 out1, _ := state2.PeriodicLaunchByID(ws, launch1.ID) 1747 out2, _ := state2.PeriodicLaunchByID(ws, launch2.ID) 1748 if !reflect.DeepEqual(launch1, out1) { 1749 t.Fatalf("bad: \n%#v\n%#v", out1, job1) 1750 } 1751 if !reflect.DeepEqual(launch2, out2) { 1752 t.Fatalf("bad: \n%#v\n%#v", out2, job2) 1753 } 1754 } 1755 1756 func TestFSM_SnapshotRestore_JobSummary(t *testing.T) { 1757 t.Parallel() 1758 // Add some state 1759 fsm := testFSM(t) 1760 state := fsm.State() 1761 1762 job1 := mock.Job() 1763 state.UpsertJob(1000, job1) 1764 ws := memdb.NewWatchSet() 1765 js1, _ := state.JobSummaryByID(ws, job1.ID) 1766 1767 job2 := mock.Job() 1768 state.UpsertJob(1001, job2) 1769 js2, _ := state.JobSummaryByID(ws, job2.ID) 1770 1771 // Verify the contents 1772 fsm2 := testSnapshotRestore(t, fsm) 1773 state2 := fsm2.State() 1774 out1, _ := state2.JobSummaryByID(ws, job1.ID) 1775 out2, _ := state2.JobSummaryByID(ws, job2.ID) 1776 if !reflect.DeepEqual(js1, out1) { 1777 t.Fatalf("bad: \n%#v\n%#v", js1, out1) 1778 } 1779 if !reflect.DeepEqual(js2, out2) { 1780 t.Fatalf("bad: \n%#v\n%#v", js2, out2) 1781 } 1782 } 1783 1784 func TestFSM_SnapshotRestore_VaultAccessors(t *testing.T) { 1785 t.Parallel() 1786 // Add some state 1787 fsm := testFSM(t) 1788 state := fsm.State() 1789 a1 := mock.VaultAccessor() 1790 a2 := mock.VaultAccessor() 1791 state.UpsertVaultAccessor(1000, []*structs.VaultAccessor{a1, a2}) 1792 1793 // Verify the contents 1794 fsm2 := testSnapshotRestore(t, fsm) 1795 state2 := fsm2.State() 1796 ws := memdb.NewWatchSet() 1797 out1, _ := state2.VaultAccessor(ws, a1.Accessor) 1798 out2, _ := state2.VaultAccessor(ws, a2.Accessor) 1799 if !reflect.DeepEqual(a1, out1) { 1800 t.Fatalf("bad: \n%#v\n%#v", out1, a1) 1801 } 1802 if !reflect.DeepEqual(a2, out2) { 1803 t.Fatalf("bad: \n%#v\n%#v", out2, a2) 1804 } 1805 } 1806 1807 func TestFSM_SnapshotRestore_JobVersions(t *testing.T) { 1808 t.Parallel() 1809 // Add some state 1810 fsm := testFSM(t) 1811 state := fsm.State() 1812 job1 := mock.Job() 1813 state.UpsertJob(1000, job1) 1814 job2 := mock.Job() 1815 job2.ID = job1.ID 1816 state.UpsertJob(1001, job2) 1817 1818 // Verify the contents 1819 ws := memdb.NewWatchSet() 1820 fsm2 := testSnapshotRestore(t, fsm) 1821 state2 := fsm2.State() 1822 out1, _ := state2.JobByIDAndVersion(ws, job1.ID, job1.Version) 1823 out2, _ := state2.JobByIDAndVersion(ws, job2.ID, job2.Version) 1824 if !reflect.DeepEqual(job1, out1) { 1825 t.Fatalf("bad: \n%#v\n%#v", out1, job1) 1826 } 1827 if !reflect.DeepEqual(job2, out2) { 1828 t.Fatalf("bad: \n%#v\n%#v", out2, job2) 1829 } 1830 if job2.Version != 1 { 1831 t.Fatalf("bad: \n%#v\n%#v", 1, job2) 1832 } 1833 } 1834 1835 func TestFSM_SnapshotRestore_Deployments(t *testing.T) { 1836 t.Parallel() 1837 // Add some state 1838 fsm := testFSM(t) 1839 state := fsm.State() 1840 d1 := mock.Deployment() 1841 d2 := mock.Deployment() 1842 state.UpsertDeployment(1000, d1) 1843 state.UpsertDeployment(1001, d2) 1844 1845 // Verify the contents 1846 fsm2 := testSnapshotRestore(t, fsm) 1847 state2 := fsm2.State() 1848 ws := memdb.NewWatchSet() 1849 out1, _ := state2.DeploymentByID(ws, d1.ID) 1850 out2, _ := state2.DeploymentByID(ws, d2.ID) 1851 if !reflect.DeepEqual(d1, out1) { 1852 t.Fatalf("bad: \n%#v\n%#v", out1, d1) 1853 } 1854 if !reflect.DeepEqual(d2, out2) { 1855 t.Fatalf("bad: \n%#v\n%#v", out2, d2) 1856 } 1857 } 1858 1859 func TestFSM_SnapshotRestore_AddMissingSummary(t *testing.T) { 1860 t.Parallel() 1861 // Add some state 1862 fsm := testFSM(t) 1863 state := fsm.State() 1864 1865 // make an allocation 1866 alloc := mock.Alloc() 1867 state.UpsertJob(1010, alloc.Job) 1868 state.UpsertAllocs(1011, []*structs.Allocation{alloc}) 1869 1870 // Delete the summary 1871 state.DeleteJobSummary(1040, alloc.Job.ID) 1872 1873 // Delete the index 1874 if err := state.RemoveIndex("job_summary"); err != nil { 1875 t.Fatalf("err: %v", err) 1876 } 1877 1878 fsm2 := testSnapshotRestore(t, fsm) 1879 state2 := fsm2.State() 1880 latestIndex, _ := state.LatestIndex() 1881 1882 ws := memdb.NewWatchSet() 1883 out, _ := state2.JobSummaryByID(ws, alloc.Job.ID) 1884 expected := structs.JobSummary{ 1885 JobID: alloc.Job.ID, 1886 Summary: map[string]structs.TaskGroupSummary{ 1887 "web": structs.TaskGroupSummary{ 1888 Starting: 1, 1889 }, 1890 }, 1891 CreateIndex: 1010, 1892 ModifyIndex: latestIndex, 1893 } 1894 if !reflect.DeepEqual(&expected, out) { 1895 t.Fatalf("expected: %#v, actual: %#v", &expected, out) 1896 } 1897 } 1898 1899 func TestFSM_ReconcileSummaries(t *testing.T) { 1900 t.Parallel() 1901 // Add some state 1902 fsm := testFSM(t) 1903 state := fsm.State() 1904 1905 // Add a node 1906 node := mock.Node() 1907 state.UpsertNode(800, node) 1908 1909 // Make a job so that none of the tasks can be placed 1910 job1 := mock.Job() 1911 job1.TaskGroups[0].Tasks[0].Resources.CPU = 5000 1912 state.UpsertJob(1000, job1) 1913 1914 // make a job which can make partial progress 1915 alloc := mock.Alloc() 1916 alloc.NodeID = node.ID 1917 state.UpsertJob(1010, alloc.Job) 1918 state.UpsertAllocs(1011, []*structs.Allocation{alloc}) 1919 1920 // Delete the summaries 1921 state.DeleteJobSummary(1030, job1.ID) 1922 state.DeleteJobSummary(1040, alloc.Job.ID) 1923 1924 req := structs.GenericRequest{} 1925 buf, err := structs.Encode(structs.ReconcileJobSummariesRequestType, req) 1926 if err != nil { 1927 t.Fatalf("err: %v", err) 1928 } 1929 1930 resp := fsm.Apply(makeLog(buf)) 1931 if resp != nil { 1932 t.Fatalf("resp: %v", resp) 1933 } 1934 1935 ws := memdb.NewWatchSet() 1936 out1, _ := state.JobSummaryByID(ws, job1.ID) 1937 expected := structs.JobSummary{ 1938 JobID: job1.ID, 1939 Summary: map[string]structs.TaskGroupSummary{ 1940 "web": structs.TaskGroupSummary{ 1941 Queued: 10, 1942 }, 1943 }, 1944 CreateIndex: 1000, 1945 ModifyIndex: out1.ModifyIndex, 1946 } 1947 if !reflect.DeepEqual(&expected, out1) { 1948 t.Fatalf("expected: %#v, actual: %#v", &expected, out1) 1949 } 1950 1951 // This exercises the code path which adds the allocations made by the 1952 // planner and the number of unplaced allocations in the reconcile summaries 1953 // codepath 1954 out2, _ := state.JobSummaryByID(ws, alloc.Job.ID) 1955 expected = structs.JobSummary{ 1956 JobID: alloc.Job.ID, 1957 Summary: map[string]structs.TaskGroupSummary{ 1958 "web": structs.TaskGroupSummary{ 1959 Queued: 9, 1960 Starting: 1, 1961 }, 1962 }, 1963 CreateIndex: 1010, 1964 ModifyIndex: out2.ModifyIndex, 1965 } 1966 if !reflect.DeepEqual(&expected, out2) { 1967 t.Fatalf("Diff % #v", pretty.Diff(&expected, out2)) 1968 } 1969 }