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