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