github.com/manicqin/nomad@v0.9.5/nomad/fsm_test.go (about) 1 package nomad 2 3 import ( 4 "bytes" 5 "fmt" 6 "reflect" 7 "strings" 8 "testing" 9 "time" 10 11 "github.com/google/go-cmp/cmp" 12 memdb "github.com/hashicorp/go-memdb" 13 "github.com/hashicorp/nomad/helper" 14 "github.com/hashicorp/nomad/helper/testlog" 15 "github.com/hashicorp/nomad/helper/uuid" 16 "github.com/hashicorp/nomad/nomad/mock" 17 "github.com/hashicorp/nomad/nomad/state" 18 "github.com/hashicorp/nomad/nomad/structs" 19 "github.com/hashicorp/nomad/testutil" 20 "github.com/hashicorp/raft" 21 "github.com/kr/pretty" 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 ) 25 26 type MockSink struct { 27 *bytes.Buffer 28 cancel bool 29 } 30 31 func (m *MockSink) ID() string { 32 return "Mock" 33 } 34 35 func (m *MockSink) Cancel() error { 36 m.cancel = true 37 return nil 38 } 39 40 func (m *MockSink) Close() error { 41 return nil 42 } 43 44 func testStateStore(t *testing.T) *state.StateStore { 45 return state.TestStateStore(t) 46 } 47 48 func testFSM(t *testing.T) *nomadFSM { 49 broker := testBroker(t, 0) 50 dispatcher, _ := testPeriodicDispatcher(t) 51 logger := testlog.HCLogger(t) 52 fsmConfig := &FSMConfig{ 53 EvalBroker: broker, 54 Periodic: dispatcher, 55 Blocked: NewBlockedEvals(broker, logger), 56 Logger: logger, 57 Region: "global", 58 } 59 fsm, err := NewFSM(fsmConfig) 60 if err != nil { 61 t.Fatalf("err: %v", err) 62 } 63 if fsm == nil { 64 t.Fatalf("missing fsm") 65 } 66 state.TestInitState(t, fsm.state) 67 return fsm 68 } 69 70 func makeLog(buf []byte) *raft.Log { 71 return &raft.Log{ 72 Index: 1, 73 Term: 1, 74 Type: raft.LogCommand, 75 Data: buf, 76 } 77 } 78 79 func TestFSM_UpsertNodeEvents(t *testing.T) { 80 t.Parallel() 81 require := require.New(t) 82 fsm := testFSM(t) 83 state := fsm.State() 84 85 node := mock.Node() 86 87 err := state.UpsertNode(1000, node) 88 if err != nil { 89 t.Fatalf("err: %v", err) 90 } 91 92 nodeEvent := &structs.NodeEvent{ 93 Message: "Heartbeating failed", 94 Subsystem: "Heartbeat", 95 Timestamp: time.Now(), 96 } 97 98 nodeEvents := []*structs.NodeEvent{nodeEvent} 99 allEvents := map[string][]*structs.NodeEvent{node.ID: nodeEvents} 100 101 req := structs.EmitNodeEventsRequest{ 102 NodeEvents: allEvents, 103 WriteRequest: structs.WriteRequest{Region: "global"}, 104 } 105 buf, err := structs.Encode(structs.UpsertNodeEventsType, req) 106 require.Nil(err) 107 108 // the response in this case will be an error 109 resp := fsm.Apply(makeLog(buf)) 110 require.Nil(resp) 111 112 ws := memdb.NewWatchSet() 113 out, err := state.NodeByID(ws, node.ID) 114 require.Nil(err) 115 116 require.Equal(2, len(out.Events)) 117 118 first := out.Events[1] 119 require.Equal(uint64(1), first.CreateIndex) 120 require.Equal("Heartbeating failed", first.Message) 121 } 122 123 func TestFSM_UpsertNode(t *testing.T) { 124 t.Parallel() 125 fsm := testFSM(t) 126 fsm.blockedEvals.SetEnabled(true) 127 128 node := mock.Node() 129 130 // Mark an eval as blocked. 131 eval := mock.Eval() 132 eval.ClassEligibility = map[string]bool{node.ComputedClass: true} 133 fsm.blockedEvals.Block(eval) 134 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 // Verify we are registered 149 ws := memdb.NewWatchSet() 150 n, err := fsm.State().NodeByID(ws, req.Node.ID) 151 if err != nil { 152 t.Fatalf("err: %v", err) 153 } 154 if n == nil { 155 t.Fatalf("not found!") 156 } 157 if n.CreateIndex != 1 { 158 t.Fatalf("bad index: %d", node.CreateIndex) 159 } 160 161 tt := fsm.TimeTable() 162 index := tt.NearestIndex(time.Now().UTC()) 163 if index != 1 { 164 t.Fatalf("bad: %d", index) 165 } 166 167 // Verify the eval was unblocked. 168 testutil.WaitForResult(func() (bool, error) { 169 bStats := fsm.blockedEvals.Stats() 170 if bStats.TotalBlocked != 0 { 171 return false, fmt.Errorf("bad: %#v", bStats) 172 } 173 return true, nil 174 }, func(err error) { 175 t.Fatalf("err: %s", err) 176 }) 177 178 } 179 180 func TestFSM_UpsertNode_Canonicalize(t *testing.T) { 181 t.Parallel() 182 require := require.New(t) 183 184 fsm := testFSM(t) 185 fsm.blockedEvals.SetEnabled(true) 186 187 // Setup a node without eligiblity 188 node := mock.Node() 189 node.SchedulingEligibility = "" 190 191 req := structs.NodeRegisterRequest{ 192 Node: node, 193 } 194 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 195 require.Nil(err) 196 197 resp := fsm.Apply(makeLog(buf)) 198 require.Nil(resp) 199 200 // Verify we are registered 201 ws := memdb.NewWatchSet() 202 n, err := fsm.State().NodeByID(ws, req.Node.ID) 203 require.Nil(err) 204 require.NotNil(n) 205 require.EqualValues(1, n.CreateIndex) 206 require.Equal(structs.NodeSchedulingEligible, n.SchedulingEligibility) 207 } 208 209 func TestFSM_DeregisterNode(t *testing.T) { 210 t.Parallel() 211 fsm := testFSM(t) 212 213 node := mock.Node() 214 req := structs.NodeRegisterRequest{ 215 Node: node, 216 } 217 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 218 if err != nil { 219 t.Fatalf("err: %v", err) 220 } 221 222 resp := fsm.Apply(makeLog(buf)) 223 if resp != nil { 224 t.Fatalf("resp: %v", resp) 225 } 226 227 req2 := structs.NodeBatchDeregisterRequest{ 228 NodeIDs: []string{node.ID}, 229 } 230 buf, err = structs.Encode(structs.NodeBatchDeregisterRequestType, req2) 231 if err != nil { 232 t.Fatalf("err: %v", err) 233 } 234 235 resp = fsm.Apply(makeLog(buf)) 236 if resp != nil { 237 t.Fatalf("resp: %v", resp) 238 } 239 240 // Verify we are NOT registered 241 ws := memdb.NewWatchSet() 242 node, err = fsm.State().NodeByID(ws, req.Node.ID) 243 if err != nil { 244 t.Fatalf("err: %v", err) 245 } 246 if node != nil { 247 t.Fatalf("node found!") 248 } 249 } 250 251 func TestFSM_UpdateNodeStatus(t *testing.T) { 252 t.Parallel() 253 require := require.New(t) 254 fsm := testFSM(t) 255 fsm.blockedEvals.SetEnabled(true) 256 257 node := mock.Node() 258 req := structs.NodeRegisterRequest{ 259 Node: node, 260 } 261 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 262 require.NoError(err) 263 264 resp := fsm.Apply(makeLog(buf)) 265 require.Nil(resp) 266 267 // Mark an eval as blocked. 268 eval := mock.Eval() 269 eval.ClassEligibility = map[string]bool{node.ComputedClass: true} 270 fsm.blockedEvals.Block(eval) 271 272 event := &structs.NodeEvent{ 273 Message: "Node ready foo", 274 Subsystem: structs.NodeEventSubsystemCluster, 275 Timestamp: time.Now(), 276 } 277 req2 := structs.NodeUpdateStatusRequest{ 278 NodeID: node.ID, 279 Status: structs.NodeStatusReady, 280 NodeEvent: event, 281 } 282 buf, err = structs.Encode(structs.NodeUpdateStatusRequestType, req2) 283 require.NoError(err) 284 285 resp = fsm.Apply(makeLog(buf)) 286 require.Nil(resp) 287 288 // Verify the status is ready. 289 ws := memdb.NewWatchSet() 290 node, err = fsm.State().NodeByID(ws, req.Node.ID) 291 require.NoError(err) 292 require.Equal(structs.NodeStatusReady, node.Status) 293 require.Len(node.Events, 2) 294 require.Equal(event.Message, node.Events[1].Message) 295 296 // Verify the eval was unblocked. 297 testutil.WaitForResult(func() (bool, error) { 298 bStats := fsm.blockedEvals.Stats() 299 if bStats.TotalBlocked != 0 { 300 return false, fmt.Errorf("bad: %#v", bStats) 301 } 302 return true, nil 303 }, func(err error) { 304 t.Fatalf("err: %s", err) 305 }) 306 } 307 308 func TestFSM_BatchUpdateNodeDrain(t *testing.T) { 309 t.Parallel() 310 require := require.New(t) 311 fsm := testFSM(t) 312 313 node := mock.Node() 314 req := structs.NodeRegisterRequest{ 315 Node: node, 316 } 317 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 318 require.Nil(err) 319 320 resp := fsm.Apply(makeLog(buf)) 321 require.Nil(resp) 322 323 strategy := &structs.DrainStrategy{ 324 DrainSpec: structs.DrainSpec{ 325 Deadline: 10 * time.Second, 326 }, 327 } 328 event := &structs.NodeEvent{ 329 Message: "Drain strategy enabled", 330 Subsystem: structs.NodeEventSubsystemDrain, 331 Timestamp: time.Now(), 332 } 333 req2 := structs.BatchNodeUpdateDrainRequest{ 334 Updates: map[string]*structs.DrainUpdate{ 335 node.ID: { 336 DrainStrategy: strategy, 337 }, 338 }, 339 NodeEvents: map[string]*structs.NodeEvent{ 340 node.ID: event, 341 }, 342 } 343 buf, err = structs.Encode(structs.BatchNodeUpdateDrainRequestType, req2) 344 require.Nil(err) 345 346 resp = fsm.Apply(makeLog(buf)) 347 require.Nil(resp) 348 349 // Verify drain is set 350 ws := memdb.NewWatchSet() 351 node, err = fsm.State().NodeByID(ws, req.Node.ID) 352 require.Nil(err) 353 require.True(node.Drain) 354 require.Equal(node.DrainStrategy, strategy) 355 require.Len(node.Events, 2) 356 } 357 358 func TestFSM_UpdateNodeDrain(t *testing.T) { 359 t.Parallel() 360 require := require.New(t) 361 fsm := testFSM(t) 362 363 node := mock.Node() 364 req := structs.NodeRegisterRequest{ 365 Node: node, 366 } 367 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 368 require.Nil(err) 369 370 resp := fsm.Apply(makeLog(buf)) 371 require.Nil(resp) 372 373 strategy := &structs.DrainStrategy{ 374 DrainSpec: structs.DrainSpec{ 375 Deadline: 10 * time.Second, 376 }, 377 } 378 req2 := structs.NodeUpdateDrainRequest{ 379 NodeID: node.ID, 380 DrainStrategy: strategy, 381 NodeEvent: &structs.NodeEvent{ 382 Message: "Drain strategy enabled", 383 Subsystem: structs.NodeEventSubsystemDrain, 384 Timestamp: time.Now(), 385 }, 386 } 387 buf, err = structs.Encode(structs.NodeUpdateDrainRequestType, req2) 388 require.Nil(err) 389 390 resp = fsm.Apply(makeLog(buf)) 391 require.Nil(resp) 392 393 // Verify we are NOT registered 394 ws := memdb.NewWatchSet() 395 node, err = fsm.State().NodeByID(ws, req.Node.ID) 396 require.Nil(err) 397 require.True(node.Drain) 398 require.Equal(node.DrainStrategy, strategy) 399 require.Len(node.Events, 2) 400 } 401 402 func TestFSM_UpdateNodeDrain_Pre08_Compatibility(t *testing.T) { 403 t.Parallel() 404 require := require.New(t) 405 fsm := testFSM(t) 406 407 // Force a node into the state store without eligiblity 408 node := mock.Node() 409 node.SchedulingEligibility = "" 410 require.Nil(fsm.State().UpsertNode(1, node)) 411 412 // Do an old style drain 413 req := structs.NodeUpdateDrainRequest{ 414 NodeID: node.ID, 415 Drain: true, 416 } 417 buf, err := structs.Encode(structs.NodeUpdateDrainRequestType, req) 418 require.Nil(err) 419 420 resp := fsm.Apply(makeLog(buf)) 421 require.Nil(resp) 422 423 // Verify we have upgraded to a force drain 424 ws := memdb.NewWatchSet() 425 node, err = fsm.State().NodeByID(ws, req.NodeID) 426 require.Nil(err) 427 require.True(node.Drain) 428 429 expected := &structs.DrainStrategy{ 430 DrainSpec: structs.DrainSpec{ 431 Deadline: -1 * time.Second, 432 }, 433 } 434 require.Equal(expected, node.DrainStrategy) 435 } 436 437 func TestFSM_UpdateNodeEligibility(t *testing.T) { 438 t.Parallel() 439 require := require.New(t) 440 fsm := testFSM(t) 441 442 node := mock.Node() 443 req := structs.NodeRegisterRequest{ 444 Node: node, 445 } 446 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 447 require.Nil(err) 448 449 resp := fsm.Apply(makeLog(buf)) 450 require.Nil(resp) 451 452 event := &structs.NodeEvent{ 453 Message: "Node marked as ineligible", 454 Subsystem: structs.NodeEventSubsystemCluster, 455 Timestamp: time.Now(), 456 } 457 458 // Set the eligibility 459 req2 := structs.NodeUpdateEligibilityRequest{ 460 NodeID: node.ID, 461 Eligibility: structs.NodeSchedulingIneligible, 462 NodeEvent: event, 463 } 464 buf, err = structs.Encode(structs.NodeUpdateEligibilityRequestType, req2) 465 require.Nil(err) 466 467 resp = fsm.Apply(makeLog(buf)) 468 require.Nil(resp) 469 470 // Lookup the node and check 471 node, err = fsm.State().NodeByID(nil, req.Node.ID) 472 require.Nil(err) 473 require.Equal(node.SchedulingEligibility, structs.NodeSchedulingIneligible) 474 require.Len(node.Events, 2) 475 require.Equal(event.Message, node.Events[1].Message) 476 477 // Update the drain 478 strategy := &structs.DrainStrategy{ 479 DrainSpec: structs.DrainSpec{ 480 Deadline: 10 * time.Second, 481 }, 482 } 483 req3 := structs.NodeUpdateDrainRequest{ 484 NodeID: node.ID, 485 DrainStrategy: strategy, 486 } 487 buf, err = structs.Encode(structs.NodeUpdateDrainRequestType, req3) 488 require.Nil(err) 489 resp = fsm.Apply(makeLog(buf)) 490 require.Nil(resp) 491 492 // Try forcing eligibility 493 req4 := structs.NodeUpdateEligibilityRequest{ 494 NodeID: node.ID, 495 Eligibility: structs.NodeSchedulingEligible, 496 } 497 buf, err = structs.Encode(structs.NodeUpdateEligibilityRequestType, req4) 498 require.Nil(err) 499 500 resp = fsm.Apply(makeLog(buf)) 501 require.NotNil(resp) 502 err, ok := resp.(error) 503 require.True(ok) 504 require.Contains(err.Error(), "draining") 505 } 506 507 func TestFSM_UpdateNodeEligibility_Unblock(t *testing.T) { 508 t.Parallel() 509 require := require.New(t) 510 fsm := testFSM(t) 511 512 node := mock.Node() 513 req := structs.NodeRegisterRequest{ 514 Node: node, 515 } 516 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 517 require.Nil(err) 518 519 resp := fsm.Apply(makeLog(buf)) 520 require.Nil(resp) 521 522 // Set the eligibility 523 req2 := structs.NodeUpdateEligibilityRequest{ 524 NodeID: node.ID, 525 Eligibility: structs.NodeSchedulingIneligible, 526 } 527 buf, err = structs.Encode(structs.NodeUpdateEligibilityRequestType, req2) 528 require.Nil(err) 529 530 resp = fsm.Apply(makeLog(buf)) 531 require.Nil(resp) 532 533 // Mark an eval as blocked. 534 eval := mock.Eval() 535 eval.ClassEligibility = map[string]bool{node.ComputedClass: true} 536 fsm.blockedEvals.Block(eval) 537 538 // Set eligible 539 req4 := structs.NodeUpdateEligibilityRequest{ 540 NodeID: node.ID, 541 Eligibility: structs.NodeSchedulingEligible, 542 } 543 buf, err = structs.Encode(structs.NodeUpdateEligibilityRequestType, req4) 544 require.Nil(err) 545 546 resp = fsm.Apply(makeLog(buf)) 547 require.Nil(resp) 548 549 // Verify the eval was unblocked. 550 testutil.WaitForResult(func() (bool, error) { 551 bStats := fsm.blockedEvals.Stats() 552 if bStats.TotalBlocked != 0 { 553 return false, fmt.Errorf("bad: %#v", bStats) 554 } 555 return true, nil 556 }, func(err error) { 557 t.Fatalf("err: %s", err) 558 }) 559 } 560 561 func TestFSM_RegisterJob(t *testing.T) { 562 t.Parallel() 563 fsm := testFSM(t) 564 565 job := mock.PeriodicJob() 566 req := structs.JobRegisterRequest{ 567 Job: job, 568 WriteRequest: structs.WriteRequest{ 569 Namespace: job.Namespace, 570 }, 571 } 572 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 573 if err != nil { 574 t.Fatalf("err: %v", err) 575 } 576 577 resp := fsm.Apply(makeLog(buf)) 578 if resp != nil { 579 t.Fatalf("resp: %v", resp) 580 } 581 582 // Verify we are registered 583 ws := memdb.NewWatchSet() 584 jobOut, err := fsm.State().JobByID(ws, req.Namespace, req.Job.ID) 585 if err != nil { 586 t.Fatalf("err: %v", err) 587 } 588 if jobOut == nil { 589 t.Fatalf("not found!") 590 } 591 if jobOut.CreateIndex != 1 { 592 t.Fatalf("bad index: %d", jobOut.CreateIndex) 593 } 594 595 // Verify it was added to the periodic runner. 596 tuple := structs.NamespacedID{ 597 ID: job.ID, 598 Namespace: job.Namespace, 599 } 600 if _, ok := fsm.periodicDispatcher.tracked[tuple]; !ok { 601 t.Fatal("job not added to periodic runner") 602 } 603 604 // Verify the launch time was tracked. 605 launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Namespace, req.Job.ID) 606 if err != nil { 607 t.Fatalf("err: %v", err) 608 } 609 if launchOut == nil { 610 t.Fatalf("not found!") 611 } 612 if launchOut.Launch.IsZero() { 613 t.Fatalf("bad launch time: %v", launchOut.Launch) 614 } 615 } 616 617 func TestFSM_RegisterPeriodicJob_NonLeader(t *testing.T) { 618 t.Parallel() 619 fsm := testFSM(t) 620 621 // Disable the dispatcher 622 fsm.periodicDispatcher.SetEnabled(false) 623 624 job := mock.PeriodicJob() 625 req := structs.JobRegisterRequest{ 626 Job: job, 627 WriteRequest: structs.WriteRequest{ 628 Namespace: job.Namespace, 629 }, 630 } 631 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 632 if err != nil { 633 t.Fatalf("err: %v", err) 634 } 635 636 resp := fsm.Apply(makeLog(buf)) 637 if resp != nil { 638 t.Fatalf("resp: %v", resp) 639 } 640 641 // Verify we are registered 642 ws := memdb.NewWatchSet() 643 jobOut, err := fsm.State().JobByID(ws, req.Namespace, req.Job.ID) 644 if err != nil { 645 t.Fatalf("err: %v", err) 646 } 647 if jobOut == nil { 648 t.Fatalf("not found!") 649 } 650 if jobOut.CreateIndex != 1 { 651 t.Fatalf("bad index: %d", jobOut.CreateIndex) 652 } 653 654 // Verify it wasn't added to the periodic runner. 655 tuple := structs.NamespacedID{ 656 ID: job.ID, 657 Namespace: job.Namespace, 658 } 659 if _, ok := fsm.periodicDispatcher.tracked[tuple]; ok { 660 t.Fatal("job added to periodic runner") 661 } 662 663 // Verify the launch time was tracked. 664 launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Namespace, req.Job.ID) 665 if err != nil { 666 t.Fatalf("err: %v", err) 667 } 668 if launchOut == nil { 669 t.Fatalf("not found!") 670 } 671 if launchOut.Launch.IsZero() { 672 t.Fatalf("bad launch time: %v", launchOut.Launch) 673 } 674 } 675 676 func TestFSM_RegisterJob_BadNamespace(t *testing.T) { 677 t.Parallel() 678 fsm := testFSM(t) 679 680 job := mock.Job() 681 job.Namespace = "foo" 682 req := structs.JobRegisterRequest{ 683 Job: job, 684 WriteRequest: structs.WriteRequest{ 685 Namespace: job.Namespace, 686 }, 687 } 688 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 689 if err != nil { 690 t.Fatalf("err: %v", err) 691 } 692 693 resp := fsm.Apply(makeLog(buf)) 694 if resp == nil { 695 t.Fatalf("no resp: %v", resp) 696 } 697 err, ok := resp.(error) 698 if !ok { 699 t.Fatalf("resp not of error type: %T %v", resp, resp) 700 } 701 if !strings.Contains(err.Error(), "nonexistent namespace") { 702 t.Fatalf("bad error: %v", err) 703 } 704 705 // Verify we are not registered 706 ws := memdb.NewWatchSet() 707 jobOut, err := fsm.State().JobByID(ws, req.Namespace, req.Job.ID) 708 if err != nil { 709 t.Fatalf("err: %v", err) 710 } 711 if jobOut != nil { 712 t.Fatalf("job found!") 713 } 714 } 715 716 func TestFSM_DeregisterJob_Purge(t *testing.T) { 717 t.Parallel() 718 fsm := testFSM(t) 719 720 job := mock.PeriodicJob() 721 req := structs.JobRegisterRequest{ 722 Job: job, 723 WriteRequest: structs.WriteRequest{ 724 Namespace: job.Namespace, 725 }, 726 } 727 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 728 if err != nil { 729 t.Fatalf("err: %v", err) 730 } 731 732 resp := fsm.Apply(makeLog(buf)) 733 if resp != nil { 734 t.Fatalf("resp: %v", resp) 735 } 736 737 req2 := structs.JobDeregisterRequest{ 738 JobID: job.ID, 739 Purge: true, 740 WriteRequest: structs.WriteRequest{ 741 Namespace: job.Namespace, 742 }, 743 } 744 buf, err = structs.Encode(structs.JobDeregisterRequestType, req2) 745 if err != nil { 746 t.Fatalf("err: %v", err) 747 } 748 749 resp = fsm.Apply(makeLog(buf)) 750 if resp != nil { 751 t.Fatalf("resp: %v", resp) 752 } 753 754 // Verify we are NOT registered 755 ws := memdb.NewWatchSet() 756 jobOut, err := fsm.State().JobByID(ws, req.Namespace, req.Job.ID) 757 if err != nil { 758 t.Fatalf("err: %v", err) 759 } 760 if jobOut != nil { 761 t.Fatalf("job found!") 762 } 763 764 // Verify it was removed from the periodic runner. 765 tuple := structs.NamespacedID{ 766 ID: job.ID, 767 Namespace: job.Namespace, 768 } 769 if _, ok := fsm.periodicDispatcher.tracked[tuple]; ok { 770 t.Fatal("job not removed from periodic runner") 771 } 772 773 // Verify it was removed from the periodic launch table. 774 launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Namespace, req.Job.ID) 775 if err != nil { 776 t.Fatalf("err: %v", err) 777 } 778 if launchOut != nil { 779 t.Fatalf("launch found!") 780 } 781 } 782 783 func TestFSM_DeregisterJob_NoPurge(t *testing.T) { 784 t.Parallel() 785 fsm := testFSM(t) 786 787 job := mock.PeriodicJob() 788 req := structs.JobRegisterRequest{ 789 Job: job, 790 WriteRequest: structs.WriteRequest{ 791 Namespace: job.Namespace, 792 }, 793 } 794 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 795 if err != nil { 796 t.Fatalf("err: %v", err) 797 } 798 799 resp := fsm.Apply(makeLog(buf)) 800 if resp != nil { 801 t.Fatalf("resp: %v", resp) 802 } 803 804 req2 := structs.JobDeregisterRequest{ 805 JobID: job.ID, 806 Purge: false, 807 WriteRequest: structs.WriteRequest{ 808 Namespace: job.Namespace, 809 }, 810 } 811 buf, err = structs.Encode(structs.JobDeregisterRequestType, req2) 812 if err != nil { 813 t.Fatalf("err: %v", err) 814 } 815 816 resp = fsm.Apply(makeLog(buf)) 817 if resp != nil { 818 t.Fatalf("resp: %v", resp) 819 } 820 821 // Verify we are NOT registered 822 ws := memdb.NewWatchSet() 823 jobOut, err := fsm.State().JobByID(ws, req.Namespace, req.Job.ID) 824 if err != nil { 825 t.Fatalf("err: %v", err) 826 } 827 if jobOut == nil { 828 t.Fatalf("job not found!") 829 } 830 if !jobOut.Stop { 831 t.Fatalf("job not stopped found!") 832 } 833 834 // Verify it was removed from the periodic runner. 835 tuple := structs.NamespacedID{ 836 ID: job.ID, 837 Namespace: job.Namespace, 838 } 839 if _, ok := fsm.periodicDispatcher.tracked[tuple]; ok { 840 t.Fatal("job not removed from periodic runner") 841 } 842 843 // Verify it was removed from the periodic launch table. 844 launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Namespace, req.Job.ID) 845 if err != nil { 846 t.Fatalf("err: %v", err) 847 } 848 if launchOut == nil { 849 t.Fatalf("launch not found!") 850 } 851 } 852 853 func TestFSM_BatchDeregisterJob(t *testing.T) { 854 t.Parallel() 855 require := require.New(t) 856 fsm := testFSM(t) 857 858 job := mock.PeriodicJob() 859 req := structs.JobRegisterRequest{ 860 Job: job, 861 WriteRequest: structs.WriteRequest{ 862 Namespace: job.Namespace, 863 }, 864 } 865 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 866 require.Nil(err) 867 resp := fsm.Apply(makeLog(buf)) 868 require.Nil(resp) 869 870 job2 := mock.Job() 871 req2 := structs.JobRegisterRequest{ 872 Job: job2, 873 WriteRequest: structs.WriteRequest{ 874 Namespace: job2.Namespace, 875 }, 876 } 877 878 buf, err = structs.Encode(structs.JobRegisterRequestType, req2) 879 require.Nil(err) 880 resp = fsm.Apply(makeLog(buf)) 881 require.Nil(resp) 882 883 req3 := structs.JobBatchDeregisterRequest{ 884 Jobs: map[structs.NamespacedID]*structs.JobDeregisterOptions{ 885 { 886 ID: job.ID, 887 Namespace: job.Namespace, 888 }: {}, 889 { 890 ID: job2.ID, 891 Namespace: job2.Namespace, 892 }: { 893 Purge: true, 894 }, 895 }, 896 WriteRequest: structs.WriteRequest{ 897 Namespace: job.Namespace, 898 }, 899 } 900 buf, err = structs.Encode(structs.JobBatchDeregisterRequestType, req3) 901 require.Nil(err) 902 903 resp = fsm.Apply(makeLog(buf)) 904 require.Nil(resp) 905 906 // Verify we are NOT registered 907 ws := memdb.NewWatchSet() 908 jobOut, err := fsm.State().JobByID(ws, req.Namespace, req.Job.ID) 909 require.Nil(err) 910 require.NotNil(jobOut) 911 require.True(jobOut.Stop) 912 913 // Verify it was removed from the periodic runner. 914 tuple := structs.NamespacedID{ 915 ID: job.ID, 916 Namespace: job.Namespace, 917 } 918 require.NotContains(fsm.periodicDispatcher.tracked, tuple) 919 920 // Verify it was not removed from the periodic launch table. 921 launchOut, err := fsm.State().PeriodicLaunchByID(ws, job.Namespace, job.ID) 922 require.Nil(err) 923 require.NotNil(launchOut) 924 925 // Verify the other jbo was purged 926 jobOut2, err := fsm.State().JobByID(ws, job2.Namespace, job2.ID) 927 require.Nil(err) 928 require.Nil(jobOut2) 929 } 930 931 func TestFSM_UpdateEval(t *testing.T) { 932 t.Parallel() 933 fsm := testFSM(t) 934 fsm.evalBroker.SetEnabled(true) 935 936 req := structs.EvalUpdateRequest{ 937 Evals: []*structs.Evaluation{mock.Eval()}, 938 } 939 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 940 if err != nil { 941 t.Fatalf("err: %v", err) 942 } 943 944 resp := fsm.Apply(makeLog(buf)) 945 if resp != nil { 946 t.Fatalf("resp: %v", resp) 947 } 948 949 // Verify we are registered 950 ws := memdb.NewWatchSet() 951 eval, err := fsm.State().EvalByID(ws, req.Evals[0].ID) 952 if err != nil { 953 t.Fatalf("err: %v", err) 954 } 955 if eval == nil { 956 t.Fatalf("not found!") 957 } 958 if eval.CreateIndex != 1 { 959 t.Fatalf("bad index: %d", eval.CreateIndex) 960 } 961 962 // Verify enqueued 963 stats := fsm.evalBroker.Stats() 964 if stats.TotalReady != 1 { 965 t.Fatalf("bad: %#v %#v", stats, eval) 966 } 967 } 968 969 func TestFSM_UpdateEval_Blocked(t *testing.T) { 970 t.Parallel() 971 fsm := testFSM(t) 972 fsm.evalBroker.SetEnabled(true) 973 fsm.blockedEvals.SetEnabled(true) 974 975 // Create a blocked eval. 976 eval := mock.Eval() 977 eval.Status = structs.EvalStatusBlocked 978 979 req := structs.EvalUpdateRequest{ 980 Evals: []*structs.Evaluation{eval}, 981 } 982 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 983 if err != nil { 984 t.Fatalf("err: %v", err) 985 } 986 987 resp := fsm.Apply(makeLog(buf)) 988 if resp != nil { 989 t.Fatalf("resp: %v", resp) 990 } 991 992 // Verify we are registered 993 ws := memdb.NewWatchSet() 994 out, err := fsm.State().EvalByID(ws, eval.ID) 995 if err != nil { 996 t.Fatalf("err: %v", err) 997 } 998 if out == nil { 999 t.Fatalf("not found!") 1000 } 1001 if out.CreateIndex != 1 { 1002 t.Fatalf("bad index: %d", out.CreateIndex) 1003 } 1004 1005 // Verify the eval wasn't enqueued 1006 stats := fsm.evalBroker.Stats() 1007 if stats.TotalReady != 0 { 1008 t.Fatalf("bad: %#v %#v", stats, out) 1009 } 1010 1011 // Verify the eval was added to the blocked tracker. 1012 bStats := fsm.blockedEvals.Stats() 1013 if bStats.TotalBlocked != 1 { 1014 t.Fatalf("bad: %#v %#v", bStats, out) 1015 } 1016 } 1017 1018 func TestFSM_UpdateEval_Untrack(t *testing.T) { 1019 t.Parallel() 1020 fsm := testFSM(t) 1021 fsm.evalBroker.SetEnabled(true) 1022 fsm.blockedEvals.SetEnabled(true) 1023 1024 // Mark an eval as blocked. 1025 bEval := mock.Eval() 1026 bEval.ClassEligibility = map[string]bool{"v1:123": true} 1027 fsm.blockedEvals.Block(bEval) 1028 1029 // Create a successful eval for the same job 1030 eval := mock.Eval() 1031 eval.JobID = bEval.JobID 1032 eval.Status = structs.EvalStatusComplete 1033 1034 req := structs.EvalUpdateRequest{ 1035 Evals: []*structs.Evaluation{eval}, 1036 } 1037 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 1038 if err != nil { 1039 t.Fatalf("err: %v", err) 1040 } 1041 1042 resp := fsm.Apply(makeLog(buf)) 1043 if resp != nil { 1044 t.Fatalf("resp: %v", resp) 1045 } 1046 1047 // Verify we are registered 1048 ws := memdb.NewWatchSet() 1049 out, err := fsm.State().EvalByID(ws, eval.ID) 1050 if err != nil { 1051 t.Fatalf("err: %v", err) 1052 } 1053 if out == nil { 1054 t.Fatalf("not found!") 1055 } 1056 if out.CreateIndex != 1 { 1057 t.Fatalf("bad index: %d", out.CreateIndex) 1058 } 1059 1060 // Verify the eval wasn't enqueued 1061 stats := fsm.evalBroker.Stats() 1062 if stats.TotalReady != 0 { 1063 t.Fatalf("bad: %#v %#v", stats, out) 1064 } 1065 1066 // Verify the eval was untracked in the blocked tracker. 1067 bStats := fsm.blockedEvals.Stats() 1068 if bStats.TotalBlocked != 0 { 1069 t.Fatalf("bad: %#v %#v", bStats, out) 1070 } 1071 } 1072 1073 func TestFSM_UpdateEval_NoUntrack(t *testing.T) { 1074 t.Parallel() 1075 fsm := testFSM(t) 1076 fsm.evalBroker.SetEnabled(true) 1077 fsm.blockedEvals.SetEnabled(true) 1078 1079 // Mark an eval as blocked. 1080 bEval := mock.Eval() 1081 bEval.ClassEligibility = map[string]bool{"v1:123": true} 1082 fsm.blockedEvals.Block(bEval) 1083 1084 // Create a successful eval for the same job but with placement failures 1085 eval := mock.Eval() 1086 eval.JobID = bEval.JobID 1087 eval.Status = structs.EvalStatusComplete 1088 eval.FailedTGAllocs = make(map[string]*structs.AllocMetric) 1089 eval.FailedTGAllocs["test"] = new(structs.AllocMetric) 1090 1091 req := structs.EvalUpdateRequest{ 1092 Evals: []*structs.Evaluation{eval}, 1093 } 1094 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 1095 if err != nil { 1096 t.Fatalf("err: %v", err) 1097 } 1098 1099 resp := fsm.Apply(makeLog(buf)) 1100 if resp != nil { 1101 t.Fatalf("resp: %v", resp) 1102 } 1103 1104 // Verify we are registered 1105 ws := memdb.NewWatchSet() 1106 out, err := fsm.State().EvalByID(ws, eval.ID) 1107 if err != nil { 1108 t.Fatalf("err: %v", err) 1109 } 1110 if out == nil { 1111 t.Fatalf("not found!") 1112 } 1113 if out.CreateIndex != 1 { 1114 t.Fatalf("bad index: %d", out.CreateIndex) 1115 } 1116 1117 // Verify the eval wasn't enqueued 1118 stats := fsm.evalBroker.Stats() 1119 if stats.TotalReady != 0 { 1120 t.Fatalf("bad: %#v %#v", stats, out) 1121 } 1122 1123 // Verify the eval was not untracked in the blocked tracker. 1124 bStats := fsm.blockedEvals.Stats() 1125 if bStats.TotalBlocked != 1 { 1126 t.Fatalf("bad: %#v %#v", bStats, out) 1127 } 1128 } 1129 1130 func TestFSM_DeleteEval(t *testing.T) { 1131 t.Parallel() 1132 fsm := testFSM(t) 1133 1134 eval := mock.Eval() 1135 req := structs.EvalUpdateRequest{ 1136 Evals: []*structs.Evaluation{eval}, 1137 } 1138 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 1139 if err != nil { 1140 t.Fatalf("err: %v", err) 1141 } 1142 1143 resp := fsm.Apply(makeLog(buf)) 1144 if resp != nil { 1145 t.Fatalf("resp: %v", resp) 1146 } 1147 1148 req2 := structs.EvalDeleteRequest{ 1149 Evals: []string{eval.ID}, 1150 } 1151 buf, err = structs.Encode(structs.EvalDeleteRequestType, req2) 1152 if err != nil { 1153 t.Fatalf("err: %v", err) 1154 } 1155 1156 resp = fsm.Apply(makeLog(buf)) 1157 if resp != nil { 1158 t.Fatalf("resp: %v", resp) 1159 } 1160 1161 // Verify we are NOT registered 1162 ws := memdb.NewWatchSet() 1163 eval, err = fsm.State().EvalByID(ws, req.Evals[0].ID) 1164 if err != nil { 1165 t.Fatalf("err: %v", err) 1166 } 1167 if eval != nil { 1168 t.Fatalf("eval found!") 1169 } 1170 } 1171 1172 func TestFSM_UpsertAllocs(t *testing.T) { 1173 t.Parallel() 1174 fsm := testFSM(t) 1175 1176 alloc := mock.Alloc() 1177 alloc.Resources = &structs.Resources{} // COMPAT(0.11): Remove in 0.11, used to bypass resource creation in state store 1178 fsm.State().UpsertJobSummary(1, mock.JobSummary(alloc.JobID)) 1179 req := structs.AllocUpdateRequest{ 1180 Alloc: []*structs.Allocation{alloc}, 1181 } 1182 buf, err := structs.Encode(structs.AllocUpdateRequestType, req) 1183 if err != nil { 1184 t.Fatalf("err: %v", err) 1185 } 1186 1187 resp := fsm.Apply(makeLog(buf)) 1188 if resp != nil { 1189 t.Fatalf("resp: %v", resp) 1190 } 1191 1192 // Verify we are registered 1193 ws := memdb.NewWatchSet() 1194 out, err := fsm.State().AllocByID(ws, alloc.ID) 1195 if err != nil { 1196 t.Fatalf("err: %v", err) 1197 } 1198 alloc.CreateIndex = out.CreateIndex 1199 alloc.ModifyIndex = out.ModifyIndex 1200 alloc.AllocModifyIndex = out.AllocModifyIndex 1201 if !reflect.DeepEqual(alloc, out) { 1202 t.Fatalf("bad: %#v %#v", alloc, out) 1203 } 1204 1205 evictAlloc := new(structs.Allocation) 1206 *evictAlloc = *alloc 1207 evictAlloc.DesiredStatus = structs.AllocDesiredStatusEvict 1208 req2 := structs.AllocUpdateRequest{ 1209 Alloc: []*structs.Allocation{evictAlloc}, 1210 } 1211 buf, err = structs.Encode(structs.AllocUpdateRequestType, req2) 1212 if err != nil { 1213 t.Fatalf("err: %v", err) 1214 } 1215 1216 resp = fsm.Apply(makeLog(buf)) 1217 if resp != nil { 1218 t.Fatalf("resp: %v", resp) 1219 } 1220 1221 // Verify we are evicted 1222 out, err = fsm.State().AllocByID(ws, alloc.ID) 1223 if err != nil { 1224 t.Fatalf("err: %v", err) 1225 } 1226 if out.DesiredStatus != structs.AllocDesiredStatusEvict { 1227 t.Fatalf("alloc found!") 1228 } 1229 } 1230 1231 func TestFSM_UpsertAllocs_SharedJob(t *testing.T) { 1232 t.Parallel() 1233 fsm := testFSM(t) 1234 1235 alloc := mock.Alloc() 1236 alloc.Resources = &structs.Resources{} // COMPAT(0.11): Remove in 0.11, used to bypass resource creation in state store 1237 fsm.State().UpsertJobSummary(1, mock.JobSummary(alloc.JobID)) 1238 job := alloc.Job 1239 alloc.Job = nil 1240 req := structs.AllocUpdateRequest{ 1241 Job: job, 1242 Alloc: []*structs.Allocation{alloc}, 1243 } 1244 buf, err := structs.Encode(structs.AllocUpdateRequestType, 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 we are registered 1255 ws := memdb.NewWatchSet() 1256 out, err := fsm.State().AllocByID(ws, alloc.ID) 1257 if err != nil { 1258 t.Fatalf("err: %v", err) 1259 } 1260 alloc.CreateIndex = out.CreateIndex 1261 alloc.ModifyIndex = out.ModifyIndex 1262 alloc.AllocModifyIndex = out.AllocModifyIndex 1263 1264 // Job should be re-attached 1265 alloc.Job = job 1266 require.Equal(t, alloc, out) 1267 1268 // Ensure that the original job is used 1269 evictAlloc := new(structs.Allocation) 1270 *evictAlloc = *alloc 1271 job = mock.Job() 1272 job.Priority = 123 1273 1274 evictAlloc.Job = nil 1275 evictAlloc.DesiredStatus = structs.AllocDesiredStatusEvict 1276 req2 := structs.AllocUpdateRequest{ 1277 Job: job, 1278 Alloc: []*structs.Allocation{evictAlloc}, 1279 } 1280 buf, err = structs.Encode(structs.AllocUpdateRequestType, req2) 1281 if err != nil { 1282 t.Fatalf("err: %v", err) 1283 } 1284 1285 resp = fsm.Apply(makeLog(buf)) 1286 if resp != nil { 1287 t.Fatalf("resp: %v", resp) 1288 } 1289 1290 // Verify we are evicted 1291 out, err = fsm.State().AllocByID(ws, alloc.ID) 1292 if err != nil { 1293 t.Fatalf("err: %v", err) 1294 } 1295 if out.DesiredStatus != structs.AllocDesiredStatusEvict { 1296 t.Fatalf("alloc found!") 1297 } 1298 if out.Job == nil || out.Job.Priority == 123 { 1299 t.Fatalf("bad job") 1300 } 1301 } 1302 1303 // COMPAT(0.11): Remove in 0.11 1304 func TestFSM_UpsertAllocs_StrippedResources(t *testing.T) { 1305 t.Parallel() 1306 fsm := testFSM(t) 1307 1308 alloc := mock.Alloc() 1309 alloc.Resources = &structs.Resources{ 1310 CPU: 500, 1311 MemoryMB: 256, 1312 DiskMB: 150, 1313 Networks: []*structs.NetworkResource{ 1314 { 1315 Device: "eth0", 1316 IP: "192.168.0.100", 1317 ReservedPorts: []structs.Port{{Label: "admin", Value: 5000}}, 1318 MBits: 50, 1319 DynamicPorts: []structs.Port{{Label: "http"}}, 1320 }, 1321 }, 1322 } 1323 alloc.TaskResources = map[string]*structs.Resources{ 1324 "web": { 1325 CPU: 500, 1326 MemoryMB: 256, 1327 Networks: []*structs.NetworkResource{ 1328 { 1329 Device: "eth0", 1330 IP: "192.168.0.100", 1331 ReservedPorts: []structs.Port{{Label: "admin", Value: 5000}}, 1332 MBits: 50, 1333 DynamicPorts: []structs.Port{{Label: "http", Value: 9876}}, 1334 }, 1335 }, 1336 }, 1337 } 1338 alloc.SharedResources = &structs.Resources{ 1339 DiskMB: 150, 1340 } 1341 1342 // Need to remove mock dynamic port from alloc as it won't be computed 1343 // in this test 1344 alloc.TaskResources["web"].Networks[0].DynamicPorts[0].Value = 0 1345 1346 fsm.State().UpsertJobSummary(1, mock.JobSummary(alloc.JobID)) 1347 job := alloc.Job 1348 origResources := alloc.Resources 1349 alloc.Resources = nil 1350 req := structs.AllocUpdateRequest{ 1351 Job: job, 1352 Alloc: []*structs.Allocation{alloc}, 1353 } 1354 buf, err := structs.Encode(structs.AllocUpdateRequestType, req) 1355 if err != nil { 1356 t.Fatalf("err: %v", err) 1357 } 1358 1359 resp := fsm.Apply(makeLog(buf)) 1360 if resp != nil { 1361 t.Fatalf("resp: %v", resp) 1362 } 1363 1364 // Verify we are registered 1365 ws := memdb.NewWatchSet() 1366 out, err := fsm.State().AllocByID(ws, alloc.ID) 1367 if err != nil { 1368 t.Fatalf("err: %v", err) 1369 } 1370 alloc.CreateIndex = out.CreateIndex 1371 alloc.ModifyIndex = out.ModifyIndex 1372 alloc.AllocModifyIndex = out.AllocModifyIndex 1373 1374 // Resources should be recomputed 1375 origResources.DiskMB = alloc.Job.TaskGroups[0].EphemeralDisk.SizeMB 1376 alloc.Resources = origResources 1377 if !reflect.DeepEqual(alloc, out) { 1378 t.Fatalf("not equal: % #v", pretty.Diff(alloc, out)) 1379 } 1380 } 1381 1382 func TestFSM_UpdateAllocFromClient_Unblock(t *testing.T) { 1383 t.Parallel() 1384 fsm := testFSM(t) 1385 fsm.blockedEvals.SetEnabled(true) 1386 state := fsm.State() 1387 1388 node := mock.Node() 1389 state.UpsertNode(1, node) 1390 1391 // Mark an eval as blocked. 1392 eval := mock.Eval() 1393 eval.ClassEligibility = map[string]bool{node.ComputedClass: true} 1394 fsm.blockedEvals.Block(eval) 1395 1396 bStats := fsm.blockedEvals.Stats() 1397 if bStats.TotalBlocked != 1 { 1398 t.Fatalf("bad: %#v", bStats) 1399 } 1400 1401 // Create a completed eval 1402 alloc := mock.Alloc() 1403 alloc.NodeID = node.ID 1404 alloc2 := mock.Alloc() 1405 alloc2.NodeID = node.ID 1406 state.UpsertJobSummary(8, mock.JobSummary(alloc.JobID)) 1407 state.UpsertJobSummary(9, mock.JobSummary(alloc2.JobID)) 1408 state.UpsertAllocs(10, []*structs.Allocation{alloc, alloc2}) 1409 1410 clientAlloc := new(structs.Allocation) 1411 *clientAlloc = *alloc 1412 clientAlloc.ClientStatus = structs.AllocClientStatusComplete 1413 update2 := &structs.Allocation{ 1414 ID: alloc2.ID, 1415 ClientStatus: structs.AllocClientStatusRunning, 1416 } 1417 1418 req := structs.AllocUpdateRequest{ 1419 Alloc: []*structs.Allocation{clientAlloc, update2}, 1420 } 1421 buf, err := structs.Encode(structs.AllocClientUpdateRequestType, req) 1422 if err != nil { 1423 t.Fatalf("err: %v", err) 1424 } 1425 1426 resp := fsm.Apply(makeLog(buf)) 1427 if resp != nil { 1428 t.Fatalf("resp: %v", resp) 1429 } 1430 1431 // Verify we are updated 1432 ws := memdb.NewWatchSet() 1433 out, err := fsm.State().AllocByID(ws, alloc.ID) 1434 if err != nil { 1435 t.Fatalf("err: %v", err) 1436 } 1437 clientAlloc.CreateIndex = out.CreateIndex 1438 clientAlloc.ModifyIndex = out.ModifyIndex 1439 if !reflect.DeepEqual(clientAlloc, out) { 1440 t.Fatalf("bad: %#v %#v", clientAlloc, out) 1441 } 1442 1443 out, err = fsm.State().AllocByID(ws, alloc2.ID) 1444 if err != nil { 1445 t.Fatalf("err: %v", err) 1446 } 1447 alloc2.CreateIndex = out.CreateIndex 1448 alloc2.ModifyIndex = out.ModifyIndex 1449 alloc2.ClientStatus = structs.AllocClientStatusRunning 1450 alloc2.TaskStates = nil 1451 if !reflect.DeepEqual(alloc2, out) { 1452 t.Fatalf("bad: %#v %#v", alloc2, out) 1453 } 1454 1455 // Verify the eval was unblocked. 1456 testutil.WaitForResult(func() (bool, error) { 1457 bStats = fsm.blockedEvals.Stats() 1458 if bStats.TotalBlocked != 0 { 1459 return false, fmt.Errorf("bad: %#v %#v", bStats, out) 1460 } 1461 return true, nil 1462 }, func(err error) { 1463 t.Fatalf("err: %s", err) 1464 }) 1465 } 1466 1467 func TestFSM_UpdateAllocFromClient(t *testing.T) { 1468 t.Parallel() 1469 fsm := testFSM(t) 1470 state := fsm.State() 1471 require := require.New(t) 1472 1473 alloc := mock.Alloc() 1474 state.UpsertJobSummary(9, mock.JobSummary(alloc.JobID)) 1475 state.UpsertAllocs(10, []*structs.Allocation{alloc}) 1476 1477 clientAlloc := new(structs.Allocation) 1478 *clientAlloc = *alloc 1479 clientAlloc.ClientStatus = structs.AllocClientStatusFailed 1480 1481 eval := mock.Eval() 1482 eval.JobID = alloc.JobID 1483 eval.TriggeredBy = structs.EvalTriggerRetryFailedAlloc 1484 eval.Type = alloc.Job.Type 1485 1486 req := structs.AllocUpdateRequest{ 1487 Alloc: []*structs.Allocation{clientAlloc}, 1488 Evals: []*structs.Evaluation{eval}, 1489 } 1490 buf, err := structs.Encode(structs.AllocClientUpdateRequestType, req) 1491 require.Nil(err) 1492 1493 resp := fsm.Apply(makeLog(buf)) 1494 require.Nil(resp) 1495 1496 // Verify we are registered 1497 ws := memdb.NewWatchSet() 1498 out, err := fsm.State().AllocByID(ws, alloc.ID) 1499 require.Nil(err) 1500 clientAlloc.CreateIndex = out.CreateIndex 1501 clientAlloc.ModifyIndex = out.ModifyIndex 1502 require.Equal(clientAlloc, out) 1503 1504 // Verify eval was inserted 1505 ws = memdb.NewWatchSet() 1506 evals, err := fsm.State().EvalsByJob(ws, eval.Namespace, eval.JobID) 1507 require.Nil(err) 1508 require.Equal(1, len(evals)) 1509 res := evals[0] 1510 eval.CreateIndex = res.CreateIndex 1511 eval.ModifyIndex = res.ModifyIndex 1512 require.Equal(eval, res) 1513 } 1514 1515 func TestFSM_UpdateAllocDesiredTransition(t *testing.T) { 1516 t.Parallel() 1517 fsm := testFSM(t) 1518 state := fsm.State() 1519 require := require.New(t) 1520 1521 alloc := mock.Alloc() 1522 alloc2 := mock.Alloc() 1523 alloc2.Job = alloc.Job 1524 alloc2.JobID = alloc.JobID 1525 state.UpsertJobSummary(9, mock.JobSummary(alloc.JobID)) 1526 state.UpsertAllocs(10, []*structs.Allocation{alloc, alloc2}) 1527 1528 t1 := &structs.DesiredTransition{ 1529 Migrate: helper.BoolToPtr(true), 1530 } 1531 1532 eval := &structs.Evaluation{ 1533 ID: uuid.Generate(), 1534 Namespace: alloc.Namespace, 1535 Priority: alloc.Job.Priority, 1536 Type: alloc.Job.Type, 1537 TriggeredBy: structs.EvalTriggerNodeDrain, 1538 JobID: alloc.Job.ID, 1539 JobModifyIndex: alloc.Job.ModifyIndex, 1540 Status: structs.EvalStatusPending, 1541 } 1542 req := structs.AllocUpdateDesiredTransitionRequest{ 1543 Allocs: map[string]*structs.DesiredTransition{ 1544 alloc.ID: t1, 1545 alloc2.ID: t1, 1546 }, 1547 Evals: []*structs.Evaluation{eval}, 1548 } 1549 buf, err := structs.Encode(structs.AllocUpdateDesiredTransitionRequestType, req) 1550 require.Nil(err) 1551 1552 resp := fsm.Apply(makeLog(buf)) 1553 require.Nil(resp) 1554 1555 // Verify we are registered 1556 ws := memdb.NewWatchSet() 1557 out1, err := fsm.State().AllocByID(ws, alloc.ID) 1558 require.Nil(err) 1559 out2, err := fsm.State().AllocByID(ws, alloc2.ID) 1560 require.Nil(err) 1561 evalOut, err := fsm.State().EvalByID(ws, eval.ID) 1562 require.Nil(err) 1563 require.NotNil(evalOut) 1564 require.Equal(eval.ID, evalOut.ID) 1565 1566 require.NotNil(out1.DesiredTransition.Migrate) 1567 require.NotNil(out2.DesiredTransition.Migrate) 1568 require.True(*out1.DesiredTransition.Migrate) 1569 require.True(*out2.DesiredTransition.Migrate) 1570 } 1571 1572 func TestFSM_UpsertVaultAccessor(t *testing.T) { 1573 t.Parallel() 1574 fsm := testFSM(t) 1575 fsm.blockedEvals.SetEnabled(true) 1576 1577 va := mock.VaultAccessor() 1578 va2 := mock.VaultAccessor() 1579 req := structs.VaultAccessorsRequest{ 1580 Accessors: []*structs.VaultAccessor{va, va2}, 1581 } 1582 buf, err := structs.Encode(structs.VaultAccessorRegisterRequestType, req) 1583 if err != nil { 1584 t.Fatalf("err: %v", err) 1585 } 1586 1587 resp := fsm.Apply(makeLog(buf)) 1588 if resp != nil { 1589 t.Fatalf("resp: %v", resp) 1590 } 1591 1592 // Verify we are registered 1593 ws := memdb.NewWatchSet() 1594 out1, err := fsm.State().VaultAccessor(ws, va.Accessor) 1595 if err != nil { 1596 t.Fatalf("err: %v", err) 1597 } 1598 if out1 == nil { 1599 t.Fatalf("not found!") 1600 } 1601 if out1.CreateIndex != 1 { 1602 t.Fatalf("bad index: %d", out1.CreateIndex) 1603 } 1604 out2, err := fsm.State().VaultAccessor(ws, va2.Accessor) 1605 if err != nil { 1606 t.Fatalf("err: %v", err) 1607 } 1608 if out2 == nil { 1609 t.Fatalf("not found!") 1610 } 1611 if out1.CreateIndex != 1 { 1612 t.Fatalf("bad index: %d", out2.CreateIndex) 1613 } 1614 1615 tt := fsm.TimeTable() 1616 index := tt.NearestIndex(time.Now().UTC()) 1617 if index != 1 { 1618 t.Fatalf("bad: %d", index) 1619 } 1620 } 1621 1622 func TestFSM_DeregisterVaultAccessor(t *testing.T) { 1623 t.Parallel() 1624 fsm := testFSM(t) 1625 fsm.blockedEvals.SetEnabled(true) 1626 1627 va := mock.VaultAccessor() 1628 va2 := mock.VaultAccessor() 1629 accessors := []*structs.VaultAccessor{va, va2} 1630 1631 // Insert the accessors 1632 if err := fsm.State().UpsertVaultAccessor(1000, accessors); err != nil { 1633 t.Fatalf("bad: %v", err) 1634 } 1635 1636 req := structs.VaultAccessorsRequest{ 1637 Accessors: accessors, 1638 } 1639 buf, err := structs.Encode(structs.VaultAccessorDeregisterRequestType, req) 1640 if err != nil { 1641 t.Fatalf("err: %v", err) 1642 } 1643 1644 resp := fsm.Apply(makeLog(buf)) 1645 if resp != nil { 1646 t.Fatalf("resp: %v", resp) 1647 } 1648 1649 ws := memdb.NewWatchSet() 1650 out1, err := fsm.State().VaultAccessor(ws, va.Accessor) 1651 if err != nil { 1652 t.Fatalf("err: %v", err) 1653 } 1654 if out1 != nil { 1655 t.Fatalf("not deleted!") 1656 } 1657 1658 tt := fsm.TimeTable() 1659 index := tt.NearestIndex(time.Now().UTC()) 1660 if index != 1 { 1661 t.Fatalf("bad: %d", index) 1662 } 1663 } 1664 1665 func TestFSM_ApplyPlanResults(t *testing.T) { 1666 t.Parallel() 1667 fsm := testFSM(t) 1668 fsm.evalBroker.SetEnabled(true) 1669 // Create the request and create a deployment 1670 alloc := mock.Alloc() 1671 alloc.Resources = &structs.Resources{} // COMPAT(0.11): Remove in 0.11, used to bypass resource creation in state store 1672 job := alloc.Job 1673 alloc.Job = nil 1674 1675 d := mock.Deployment() 1676 d.JobID = job.ID 1677 d.JobModifyIndex = job.ModifyIndex 1678 d.JobVersion = job.Version 1679 1680 alloc.DeploymentID = d.ID 1681 1682 eval := mock.Eval() 1683 eval.JobID = job.ID 1684 fsm.State().UpsertEvals(1, []*structs.Evaluation{eval}) 1685 1686 fsm.State().UpsertJobSummary(1, mock.JobSummary(alloc.JobID)) 1687 1688 // set up preempted jobs and allocs 1689 job1 := mock.Job() 1690 job2 := mock.Job() 1691 1692 alloc1 := mock.Alloc() 1693 alloc1.Job = job1 1694 alloc1.JobID = job1.ID 1695 alloc1.PreemptedByAllocation = alloc.ID 1696 1697 alloc2 := mock.Alloc() 1698 alloc2.Job = job2 1699 alloc2.JobID = job2.ID 1700 alloc2.PreemptedByAllocation = alloc.ID 1701 1702 fsm.State().UpsertAllocs(1, []*structs.Allocation{alloc1, alloc2}) 1703 1704 // evals for preempted jobs 1705 eval1 := mock.Eval() 1706 eval1.JobID = job1.ID 1707 1708 eval2 := mock.Eval() 1709 eval2.JobID = job2.ID 1710 1711 req := structs.ApplyPlanResultsRequest{ 1712 AllocUpdateRequest: structs.AllocUpdateRequest{ 1713 Job: job, 1714 Alloc: []*structs.Allocation{alloc}, 1715 }, 1716 Deployment: d, 1717 EvalID: eval.ID, 1718 NodePreemptions: []*structs.Allocation{alloc1, alloc2}, 1719 PreemptionEvals: []*structs.Evaluation{eval1, eval2}, 1720 } 1721 buf, err := structs.Encode(structs.ApplyPlanResultsRequestType, req) 1722 if err != nil { 1723 t.Fatalf("err: %v", err) 1724 } 1725 1726 resp := fsm.Apply(makeLog(buf)) 1727 if resp != nil { 1728 t.Fatalf("resp: %v", resp) 1729 } 1730 1731 // Verify the allocation is registered 1732 ws := memdb.NewWatchSet() 1733 assert := assert.New(t) 1734 out, err := fsm.State().AllocByID(ws, alloc.ID) 1735 assert.Nil(err) 1736 alloc.CreateIndex = out.CreateIndex 1737 alloc.ModifyIndex = out.ModifyIndex 1738 alloc.AllocModifyIndex = out.AllocModifyIndex 1739 1740 // Job should be re-attached 1741 alloc.Job = job 1742 assert.Equal(alloc, out) 1743 1744 // Verify that evals for preempted jobs have been created 1745 e1, err := fsm.State().EvalByID(ws, eval1.ID) 1746 require := require.New(t) 1747 require.Nil(err) 1748 require.NotNil(e1) 1749 1750 e2, err := fsm.State().EvalByID(ws, eval2.ID) 1751 require.Nil(err) 1752 require.NotNil(e2) 1753 1754 // Verify that eval broker has both evals 1755 _, ok := fsm.evalBroker.evals[e1.ID] 1756 require.True(ok) 1757 1758 _, ok = fsm.evalBroker.evals[e1.ID] 1759 require.True(ok) 1760 1761 dout, err := fsm.State().DeploymentByID(ws, d.ID) 1762 assert.Nil(err) 1763 tg, ok := dout.TaskGroups[alloc.TaskGroup] 1764 assert.True(ok) 1765 assert.NotNil(tg) 1766 assert.Equal(1, tg.PlacedAllocs) 1767 1768 // Ensure that the original job is used 1769 evictAlloc := alloc.Copy() 1770 job = mock.Job() 1771 job.Priority = 123 1772 eval = mock.Eval() 1773 eval.JobID = job.ID 1774 1775 fsm.State().UpsertEvals(2, []*structs.Evaluation{eval}) 1776 1777 evictAlloc.Job = nil 1778 evictAlloc.DesiredStatus = structs.AllocDesiredStatusEvict 1779 req2 := structs.ApplyPlanResultsRequest{ 1780 AllocUpdateRequest: structs.AllocUpdateRequest{ 1781 Job: job, 1782 Alloc: []*structs.Allocation{evictAlloc}, 1783 }, 1784 EvalID: eval.ID, 1785 } 1786 buf, err = structs.Encode(structs.ApplyPlanResultsRequestType, req2) 1787 assert.Nil(err) 1788 1789 log := makeLog(buf) 1790 //set the index to something other than 1 1791 log.Index = 25 1792 resp = fsm.Apply(log) 1793 assert.Nil(resp) 1794 1795 // Verify we are evicted 1796 out, err = fsm.State().AllocByID(ws, alloc.ID) 1797 assert.Nil(err) 1798 assert.Equal(structs.AllocDesiredStatusEvict, out.DesiredStatus) 1799 assert.NotNil(out.Job) 1800 assert.NotEqual(123, out.Job.Priority) 1801 1802 evalOut, err := fsm.State().EvalByID(ws, eval.ID) 1803 assert.Nil(err) 1804 assert.Equal(log.Index, evalOut.ModifyIndex) 1805 1806 } 1807 1808 func TestFSM_DeploymentStatusUpdate(t *testing.T) { 1809 t.Parallel() 1810 fsm := testFSM(t) 1811 fsm.evalBroker.SetEnabled(true) 1812 state := fsm.State() 1813 1814 // Upsert a deployment 1815 d := mock.Deployment() 1816 if err := state.UpsertDeployment(1, d); err != nil { 1817 t.Fatalf("bad: %v", err) 1818 } 1819 1820 // Create a request to update the deployment, create an eval and job 1821 e := mock.Eval() 1822 j := mock.Job() 1823 status, desc := structs.DeploymentStatusFailed, "foo" 1824 req := &structs.DeploymentStatusUpdateRequest{ 1825 DeploymentUpdate: &structs.DeploymentStatusUpdate{ 1826 DeploymentID: d.ID, 1827 Status: status, 1828 StatusDescription: desc, 1829 }, 1830 Job: j, 1831 Eval: e, 1832 } 1833 buf, err := structs.Encode(structs.DeploymentStatusUpdateRequestType, req) 1834 if err != nil { 1835 t.Fatalf("err: %v", err) 1836 } 1837 resp := fsm.Apply(makeLog(buf)) 1838 if resp != nil { 1839 t.Fatalf("resp: %v", resp) 1840 } 1841 1842 // Check that the status was updated properly 1843 ws := memdb.NewWatchSet() 1844 dout, err := state.DeploymentByID(ws, d.ID) 1845 if err != nil { 1846 t.Fatalf("bad: %v", err) 1847 } 1848 if dout.Status != status || dout.StatusDescription != desc { 1849 t.Fatalf("bad: %#v", dout) 1850 } 1851 1852 // Check that the evaluation was created 1853 eout, _ := state.EvalByID(ws, e.ID) 1854 if err != nil { 1855 t.Fatalf("bad: %v", err) 1856 } 1857 if eout == nil { 1858 t.Fatalf("bad: %#v", eout) 1859 } 1860 1861 // Check that the job was created 1862 jout, _ := state.JobByID(ws, j.Namespace, j.ID) 1863 if err != nil { 1864 t.Fatalf("bad: %v", err) 1865 } 1866 if jout == nil { 1867 t.Fatalf("bad: %#v", jout) 1868 } 1869 1870 // Assert the eval was enqueued 1871 stats := fsm.evalBroker.Stats() 1872 if stats.TotalReady != 1 { 1873 t.Fatalf("bad: %#v %#v", stats, e) 1874 } 1875 } 1876 1877 func TestFSM_JobStabilityUpdate(t *testing.T) { 1878 t.Parallel() 1879 fsm := testFSM(t) 1880 fsm.evalBroker.SetEnabled(true) 1881 state := fsm.State() 1882 1883 // Upsert a deployment 1884 job := mock.Job() 1885 if err := state.UpsertJob(1, job); err != nil { 1886 t.Fatalf("bad: %v", err) 1887 } 1888 1889 // Create a request to update the job to stable 1890 req := &structs.JobStabilityRequest{ 1891 JobID: job.ID, 1892 JobVersion: job.Version, 1893 Stable: true, 1894 WriteRequest: structs.WriteRequest{ 1895 Namespace: job.Namespace, 1896 }, 1897 } 1898 buf, err := structs.Encode(structs.JobStabilityRequestType, req) 1899 if err != nil { 1900 t.Fatalf("err: %v", err) 1901 } 1902 resp := fsm.Apply(makeLog(buf)) 1903 if resp != nil { 1904 t.Fatalf("resp: %v", resp) 1905 } 1906 1907 // Check that the stability was updated properly 1908 ws := memdb.NewWatchSet() 1909 jout, _ := state.JobByIDAndVersion(ws, job.Namespace, job.ID, job.Version) 1910 if err != nil { 1911 t.Fatalf("bad: %v", err) 1912 } 1913 if jout == nil || !jout.Stable { 1914 t.Fatalf("bad: %#v", jout) 1915 } 1916 } 1917 1918 func TestFSM_DeploymentPromotion(t *testing.T) { 1919 t.Parallel() 1920 fsm := testFSM(t) 1921 fsm.evalBroker.SetEnabled(true) 1922 state := fsm.State() 1923 1924 // Create a job with two task groups 1925 j := mock.Job() 1926 tg1 := j.TaskGroups[0] 1927 tg2 := tg1.Copy() 1928 tg2.Name = "foo" 1929 j.TaskGroups = append(j.TaskGroups, tg2) 1930 if err := state.UpsertJob(1, j); err != nil { 1931 t.Fatalf("bad: %v", err) 1932 } 1933 1934 // Create a deployment 1935 d := mock.Deployment() 1936 d.JobID = j.ID 1937 d.TaskGroups = map[string]*structs.DeploymentState{ 1938 "web": { 1939 DesiredTotal: 10, 1940 DesiredCanaries: 1, 1941 }, 1942 "foo": { 1943 DesiredTotal: 10, 1944 DesiredCanaries: 1, 1945 }, 1946 } 1947 if err := state.UpsertDeployment(2, d); err != nil { 1948 t.Fatalf("bad: %v", err) 1949 } 1950 1951 // Create a set of allocations 1952 c1 := mock.Alloc() 1953 c1.JobID = j.ID 1954 c1.DeploymentID = d.ID 1955 d.TaskGroups[c1.TaskGroup].PlacedCanaries = append(d.TaskGroups[c1.TaskGroup].PlacedCanaries, c1.ID) 1956 c1.DeploymentStatus = &structs.AllocDeploymentStatus{ 1957 Healthy: helper.BoolToPtr(true), 1958 } 1959 c2 := mock.Alloc() 1960 c2.JobID = j.ID 1961 c2.DeploymentID = d.ID 1962 d.TaskGroups[c2.TaskGroup].PlacedCanaries = append(d.TaskGroups[c2.TaskGroup].PlacedCanaries, c2.ID) 1963 c2.TaskGroup = tg2.Name 1964 c2.DeploymentStatus = &structs.AllocDeploymentStatus{ 1965 Healthy: helper.BoolToPtr(true), 1966 } 1967 1968 if err := state.UpsertAllocs(3, []*structs.Allocation{c1, c2}); err != nil { 1969 t.Fatalf("err: %v", err) 1970 } 1971 1972 // Create an eval 1973 e := mock.Eval() 1974 1975 // Promote the canaries 1976 req := &structs.ApplyDeploymentPromoteRequest{ 1977 DeploymentPromoteRequest: structs.DeploymentPromoteRequest{ 1978 DeploymentID: d.ID, 1979 All: true, 1980 }, 1981 Eval: e, 1982 } 1983 buf, err := structs.Encode(structs.DeploymentPromoteRequestType, req) 1984 if err != nil { 1985 t.Fatalf("err: %v", err) 1986 } 1987 resp := fsm.Apply(makeLog(buf)) 1988 if resp != nil { 1989 t.Fatalf("resp: %v", resp) 1990 } 1991 1992 // Check that the status per task group was updated properly 1993 ws := memdb.NewWatchSet() 1994 dout, err := state.DeploymentByID(ws, d.ID) 1995 if err != nil { 1996 t.Fatalf("bad: %v", err) 1997 } 1998 if len(dout.TaskGroups) != 2 { 1999 t.Fatalf("bad: %#v", dout.TaskGroups) 2000 } 2001 for tg, state := range dout.TaskGroups { 2002 if !state.Promoted { 2003 t.Fatalf("bad: group %q not promoted %#v", tg, state) 2004 } 2005 } 2006 2007 // Check that the evaluation was created 2008 eout, _ := state.EvalByID(ws, e.ID) 2009 if err != nil { 2010 t.Fatalf("bad: %v", err) 2011 } 2012 if eout == nil { 2013 t.Fatalf("bad: %#v", eout) 2014 } 2015 2016 // Assert the eval was enqueued 2017 stats := fsm.evalBroker.Stats() 2018 if stats.TotalReady != 1 { 2019 t.Fatalf("bad: %#v %#v", stats, e) 2020 } 2021 } 2022 2023 func TestFSM_DeploymentAllocHealth(t *testing.T) { 2024 t.Parallel() 2025 fsm := testFSM(t) 2026 fsm.evalBroker.SetEnabled(true) 2027 state := fsm.State() 2028 2029 // Insert a deployment 2030 d := mock.Deployment() 2031 if err := state.UpsertDeployment(1, d); err != nil { 2032 t.Fatalf("bad: %v", err) 2033 } 2034 2035 // Insert two allocations 2036 a1 := mock.Alloc() 2037 a1.DeploymentID = d.ID 2038 a2 := mock.Alloc() 2039 a2.DeploymentID = d.ID 2040 if err := state.UpsertAllocs(2, []*structs.Allocation{a1, a2}); err != nil { 2041 t.Fatalf("bad: %v", err) 2042 } 2043 2044 // Create a job to roll back to 2045 j := mock.Job() 2046 2047 // Create an eval that should be upserted 2048 e := mock.Eval() 2049 2050 // Create a status update for the deployment 2051 status, desc := structs.DeploymentStatusFailed, "foo" 2052 u := &structs.DeploymentStatusUpdate{ 2053 DeploymentID: d.ID, 2054 Status: status, 2055 StatusDescription: desc, 2056 } 2057 2058 // Set health against the deployment 2059 req := &structs.ApplyDeploymentAllocHealthRequest{ 2060 DeploymentAllocHealthRequest: structs.DeploymentAllocHealthRequest{ 2061 DeploymentID: d.ID, 2062 HealthyAllocationIDs: []string{a1.ID}, 2063 UnhealthyAllocationIDs: []string{a2.ID}, 2064 }, 2065 Job: j, 2066 Eval: e, 2067 DeploymentUpdate: u, 2068 } 2069 buf, err := structs.Encode(structs.DeploymentAllocHealthRequestType, req) 2070 if err != nil { 2071 t.Fatalf("err: %v", err) 2072 } 2073 resp := fsm.Apply(makeLog(buf)) 2074 if resp != nil { 2075 t.Fatalf("resp: %v", resp) 2076 } 2077 2078 // Check that the status was updated properly 2079 ws := memdb.NewWatchSet() 2080 dout, err := state.DeploymentByID(ws, d.ID) 2081 if err != nil { 2082 t.Fatalf("bad: %v", err) 2083 } 2084 if dout.Status != status || dout.StatusDescription != desc { 2085 t.Fatalf("bad: %#v", dout) 2086 } 2087 2088 // Check that the evaluation was created 2089 eout, _ := state.EvalByID(ws, e.ID) 2090 if err != nil { 2091 t.Fatalf("bad: %v", err) 2092 } 2093 if eout == nil { 2094 t.Fatalf("bad: %#v", eout) 2095 } 2096 2097 // Check that the job was created 2098 jout, _ := state.JobByID(ws, j.Namespace, j.ID) 2099 if err != nil { 2100 t.Fatalf("bad: %v", err) 2101 } 2102 if jout == nil { 2103 t.Fatalf("bad: %#v", jout) 2104 } 2105 2106 // Check the status of the allocs 2107 out1, err := state.AllocByID(ws, a1.ID) 2108 if err != nil { 2109 t.Fatalf("err: %v", err) 2110 } 2111 out2, err := state.AllocByID(ws, a2.ID) 2112 if err != nil { 2113 t.Fatalf("err: %v", err) 2114 } 2115 2116 if !out1.DeploymentStatus.IsHealthy() { 2117 t.Fatalf("bad: alloc %q not healthy", out1.ID) 2118 } 2119 if !out2.DeploymentStatus.IsUnhealthy() { 2120 t.Fatalf("bad: alloc %q not unhealthy", out2.ID) 2121 } 2122 2123 // Assert the eval was enqueued 2124 stats := fsm.evalBroker.Stats() 2125 if stats.TotalReady != 1 { 2126 t.Fatalf("bad: %#v %#v", stats, e) 2127 } 2128 } 2129 2130 func TestFSM_DeleteDeployment(t *testing.T) { 2131 t.Parallel() 2132 fsm := testFSM(t) 2133 state := fsm.State() 2134 2135 // Upsert a deployments 2136 d := mock.Deployment() 2137 if err := state.UpsertDeployment(1, d); err != nil { 2138 t.Fatalf("bad: %v", err) 2139 } 2140 2141 req := structs.DeploymentDeleteRequest{ 2142 Deployments: []string{d.ID}, 2143 } 2144 buf, err := structs.Encode(structs.DeploymentDeleteRequestType, req) 2145 if err != nil { 2146 t.Fatalf("err: %v", err) 2147 } 2148 2149 resp := fsm.Apply(makeLog(buf)) 2150 if resp != nil { 2151 t.Fatalf("resp: %v", resp) 2152 } 2153 2154 // Verify we are NOT registered 2155 ws := memdb.NewWatchSet() 2156 deployment, err := state.DeploymentByID(ws, d.ID) 2157 if err != nil { 2158 t.Fatalf("err: %v", err) 2159 } 2160 if deployment != nil { 2161 t.Fatalf("deployment found!") 2162 } 2163 } 2164 2165 func TestFSM_UpsertACLPolicies(t *testing.T) { 2166 t.Parallel() 2167 fsm := testFSM(t) 2168 2169 policy := mock.ACLPolicy() 2170 req := structs.ACLPolicyUpsertRequest{ 2171 Policies: []*structs.ACLPolicy{policy}, 2172 } 2173 buf, err := structs.Encode(structs.ACLPolicyUpsertRequestType, req) 2174 if err != nil { 2175 t.Fatalf("err: %v", err) 2176 } 2177 2178 resp := fsm.Apply(makeLog(buf)) 2179 if resp != nil { 2180 t.Fatalf("resp: %v", resp) 2181 } 2182 2183 // Verify we are registered 2184 ws := memdb.NewWatchSet() 2185 out, err := fsm.State().ACLPolicyByName(ws, policy.Name) 2186 assert.Nil(t, err) 2187 assert.NotNil(t, out) 2188 } 2189 2190 func TestFSM_DeleteACLPolicies(t *testing.T) { 2191 t.Parallel() 2192 fsm := testFSM(t) 2193 2194 policy := mock.ACLPolicy() 2195 err := fsm.State().UpsertACLPolicies(1000, []*structs.ACLPolicy{policy}) 2196 assert.Nil(t, err) 2197 2198 req := structs.ACLPolicyDeleteRequest{ 2199 Names: []string{policy.Name}, 2200 } 2201 buf, err := structs.Encode(structs.ACLPolicyDeleteRequestType, req) 2202 if err != nil { 2203 t.Fatalf("err: %v", err) 2204 } 2205 2206 resp := fsm.Apply(makeLog(buf)) 2207 if resp != nil { 2208 t.Fatalf("resp: %v", resp) 2209 } 2210 2211 // Verify we are NOT registered 2212 ws := memdb.NewWatchSet() 2213 out, err := fsm.State().ACLPolicyByName(ws, policy.Name) 2214 assert.Nil(t, err) 2215 assert.Nil(t, out) 2216 } 2217 2218 func TestFSM_BootstrapACLTokens(t *testing.T) { 2219 t.Parallel() 2220 fsm := testFSM(t) 2221 2222 token := mock.ACLToken() 2223 req := structs.ACLTokenBootstrapRequest{ 2224 Token: token, 2225 } 2226 buf, err := structs.Encode(structs.ACLTokenBootstrapRequestType, req) 2227 if err != nil { 2228 t.Fatalf("err: %v", err) 2229 } 2230 2231 resp := fsm.Apply(makeLog(buf)) 2232 if resp != nil { 2233 t.Fatalf("resp: %v", resp) 2234 } 2235 2236 // Verify we are registered 2237 out, err := fsm.State().ACLTokenByAccessorID(nil, token.AccessorID) 2238 assert.Nil(t, err) 2239 assert.NotNil(t, out) 2240 2241 // Test with reset 2242 token2 := mock.ACLToken() 2243 req = structs.ACLTokenBootstrapRequest{ 2244 Token: token2, 2245 ResetIndex: out.CreateIndex, 2246 } 2247 buf, err = structs.Encode(structs.ACLTokenBootstrapRequestType, req) 2248 if err != nil { 2249 t.Fatalf("err: %v", err) 2250 } 2251 2252 resp = fsm.Apply(makeLog(buf)) 2253 if resp != nil { 2254 t.Fatalf("resp: %v", resp) 2255 } 2256 2257 // Verify we are registered 2258 out2, err := fsm.State().ACLTokenByAccessorID(nil, token2.AccessorID) 2259 assert.Nil(t, err) 2260 assert.NotNil(t, out2) 2261 } 2262 2263 func TestFSM_UpsertACLTokens(t *testing.T) { 2264 t.Parallel() 2265 fsm := testFSM(t) 2266 2267 token := mock.ACLToken() 2268 req := structs.ACLTokenUpsertRequest{ 2269 Tokens: []*structs.ACLToken{token}, 2270 } 2271 buf, err := structs.Encode(structs.ACLTokenUpsertRequestType, req) 2272 if err != nil { 2273 t.Fatalf("err: %v", err) 2274 } 2275 2276 resp := fsm.Apply(makeLog(buf)) 2277 if resp != nil { 2278 t.Fatalf("resp: %v", resp) 2279 } 2280 2281 // Verify we are registered 2282 ws := memdb.NewWatchSet() 2283 out, err := fsm.State().ACLTokenByAccessorID(ws, token.AccessorID) 2284 assert.Nil(t, err) 2285 assert.NotNil(t, out) 2286 } 2287 2288 func TestFSM_DeleteACLTokens(t *testing.T) { 2289 t.Parallel() 2290 fsm := testFSM(t) 2291 2292 token := mock.ACLToken() 2293 err := fsm.State().UpsertACLTokens(1000, []*structs.ACLToken{token}) 2294 assert.Nil(t, err) 2295 2296 req := structs.ACLTokenDeleteRequest{ 2297 AccessorIDs: []string{token.AccessorID}, 2298 } 2299 buf, err := structs.Encode(structs.ACLTokenDeleteRequestType, req) 2300 if err != nil { 2301 t.Fatalf("err: %v", err) 2302 } 2303 2304 resp := fsm.Apply(makeLog(buf)) 2305 if resp != nil { 2306 t.Fatalf("resp: %v", resp) 2307 } 2308 2309 // Verify we are NOT registered 2310 ws := memdb.NewWatchSet() 2311 out, err := fsm.State().ACLTokenByAccessorID(ws, token.AccessorID) 2312 assert.Nil(t, err) 2313 assert.Nil(t, out) 2314 } 2315 2316 func testSnapshotRestore(t *testing.T, fsm *nomadFSM) *nomadFSM { 2317 // Snapshot 2318 snap, err := fsm.Snapshot() 2319 if err != nil { 2320 t.Fatalf("err: %v", err) 2321 } 2322 defer snap.Release() 2323 2324 // Persist 2325 buf := bytes.NewBuffer(nil) 2326 sink := &MockSink{buf, false} 2327 if err := snap.Persist(sink); err != nil { 2328 t.Fatalf("err: %v", err) 2329 } 2330 2331 // Try to restore on a new FSM 2332 fsm2 := testFSM(t) 2333 snap, err = fsm2.Snapshot() 2334 if err != nil { 2335 t.Fatalf("err: %v", err) 2336 } 2337 defer snap.Release() 2338 2339 abandonCh := fsm2.State().AbandonCh() 2340 2341 // Do a restore 2342 if err := fsm2.Restore(sink); err != nil { 2343 t.Fatalf("err: %v", err) 2344 } 2345 2346 select { 2347 case <-abandonCh: 2348 default: 2349 t.Fatalf("bad") 2350 } 2351 2352 return fsm2 2353 } 2354 2355 func TestFSM_SnapshotRestore_Nodes(t *testing.T) { 2356 t.Parallel() 2357 // Add some state 2358 fsm := testFSM(t) 2359 state := fsm.State() 2360 node1 := mock.Node() 2361 state.UpsertNode(1000, node1) 2362 2363 // Upgrade this node 2364 node2 := mock.Node() 2365 node2.SchedulingEligibility = "" 2366 state.UpsertNode(1001, node2) 2367 2368 // Verify the contents 2369 fsm2 := testSnapshotRestore(t, fsm) 2370 state2 := fsm2.State() 2371 out1, _ := state2.NodeByID(nil, node1.ID) 2372 out2, _ := state2.NodeByID(nil, node2.ID) 2373 node2.SchedulingEligibility = structs.NodeSchedulingEligible 2374 if !reflect.DeepEqual(node1, out1) { 2375 t.Fatalf("bad: \n%#v\n%#v", out1, node1) 2376 } 2377 if !reflect.DeepEqual(node2, out2) { 2378 t.Fatalf("bad: \n%#v\n%#v", out2, node2) 2379 } 2380 } 2381 2382 func TestFSM_SnapshotRestore_Jobs(t *testing.T) { 2383 t.Parallel() 2384 // Add some state 2385 fsm := testFSM(t) 2386 state := fsm.State() 2387 job1 := mock.Job() 2388 state.UpsertJob(1000, job1) 2389 job2 := mock.Job() 2390 state.UpsertJob(1001, job2) 2391 2392 // Verify the contents 2393 ws := memdb.NewWatchSet() 2394 fsm2 := testSnapshotRestore(t, fsm) 2395 state2 := fsm2.State() 2396 out1, _ := state2.JobByID(ws, job1.Namespace, job1.ID) 2397 out2, _ := state2.JobByID(ws, job2.Namespace, job2.ID) 2398 if !reflect.DeepEqual(job1, out1) { 2399 t.Fatalf("bad: \n%#v\n%#v", out1, job1) 2400 } 2401 if !reflect.DeepEqual(job2, out2) { 2402 t.Fatalf("bad: \n%#v\n%#v", out2, job2) 2403 } 2404 } 2405 2406 func TestFSM_SnapshotRestore_Evals(t *testing.T) { 2407 t.Parallel() 2408 // Add some state 2409 fsm := testFSM(t) 2410 state := fsm.State() 2411 eval1 := mock.Eval() 2412 state.UpsertEvals(1000, []*structs.Evaluation{eval1}) 2413 eval2 := mock.Eval() 2414 state.UpsertEvals(1001, []*structs.Evaluation{eval2}) 2415 2416 // Verify the contents 2417 fsm2 := testSnapshotRestore(t, fsm) 2418 state2 := fsm2.State() 2419 ws := memdb.NewWatchSet() 2420 out1, _ := state2.EvalByID(ws, eval1.ID) 2421 out2, _ := state2.EvalByID(ws, eval2.ID) 2422 if !reflect.DeepEqual(eval1, out1) { 2423 t.Fatalf("bad: \n%#v\n%#v", out1, eval1) 2424 } 2425 if !reflect.DeepEqual(eval2, out2) { 2426 t.Fatalf("bad: \n%#v\n%#v", out2, eval2) 2427 } 2428 } 2429 2430 func TestFSM_SnapshotRestore_Allocs(t *testing.T) { 2431 t.Parallel() 2432 // Add some state 2433 fsm := testFSM(t) 2434 state := fsm.State() 2435 alloc1 := mock.Alloc() 2436 alloc2 := mock.Alloc() 2437 state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID)) 2438 state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID)) 2439 state.UpsertAllocs(1000, []*structs.Allocation{alloc1}) 2440 state.UpsertAllocs(1001, []*structs.Allocation{alloc2}) 2441 2442 // Verify the contents 2443 fsm2 := testSnapshotRestore(t, fsm) 2444 state2 := fsm2.State() 2445 ws := memdb.NewWatchSet() 2446 out1, _ := state2.AllocByID(ws, alloc1.ID) 2447 out2, _ := state2.AllocByID(ws, alloc2.ID) 2448 if !reflect.DeepEqual(alloc1, out1) { 2449 t.Fatalf("bad: \n%#v\n%#v", out1, alloc1) 2450 } 2451 if !reflect.DeepEqual(alloc2, out2) { 2452 t.Fatalf("bad: \n%#v\n%#v", out2, alloc2) 2453 } 2454 } 2455 2456 func TestFSM_SnapshotRestore_Indexes(t *testing.T) { 2457 t.Parallel() 2458 // Add some state 2459 fsm := testFSM(t) 2460 state := fsm.State() 2461 node1 := mock.Node() 2462 state.UpsertNode(1000, node1) 2463 2464 // Verify the contents 2465 fsm2 := testSnapshotRestore(t, fsm) 2466 state2 := fsm2.State() 2467 2468 index, err := state2.Index("nodes") 2469 if err != nil { 2470 t.Fatalf("err: %v", err) 2471 } 2472 if index != 1000 { 2473 t.Fatalf("bad: %d", index) 2474 } 2475 } 2476 2477 func TestFSM_SnapshotRestore_TimeTable(t *testing.T) { 2478 t.Parallel() 2479 // Add some state 2480 fsm := testFSM(t) 2481 2482 tt := fsm.TimeTable() 2483 start := time.Now().UTC() 2484 tt.Witness(1000, start) 2485 tt.Witness(2000, start.Add(10*time.Minute)) 2486 2487 // Verify the contents 2488 fsm2 := testSnapshotRestore(t, fsm) 2489 2490 tt2 := fsm2.TimeTable() 2491 if tt2.NearestTime(1500) != start { 2492 t.Fatalf("bad") 2493 } 2494 if tt2.NearestIndex(start.Add(15*time.Minute)) != 2000 { 2495 t.Fatalf("bad") 2496 } 2497 } 2498 2499 func TestFSM_SnapshotRestore_PeriodicLaunches(t *testing.T) { 2500 t.Parallel() 2501 // Add some state 2502 fsm := testFSM(t) 2503 state := fsm.State() 2504 job1 := mock.Job() 2505 launch1 := &structs.PeriodicLaunch{ 2506 ID: job1.ID, 2507 Namespace: job1.Namespace, 2508 Launch: time.Now(), 2509 } 2510 state.UpsertPeriodicLaunch(1000, launch1) 2511 job2 := mock.Job() 2512 launch2 := &structs.PeriodicLaunch{ 2513 ID: job2.ID, 2514 Namespace: job2.Namespace, 2515 Launch: time.Now(), 2516 } 2517 state.UpsertPeriodicLaunch(1001, launch2) 2518 2519 // Verify the contents 2520 fsm2 := testSnapshotRestore(t, fsm) 2521 state2 := fsm2.State() 2522 ws := memdb.NewWatchSet() 2523 out1, _ := state2.PeriodicLaunchByID(ws, launch1.Namespace, launch1.ID) 2524 out2, _ := state2.PeriodicLaunchByID(ws, launch2.Namespace, launch2.ID) 2525 2526 if !cmp.Equal(launch1, out1) { 2527 t.Fatalf("bad: %v", cmp.Diff(launch1, out1)) 2528 } 2529 if !cmp.Equal(launch2, out2) { 2530 t.Fatalf("bad: %v", cmp.Diff(launch2, out2)) 2531 } 2532 } 2533 2534 func TestFSM_SnapshotRestore_JobSummary(t *testing.T) { 2535 t.Parallel() 2536 // Add some state 2537 fsm := testFSM(t) 2538 state := fsm.State() 2539 2540 job1 := mock.Job() 2541 state.UpsertJob(1000, job1) 2542 ws := memdb.NewWatchSet() 2543 js1, _ := state.JobSummaryByID(ws, job1.Namespace, job1.ID) 2544 2545 job2 := mock.Job() 2546 state.UpsertJob(1001, job2) 2547 js2, _ := state.JobSummaryByID(ws, job2.Namespace, job2.ID) 2548 2549 // Verify the contents 2550 fsm2 := testSnapshotRestore(t, fsm) 2551 state2 := fsm2.State() 2552 out1, _ := state2.JobSummaryByID(ws, job1.Namespace, job1.ID) 2553 out2, _ := state2.JobSummaryByID(ws, job2.Namespace, job2.ID) 2554 if !reflect.DeepEqual(js1, out1) { 2555 t.Fatalf("bad: \n%#v\n%#v", js1, out1) 2556 } 2557 if !reflect.DeepEqual(js2, out2) { 2558 t.Fatalf("bad: \n%#v\n%#v", js2, out2) 2559 } 2560 } 2561 2562 func TestFSM_SnapshotRestore_VaultAccessors(t *testing.T) { 2563 t.Parallel() 2564 // Add some state 2565 fsm := testFSM(t) 2566 state := fsm.State() 2567 a1 := mock.VaultAccessor() 2568 a2 := mock.VaultAccessor() 2569 state.UpsertVaultAccessor(1000, []*structs.VaultAccessor{a1, a2}) 2570 2571 // Verify the contents 2572 fsm2 := testSnapshotRestore(t, fsm) 2573 state2 := fsm2.State() 2574 ws := memdb.NewWatchSet() 2575 out1, _ := state2.VaultAccessor(ws, a1.Accessor) 2576 out2, _ := state2.VaultAccessor(ws, a2.Accessor) 2577 if !reflect.DeepEqual(a1, out1) { 2578 t.Fatalf("bad: \n%#v\n%#v", out1, a1) 2579 } 2580 if !reflect.DeepEqual(a2, out2) { 2581 t.Fatalf("bad: \n%#v\n%#v", out2, a2) 2582 } 2583 } 2584 2585 func TestFSM_SnapshotRestore_JobVersions(t *testing.T) { 2586 t.Parallel() 2587 // Add some state 2588 fsm := testFSM(t) 2589 state := fsm.State() 2590 job1 := mock.Job() 2591 state.UpsertJob(1000, job1) 2592 job2 := mock.Job() 2593 job2.ID = job1.ID 2594 state.UpsertJob(1001, job2) 2595 2596 // Verify the contents 2597 ws := memdb.NewWatchSet() 2598 fsm2 := testSnapshotRestore(t, fsm) 2599 state2 := fsm2.State() 2600 out1, _ := state2.JobByIDAndVersion(ws, job1.Namespace, job1.ID, job1.Version) 2601 out2, _ := state2.JobByIDAndVersion(ws, job2.Namespace, job2.ID, job2.Version) 2602 if !reflect.DeepEqual(job1, out1) { 2603 t.Fatalf("bad: \n%#v\n%#v", out1, job1) 2604 } 2605 if !reflect.DeepEqual(job2, out2) { 2606 t.Fatalf("bad: \n%#v\n%#v", out2, job2) 2607 } 2608 if job2.Version != 1 { 2609 t.Fatalf("bad: \n%#v\n%#v", 1, job2) 2610 } 2611 } 2612 2613 func TestFSM_SnapshotRestore_Deployments(t *testing.T) { 2614 t.Parallel() 2615 // Add some state 2616 fsm := testFSM(t) 2617 state := fsm.State() 2618 d1 := mock.Deployment() 2619 d2 := mock.Deployment() 2620 2621 j := mock.Job() 2622 d1.JobID = j.ID 2623 d2.JobID = j.ID 2624 2625 state.UpsertJob(999, j) 2626 state.UpsertDeployment(1000, d1) 2627 state.UpsertDeployment(1001, d2) 2628 2629 // Verify the contents 2630 fsm2 := testSnapshotRestore(t, fsm) 2631 state2 := fsm2.State() 2632 ws := memdb.NewWatchSet() 2633 out1, _ := state2.DeploymentByID(ws, d1.ID) 2634 out2, _ := state2.DeploymentByID(ws, d2.ID) 2635 if !reflect.DeepEqual(d1, out1) { 2636 t.Fatalf("bad: \n%#v\n%#v", out1, d1) 2637 } 2638 if !reflect.DeepEqual(d2, out2) { 2639 t.Fatalf("bad: \n%#v\n%#v", out2, d2) 2640 } 2641 } 2642 2643 func TestFSM_SnapshotRestore_ACLPolicy(t *testing.T) { 2644 t.Parallel() 2645 // Add some state 2646 fsm := testFSM(t) 2647 state := fsm.State() 2648 p1 := mock.ACLPolicy() 2649 p2 := mock.ACLPolicy() 2650 state.UpsertACLPolicies(1000, []*structs.ACLPolicy{p1, p2}) 2651 2652 // Verify the contents 2653 fsm2 := testSnapshotRestore(t, fsm) 2654 state2 := fsm2.State() 2655 ws := memdb.NewWatchSet() 2656 out1, _ := state2.ACLPolicyByName(ws, p1.Name) 2657 out2, _ := state2.ACLPolicyByName(ws, p2.Name) 2658 assert.Equal(t, p1, out1) 2659 assert.Equal(t, p2, out2) 2660 } 2661 2662 func TestFSM_SnapshotRestore_ACLTokens(t *testing.T) { 2663 t.Parallel() 2664 // Add some state 2665 fsm := testFSM(t) 2666 state := fsm.State() 2667 tk1 := mock.ACLToken() 2668 tk2 := mock.ACLToken() 2669 state.UpsertACLTokens(1000, []*structs.ACLToken{tk1, tk2}) 2670 2671 // Verify the contents 2672 fsm2 := testSnapshotRestore(t, fsm) 2673 state2 := fsm2.State() 2674 ws := memdb.NewWatchSet() 2675 out1, _ := state2.ACLTokenByAccessorID(ws, tk1.AccessorID) 2676 out2, _ := state2.ACLTokenByAccessorID(ws, tk2.AccessorID) 2677 assert.Equal(t, tk1, out1) 2678 assert.Equal(t, tk2, out2) 2679 } 2680 2681 func TestFSM_SnapshotRestore_SchedulerConfiguration(t *testing.T) { 2682 t.Parallel() 2683 // Add some state 2684 fsm := testFSM(t) 2685 state := fsm.State() 2686 schedConfig := &structs.SchedulerConfiguration{ 2687 PreemptionConfig: structs.PreemptionConfig{ 2688 SystemSchedulerEnabled: true, 2689 }, 2690 } 2691 state.SchedulerSetConfig(1000, schedConfig) 2692 2693 // Verify the contents 2694 require := require.New(t) 2695 fsm2 := testSnapshotRestore(t, fsm) 2696 state2 := fsm2.State() 2697 index, out, err := state2.SchedulerConfig() 2698 require.Nil(err) 2699 require.EqualValues(1000, index) 2700 require.Equal(schedConfig, out) 2701 2702 } 2703 2704 func TestFSM_ReconcileSummaries(t *testing.T) { 2705 t.Parallel() 2706 // Add some state 2707 fsm := testFSM(t) 2708 state := fsm.State() 2709 2710 // Add a node 2711 node := mock.Node() 2712 state.UpsertNode(800, node) 2713 2714 // Make a job so that none of the tasks can be placed 2715 job1 := mock.Job() 2716 job1.TaskGroups[0].Tasks[0].Resources.CPU = 5000 2717 state.UpsertJob(1000, job1) 2718 2719 // make a job which can make partial progress 2720 alloc := mock.Alloc() 2721 alloc.NodeID = node.ID 2722 state.UpsertJob(1010, alloc.Job) 2723 state.UpsertAllocs(1011, []*structs.Allocation{alloc}) 2724 2725 // Delete the summaries 2726 state.DeleteJobSummary(1030, job1.Namespace, job1.ID) 2727 state.DeleteJobSummary(1040, alloc.Namespace, alloc.Job.ID) 2728 2729 req := structs.GenericRequest{} 2730 buf, err := structs.Encode(structs.ReconcileJobSummariesRequestType, req) 2731 if err != nil { 2732 t.Fatalf("err: %v", err) 2733 } 2734 2735 resp := fsm.Apply(makeLog(buf)) 2736 if resp != nil { 2737 t.Fatalf("resp: %v", resp) 2738 } 2739 2740 ws := memdb.NewWatchSet() 2741 out1, _ := state.JobSummaryByID(ws, job1.Namespace, job1.ID) 2742 expected := structs.JobSummary{ 2743 JobID: job1.ID, 2744 Namespace: job1.Namespace, 2745 Summary: map[string]structs.TaskGroupSummary{ 2746 "web": { 2747 Queued: 10, 2748 }, 2749 }, 2750 CreateIndex: 1000, 2751 ModifyIndex: out1.ModifyIndex, 2752 } 2753 if !reflect.DeepEqual(&expected, out1) { 2754 t.Fatalf("expected: %#v, actual: %#v", &expected, out1) 2755 } 2756 2757 // This exercises the code path which adds the allocations made by the 2758 // planner and the number of unplaced allocations in the reconcile summaries 2759 // codepath 2760 out2, _ := state.JobSummaryByID(ws, alloc.Namespace, alloc.Job.ID) 2761 expected = structs.JobSummary{ 2762 JobID: alloc.Job.ID, 2763 Namespace: alloc.Job.Namespace, 2764 Summary: map[string]structs.TaskGroupSummary{ 2765 "web": { 2766 Queued: 9, 2767 Starting: 1, 2768 }, 2769 }, 2770 CreateIndex: 1010, 2771 ModifyIndex: out2.ModifyIndex, 2772 } 2773 if !reflect.DeepEqual(&expected, out2) { 2774 t.Fatalf("Diff % #v", pretty.Diff(&expected, out2)) 2775 } 2776 } 2777 2778 // COMPAT: Remove in 0.11 2779 func TestFSM_ReconcileParentJobSummary(t *testing.T) { 2780 // This test exercises code to handle https://github.com/hashicorp/nomad/issues/3886 2781 t.Parallel() 2782 2783 require := require.New(t) 2784 // Add some state 2785 fsm := testFSM(t) 2786 state := fsm.State() 2787 2788 // Add a node 2789 node := mock.Node() 2790 state.UpsertNode(800, node) 2791 2792 // Make a parameterized job 2793 job1 := mock.BatchJob() 2794 job1.ID = "test" 2795 job1.ParameterizedJob = &structs.ParameterizedJobConfig{ 2796 Payload: "random", 2797 } 2798 job1.TaskGroups[0].Count = 1 2799 state.UpsertJob(1000, job1) 2800 2801 // Make a child job 2802 childJob := job1.Copy() 2803 childJob.ID = job1.ID + "dispatch-23423423" 2804 childJob.ParentID = job1.ID 2805 childJob.Dispatched = true 2806 childJob.Status = structs.JobStatusRunning 2807 2808 // Create an alloc for child job 2809 alloc := mock.Alloc() 2810 alloc.NodeID = node.ID 2811 alloc.Job = childJob 2812 alloc.JobID = childJob.ID 2813 alloc.ClientStatus = structs.AllocClientStatusRunning 2814 2815 state.UpsertJob(1010, childJob) 2816 state.UpsertAllocs(1011, []*structs.Allocation{alloc}) 2817 2818 // Make the summary incorrect in the state store 2819 summary, err := state.JobSummaryByID(nil, job1.Namespace, job1.ID) 2820 require.Nil(err) 2821 2822 summary.Children = nil 2823 summary.Summary = make(map[string]structs.TaskGroupSummary) 2824 summary.Summary["web"] = structs.TaskGroupSummary{ 2825 Queued: 1, 2826 } 2827 2828 req := structs.GenericRequest{} 2829 buf, err := structs.Encode(structs.ReconcileJobSummariesRequestType, req) 2830 require.Nil(err) 2831 2832 resp := fsm.Apply(makeLog(buf)) 2833 require.Nil(resp) 2834 2835 ws := memdb.NewWatchSet() 2836 out1, _ := state.JobSummaryByID(ws, job1.Namespace, job1.ID) 2837 expected := structs.JobSummary{ 2838 JobID: job1.ID, 2839 Namespace: job1.Namespace, 2840 Summary: make(map[string]structs.TaskGroupSummary), 2841 CreateIndex: 1000, 2842 ModifyIndex: out1.ModifyIndex, 2843 Children: &structs.JobChildrenSummary{ 2844 Running: 1, 2845 }, 2846 } 2847 require.Equal(&expected, out1) 2848 } 2849 2850 func TestFSM_LeakedDeployments(t *testing.T) { 2851 t.Parallel() 2852 require := require.New(t) 2853 2854 // Add some state 2855 fsm := testFSM(t) 2856 state := fsm.State() 2857 d := mock.Deployment() 2858 require.NoError(state.UpsertDeployment(1000, d)) 2859 2860 // Verify the contents 2861 fsm2 := testSnapshotRestore(t, fsm) 2862 state2 := fsm2.State() 2863 out, _ := state2.DeploymentByID(nil, d.ID) 2864 require.NotNil(out) 2865 require.Equal(structs.DeploymentStatusCancelled, out.Status) 2866 } 2867 2868 func TestFSM_Autopilot(t *testing.T) { 2869 t.Parallel() 2870 fsm := testFSM(t) 2871 2872 // Set the autopilot config using a request. 2873 req := structs.AutopilotSetConfigRequest{ 2874 Datacenter: "dc1", 2875 Config: structs.AutopilotConfig{ 2876 CleanupDeadServers: true, 2877 LastContactThreshold: 10 * time.Second, 2878 MaxTrailingLogs: 300, 2879 }, 2880 } 2881 buf, err := structs.Encode(structs.AutopilotRequestType, req) 2882 if err != nil { 2883 t.Fatalf("err: %v", err) 2884 } 2885 resp := fsm.Apply(makeLog(buf)) 2886 if _, ok := resp.(error); ok { 2887 t.Fatalf("bad: %v", resp) 2888 } 2889 2890 // Verify key is set directly in the state store. 2891 _, config, err := fsm.state.AutopilotConfig() 2892 if err != nil { 2893 t.Fatalf("err: %v", err) 2894 } 2895 if config.CleanupDeadServers != req.Config.CleanupDeadServers { 2896 t.Fatalf("bad: %v", config.CleanupDeadServers) 2897 } 2898 if config.LastContactThreshold != req.Config.LastContactThreshold { 2899 t.Fatalf("bad: %v", config.LastContactThreshold) 2900 } 2901 if config.MaxTrailingLogs != req.Config.MaxTrailingLogs { 2902 t.Fatalf("bad: %v", config.MaxTrailingLogs) 2903 } 2904 2905 // Now use CAS and provide an old index 2906 req.CAS = true 2907 req.Config.CleanupDeadServers = false 2908 req.Config.ModifyIndex = config.ModifyIndex - 1 2909 buf, err = structs.Encode(structs.AutopilotRequestType, req) 2910 if err != nil { 2911 t.Fatalf("err: %v", err) 2912 } 2913 resp = fsm.Apply(makeLog(buf)) 2914 if _, ok := resp.(error); ok { 2915 t.Fatalf("bad: %v", resp) 2916 } 2917 2918 _, config, err = fsm.state.AutopilotConfig() 2919 if err != nil { 2920 t.Fatalf("err: %v", err) 2921 } 2922 if !config.CleanupDeadServers { 2923 t.Fatalf("bad: %v", config.CleanupDeadServers) 2924 } 2925 } 2926 2927 func TestFSM_SchedulerConfig(t *testing.T) { 2928 t.Parallel() 2929 fsm := testFSM(t) 2930 2931 require := require.New(t) 2932 2933 // Set the scheduler config using a request. 2934 req := structs.SchedulerSetConfigRequest{ 2935 Config: structs.SchedulerConfiguration{ 2936 PreemptionConfig: structs.PreemptionConfig{ 2937 SystemSchedulerEnabled: true, 2938 BatchSchedulerEnabled: true, 2939 }, 2940 }, 2941 } 2942 buf, err := structs.Encode(structs.SchedulerConfigRequestType, req) 2943 require.Nil(err) 2944 2945 resp := fsm.Apply(makeLog(buf)) 2946 if _, ok := resp.(error); ok { 2947 t.Fatalf("bad: %v", resp) 2948 } 2949 2950 // Verify key is set directly in the state store. 2951 _, config, err := fsm.state.SchedulerConfig() 2952 require.Nil(err) 2953 2954 require.Equal(config.PreemptionConfig.SystemSchedulerEnabled, req.Config.PreemptionConfig.SystemSchedulerEnabled) 2955 require.Equal(config.PreemptionConfig.BatchSchedulerEnabled, req.Config.PreemptionConfig.BatchSchedulerEnabled) 2956 2957 // Now use CAS and provide an old index 2958 req.CAS = true 2959 req.Config.PreemptionConfig = structs.PreemptionConfig{SystemSchedulerEnabled: false, BatchSchedulerEnabled: false} 2960 req.Config.ModifyIndex = config.ModifyIndex - 1 2961 buf, err = structs.Encode(structs.SchedulerConfigRequestType, req) 2962 require.Nil(err) 2963 2964 resp = fsm.Apply(makeLog(buf)) 2965 if _, ok := resp.(error); ok { 2966 t.Fatalf("bad: %v", resp) 2967 } 2968 2969 _, config, err = fsm.state.SchedulerConfig() 2970 require.Nil(err) 2971 // Verify that preemption is still enabled 2972 require.True(config.PreemptionConfig.SystemSchedulerEnabled) 2973 require.True(config.PreemptionConfig.BatchSchedulerEnabled) 2974 }