github.com/hernad/nomad@v1.6.112/nomad/fsm_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package nomad 5 6 import ( 7 "bytes" 8 "context" 9 "fmt" 10 "reflect" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/google/go-cmp/cmp" 16 "github.com/google/go-cmp/cmp/cmpopts" 17 memdb "github.com/hashicorp/go-memdb" 18 "github.com/hashicorp/raft" 19 "github.com/kr/pretty" 20 "github.com/shoenig/test/must" 21 "github.com/stretchr/testify/assert" 22 "github.com/stretchr/testify/require" 23 24 "github.com/hernad/nomad/ci" 25 "github.com/hernad/nomad/helper/pointer" 26 "github.com/hernad/nomad/helper/testlog" 27 "github.com/hernad/nomad/helper/uuid" 28 "github.com/hernad/nomad/nomad/mock" 29 "github.com/hernad/nomad/nomad/state" 30 "github.com/hernad/nomad/nomad/stream" 31 "github.com/hernad/nomad/nomad/structs" 32 "github.com/hernad/nomad/testutil" 33 ) 34 35 type MockSink struct { 36 *bytes.Buffer 37 cancel bool 38 } 39 40 func (m *MockSink) ID() string { 41 return "Mock" 42 } 43 44 func (m *MockSink) Cancel() error { 45 m.cancel = true 46 return nil 47 } 48 49 func (m *MockSink) Close() error { 50 return nil 51 } 52 53 func testStateStore(t *testing.T) *state.StateStore { 54 return state.TestStateStore(t) 55 } 56 57 func testFSM(t *testing.T) *nomadFSM { 58 broker := testBroker(t, 0) 59 dispatcher, _ := testPeriodicDispatcher(t) 60 logger := testlog.HCLogger(t) 61 fsmConfig := &FSMConfig{ 62 EvalBroker: broker, 63 Periodic: dispatcher, 64 Blocked: NewBlockedEvals(broker, logger), 65 Logger: logger, 66 Region: "global", 67 EnableEventBroker: true, 68 EventBufferSize: 100, 69 } 70 fsm, err := NewFSM(fsmConfig) 71 if err != nil { 72 t.Fatalf("err: %v", err) 73 } 74 if fsm == nil { 75 t.Fatalf("missing fsm") 76 } 77 return fsm 78 } 79 80 func makeLog(buf []byte) *raft.Log { 81 return &raft.Log{ 82 Index: 1, 83 Term: 1, 84 Type: raft.LogCommand, 85 Data: buf, 86 } 87 } 88 89 func TestFSM_UpsertNodeEvents(t *testing.T) { 90 ci.Parallel(t) 91 require := require.New(t) 92 fsm := testFSM(t) 93 state := fsm.State() 94 95 node := mock.Node() 96 97 err := state.UpsertNode(structs.MsgTypeTestSetup, 1000, node) 98 if err != nil { 99 t.Fatalf("err: %v", err) 100 } 101 102 nodeEvent := &structs.NodeEvent{ 103 Message: "Heartbeating failed", 104 Subsystem: "Heartbeat", 105 Timestamp: time.Now(), 106 } 107 108 nodeEvents := []*structs.NodeEvent{nodeEvent} 109 allEvents := map[string][]*structs.NodeEvent{node.ID: nodeEvents} 110 111 req := structs.EmitNodeEventsRequest{ 112 NodeEvents: allEvents, 113 WriteRequest: structs.WriteRequest{Region: "global"}, 114 } 115 buf, err := structs.Encode(structs.UpsertNodeEventsType, req) 116 require.Nil(err) 117 118 // the response in this case will be an error 119 resp := fsm.Apply(makeLog(buf)) 120 require.Nil(resp) 121 122 ws := memdb.NewWatchSet() 123 out, err := state.NodeByID(ws, node.ID) 124 require.Nil(err) 125 126 require.Equal(2, len(out.Events)) 127 128 first := out.Events[1] 129 require.Equal(uint64(1), first.CreateIndex) 130 require.Equal("Heartbeating failed", first.Message) 131 } 132 133 func TestFSM_UpsertNode(t *testing.T) { 134 ci.Parallel(t) 135 fsm := testFSM(t) 136 fsm.blockedEvals.SetEnabled(true) 137 138 node := mock.Node() 139 140 // Mark an eval as blocked. 141 eval := mock.Eval() 142 eval.ClassEligibility = map[string]bool{node.ComputedClass: true} 143 fsm.blockedEvals.Block(eval) 144 145 req := structs.NodeRegisterRequest{ 146 Node: node, 147 } 148 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 149 if err != nil { 150 t.Fatalf("err: %v", err) 151 } 152 153 resp := fsm.Apply(makeLog(buf)) 154 if resp != nil { 155 t.Fatalf("resp: %v", resp) 156 } 157 158 // Verify we are registered 159 ws := memdb.NewWatchSet() 160 n, err := fsm.State().NodeByID(ws, req.Node.ID) 161 if err != nil { 162 t.Fatalf("err: %v", err) 163 } 164 if n == nil { 165 t.Fatalf("not found!") 166 } 167 if n.CreateIndex != 1 { 168 t.Fatalf("bad index: %d", node.CreateIndex) 169 } 170 171 tt := fsm.TimeTable() 172 index := tt.NearestIndex(time.Now().UTC()) 173 if index != 1 { 174 t.Fatalf("bad: %d", index) 175 } 176 177 // Verify the eval was unblocked. 178 testutil.WaitForResult(func() (bool, error) { 179 bStats := fsm.blockedEvals.Stats() 180 if bStats.TotalBlocked != 0 { 181 return false, fmt.Errorf("bad: %#v", bStats) 182 } 183 return true, nil 184 }, func(err error) { 185 t.Fatalf("err: %s", err) 186 }) 187 188 } 189 190 func TestFSM_UpsertNode_Canonicalize(t *testing.T) { 191 ci.Parallel(t) 192 require := require.New(t) 193 194 fsm := testFSM(t) 195 fsm.blockedEvals.SetEnabled(true) 196 197 // Setup a node without eligibility, ensure that upsert/canonicalize put it back 198 node := mock.Node() 199 node.SchedulingEligibility = "" 200 201 req := structs.NodeRegisterRequest{ 202 Node: node, 203 } 204 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 205 require.Nil(err) 206 207 require.Nil(fsm.Apply(makeLog(buf))) 208 209 // Verify we are registered 210 n, err := fsm.State().NodeByID(nil, req.Node.ID) 211 require.Nil(err) 212 require.NotNil(n) 213 require.EqualValues(1, n.CreateIndex) 214 require.Equal(structs.NodeSchedulingEligible, n.SchedulingEligibility) 215 } 216 217 func TestFSM_UpsertNode_Canonicalize_Ineligible(t *testing.T) { 218 ci.Parallel(t) 219 require := require.New(t) 220 221 fsm := testFSM(t) 222 fsm.blockedEvals.SetEnabled(true) 223 224 // Setup a node without eligibility, ensure that upsert/canonicalize put it back 225 node := mock.DrainNode() 226 node.SchedulingEligibility = "" 227 228 req := structs.NodeRegisterRequest{ 229 Node: node, 230 } 231 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 232 require.Nil(err) 233 234 require.Nil(fsm.Apply(makeLog(buf))) 235 236 // Verify we are registered 237 n, err := fsm.State().NodeByID(nil, req.Node.ID) 238 require.Nil(err) 239 require.NotNil(n) 240 require.EqualValues(1, n.CreateIndex) 241 require.Equal(structs.NodeSchedulingIneligible, n.SchedulingEligibility) 242 } 243 244 func TestFSM_UpsertNode_NodePool(t *testing.T) { 245 ci.Parallel(t) 246 247 testCases := []struct { 248 name string 249 setupReqFn func(*structs.NodeRegisterRequest) 250 validateFn func(*testing.T, *structs.Node, *structs.NodePool) 251 }{ 252 { 253 name: "node with empty node pool is placed in defualt", 254 setupReqFn: func(req *structs.NodeRegisterRequest) { 255 req.Node.NodePool = "" 256 }, 257 validateFn: func(t *testing.T, node *structs.Node, pool *structs.NodePool) { 258 must.Eq(t, structs.NodePoolDefault, node.NodePool) 259 must.Eq(t, 1, pool.ModifyIndex) 260 }, 261 }, 262 { 263 name: "create new node pool with node", 264 setupReqFn: func(req *structs.NodeRegisterRequest) { 265 req.Node.NodePool = "new" 266 req.CreateNodePool = true 267 }, 268 validateFn: func(t *testing.T, node *structs.Node, pool *structs.NodePool) { 269 must.NotNil(t, pool) 270 must.Eq(t, "new", pool.Name) 271 must.Eq(t, pool.Name, node.NodePool) 272 must.Eq(t, node.ModifyIndex, pool.CreateIndex) 273 }, 274 }, 275 { 276 name: "don't create new node pool with node", 277 setupReqFn: func(req *structs.NodeRegisterRequest) { 278 req.Node.NodePool = "new" 279 req.CreateNodePool = false 280 }, 281 validateFn: func(t *testing.T, node *structs.Node, pool *structs.NodePool) { 282 must.Nil(t, pool) 283 must.Eq(t, "new", node.NodePool) 284 }, 285 }, 286 } 287 288 for _, tc := range testCases { 289 t.Run(tc.name, func(t *testing.T) { 290 fsm := testFSM(t) 291 292 node := mock.Node() 293 req := structs.NodeRegisterRequest{ 294 Node: node, 295 } 296 if tc.setupReqFn != nil { 297 tc.setupReqFn(&req) 298 } 299 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 300 must.NoError(t, err) 301 302 resp := fsm.Apply(makeLog(buf)) 303 must.Nil(t, resp) 304 305 // Snapshot the state. 306 s := fsm.State() 307 308 gotNode, err := s.NodeByID(nil, node.ID) 309 must.NoError(t, err) 310 311 gotPool, err := s.NodePoolByName(nil, gotNode.NodePool) 312 must.NoError(t, err) 313 314 if tc.validateFn != nil { 315 tc.validateFn(t, gotNode, gotPool) 316 } 317 }) 318 } 319 } 320 321 func TestFSM_DeregisterNode(t *testing.T) { 322 ci.Parallel(t) 323 fsm := testFSM(t) 324 325 node := mock.Node() 326 req := structs.NodeRegisterRequest{ 327 Node: node, 328 } 329 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 330 if err != nil { 331 t.Fatalf("err: %v", err) 332 } 333 334 resp := fsm.Apply(makeLog(buf)) 335 if resp != nil { 336 t.Fatalf("resp: %v", resp) 337 } 338 339 req2 := structs.NodeBatchDeregisterRequest{ 340 NodeIDs: []string{node.ID}, 341 } 342 buf, err = structs.Encode(structs.NodeBatchDeregisterRequestType, req2) 343 if err != nil { 344 t.Fatalf("err: %v", err) 345 } 346 347 resp = fsm.Apply(makeLog(buf)) 348 if resp != nil { 349 t.Fatalf("resp: %v", resp) 350 } 351 352 // Verify we are NOT registered 353 ws := memdb.NewWatchSet() 354 node, err = fsm.State().NodeByID(ws, req.Node.ID) 355 if err != nil { 356 t.Fatalf("err: %v", err) 357 } 358 if node != nil { 359 t.Fatalf("node found!") 360 } 361 } 362 363 func TestFSM_UpdateNodeStatus(t *testing.T) { 364 ci.Parallel(t) 365 require := require.New(t) 366 fsm := testFSM(t) 367 fsm.blockedEvals.SetEnabled(true) 368 369 node := mock.Node() 370 req := structs.NodeRegisterRequest{ 371 Node: node, 372 } 373 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 374 require.NoError(err) 375 376 resp := fsm.Apply(makeLog(buf)) 377 require.Nil(resp) 378 379 // Mark an eval as blocked. 380 eval := mock.Eval() 381 eval.ClassEligibility = map[string]bool{node.ComputedClass: true} 382 fsm.blockedEvals.Block(eval) 383 384 event := &structs.NodeEvent{ 385 Message: "Node ready foo", 386 Subsystem: structs.NodeEventSubsystemCluster, 387 Timestamp: time.Now(), 388 } 389 req2 := structs.NodeUpdateStatusRequest{ 390 NodeID: node.ID, 391 Status: structs.NodeStatusReady, 392 NodeEvent: event, 393 } 394 buf, err = structs.Encode(structs.NodeUpdateStatusRequestType, req2) 395 require.NoError(err) 396 397 resp = fsm.Apply(makeLog(buf)) 398 require.Nil(resp) 399 400 // Verify the status is ready. 401 ws := memdb.NewWatchSet() 402 node, err = fsm.State().NodeByID(ws, req.Node.ID) 403 require.NoError(err) 404 require.Equal(structs.NodeStatusReady, node.Status) 405 require.Len(node.Events, 2) 406 require.Equal(event.Message, node.Events[1].Message) 407 408 // Verify the eval was unblocked. 409 testutil.WaitForResult(func() (bool, error) { 410 bStats := fsm.blockedEvals.Stats() 411 if bStats.TotalBlocked != 0 { 412 return false, fmt.Errorf("bad: %#v", bStats) 413 } 414 return true, nil 415 }, func(err error) { 416 t.Fatalf("err: %s", err) 417 }) 418 } 419 420 func TestFSM_BatchUpdateNodeDrain(t *testing.T) { 421 ci.Parallel(t) 422 require := require.New(t) 423 fsm := testFSM(t) 424 425 node := mock.Node() 426 req := structs.NodeRegisterRequest{ 427 Node: node, 428 } 429 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 430 require.Nil(err) 431 432 resp := fsm.Apply(makeLog(buf)) 433 require.Nil(resp) 434 435 strategy := &structs.DrainStrategy{ 436 DrainSpec: structs.DrainSpec{ 437 Deadline: 10 * time.Second, 438 }, 439 } 440 event := &structs.NodeEvent{ 441 Message: "Drain strategy enabled", 442 Subsystem: structs.NodeEventSubsystemDrain, 443 Timestamp: time.Now(), 444 } 445 req2 := structs.BatchNodeUpdateDrainRequest{ 446 Updates: map[string]*structs.DrainUpdate{ 447 node.ID: { 448 DrainStrategy: strategy, 449 }, 450 }, 451 NodeEvents: map[string]*structs.NodeEvent{ 452 node.ID: event, 453 }, 454 } 455 buf, err = structs.Encode(structs.BatchNodeUpdateDrainRequestType, req2) 456 require.Nil(err) 457 458 resp = fsm.Apply(makeLog(buf)) 459 require.Nil(resp) 460 461 // Verify drain is set 462 ws := memdb.NewWatchSet() 463 node, err = fsm.State().NodeByID(ws, req.Node.ID) 464 require.Nil(err) 465 require.Equal(node.DrainStrategy, strategy) 466 require.Len(node.Events, 2) 467 } 468 469 func TestFSM_UpdateNodeDrain(t *testing.T) { 470 ci.Parallel(t) 471 require := require.New(t) 472 fsm := testFSM(t) 473 474 node := mock.Node() 475 req := structs.NodeRegisterRequest{ 476 Node: node, 477 } 478 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 479 require.Nil(err) 480 481 resp := fsm.Apply(makeLog(buf)) 482 require.Nil(resp) 483 484 strategy := &structs.DrainStrategy{ 485 DrainSpec: structs.DrainSpec{ 486 Deadline: 10 * time.Second, 487 }, 488 } 489 req2 := structs.NodeUpdateDrainRequest{ 490 NodeID: node.ID, 491 DrainStrategy: strategy, 492 NodeEvent: &structs.NodeEvent{ 493 Message: "Drain strategy enabled", 494 Subsystem: structs.NodeEventSubsystemDrain, 495 Timestamp: time.Now(), 496 }, 497 } 498 buf, err = structs.Encode(structs.NodeUpdateDrainRequestType, req2) 499 require.Nil(err) 500 501 resp = fsm.Apply(makeLog(buf)) 502 require.Nil(resp) 503 504 // Verify we are NOT registered 505 ws := memdb.NewWatchSet() 506 node, err = fsm.State().NodeByID(ws, req.Node.ID) 507 require.Nil(err) 508 require.Equal(node.DrainStrategy, strategy) 509 require.Len(node.Events, 2) 510 } 511 512 func TestFSM_UpdateNodeEligibility(t *testing.T) { 513 ci.Parallel(t) 514 require := require.New(t) 515 fsm := testFSM(t) 516 517 node := mock.Node() 518 req := structs.NodeRegisterRequest{ 519 Node: node, 520 } 521 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 522 require.Nil(err) 523 524 resp := fsm.Apply(makeLog(buf)) 525 require.Nil(resp) 526 527 event := &structs.NodeEvent{ 528 Message: "Node marked as ineligible", 529 Subsystem: structs.NodeEventSubsystemCluster, 530 Timestamp: time.Now(), 531 } 532 533 // Set the eligibility 534 req2 := structs.NodeUpdateEligibilityRequest{ 535 NodeID: node.ID, 536 Eligibility: structs.NodeSchedulingIneligible, 537 NodeEvent: event, 538 } 539 buf, err = structs.Encode(structs.NodeUpdateEligibilityRequestType, req2) 540 require.Nil(err) 541 542 resp = fsm.Apply(makeLog(buf)) 543 require.Nil(resp) 544 545 // Lookup the node and check 546 node, err = fsm.State().NodeByID(nil, req.Node.ID) 547 require.Nil(err) 548 require.Equal(node.SchedulingEligibility, structs.NodeSchedulingIneligible) 549 require.Len(node.Events, 2) 550 require.Equal(event.Message, node.Events[1].Message) 551 552 // Update the drain 553 strategy := &structs.DrainStrategy{ 554 DrainSpec: structs.DrainSpec{ 555 Deadline: 10 * time.Second, 556 }, 557 } 558 req3 := structs.NodeUpdateDrainRequest{ 559 NodeID: node.ID, 560 DrainStrategy: strategy, 561 } 562 buf, err = structs.Encode(structs.NodeUpdateDrainRequestType, req3) 563 require.Nil(err) 564 resp = fsm.Apply(makeLog(buf)) 565 require.Nil(resp) 566 567 // Try forcing eligibility 568 req4 := structs.NodeUpdateEligibilityRequest{ 569 NodeID: node.ID, 570 Eligibility: structs.NodeSchedulingEligible, 571 } 572 buf, err = structs.Encode(structs.NodeUpdateEligibilityRequestType, req4) 573 require.Nil(err) 574 575 resp = fsm.Apply(makeLog(buf)) 576 require.NotNil(resp) 577 err, ok := resp.(error) 578 require.True(ok) 579 require.Contains(err.Error(), "draining") 580 } 581 582 func TestFSM_UpdateNodeEligibility_Unblock(t *testing.T) { 583 ci.Parallel(t) 584 require := require.New(t) 585 fsm := testFSM(t) 586 587 node := mock.Node() 588 req := structs.NodeRegisterRequest{ 589 Node: node, 590 } 591 buf, err := structs.Encode(structs.NodeRegisterRequestType, req) 592 require.Nil(err) 593 594 resp := fsm.Apply(makeLog(buf)) 595 require.Nil(resp) 596 597 // Set the eligibility 598 req2 := structs.NodeUpdateEligibilityRequest{ 599 NodeID: node.ID, 600 Eligibility: structs.NodeSchedulingIneligible, 601 } 602 buf, err = structs.Encode(structs.NodeUpdateEligibilityRequestType, req2) 603 require.Nil(err) 604 605 resp = fsm.Apply(makeLog(buf)) 606 require.Nil(resp) 607 608 // Mark an eval as blocked. 609 eval := mock.Eval() 610 eval.ClassEligibility = map[string]bool{node.ComputedClass: true} 611 fsm.blockedEvals.Block(eval) 612 613 // Set eligible 614 req4 := structs.NodeUpdateEligibilityRequest{ 615 NodeID: node.ID, 616 Eligibility: structs.NodeSchedulingEligible, 617 } 618 buf, err = structs.Encode(structs.NodeUpdateEligibilityRequestType, req4) 619 require.Nil(err) 620 621 resp = fsm.Apply(makeLog(buf)) 622 require.Nil(resp) 623 624 // Verify the eval was unblocked. 625 testutil.WaitForResult(func() (bool, error) { 626 bStats := fsm.blockedEvals.Stats() 627 if bStats.TotalBlocked != 0 { 628 return false, fmt.Errorf("bad: %#v", bStats) 629 } 630 return true, nil 631 }, func(err error) { 632 t.Fatalf("err: %s", err) 633 }) 634 } 635 636 func TestFSM_NodePoolDelete(t *testing.T) { 637 ci.Parallel(t) 638 639 // Create FSM and populate state. 640 fsm := testFSM(t) 641 pools := []*structs.NodePool{ 642 mock.NodePool(), 643 mock.NodePool(), 644 mock.NodePool(), 645 mock.NodePool(), 646 } 647 err := fsm.State().UpsertNodePools(structs.MsgTypeTestSetup, 1000, pools) 648 must.NoError(t, err) 649 650 // Delete some of the node pools. 651 req := structs.NodePoolDeleteRequest{ 652 Names: []string{pools[0].Name, pools[1].Name}, 653 } 654 buf, err := structs.Encode(structs.NodePoolDeleteRequestType, req) 655 must.NoError(t, err) 656 657 resp := fsm.Apply(makeLog(buf)) 658 must.Nil(t, resp) 659 660 // Verify selected node pools were deleted. 661 ws := memdb.NewWatchSet() 662 for i, pool := range pools { 663 got, err := fsm.State().NodePoolByName(ws, pool.Name) 664 must.NoError(t, err) 665 666 switch i { 667 // Node pools 0 and 1 were deleted. 668 case 0, 1: 669 must.Nil(t, got) 670 default: 671 must.NotNil(t, got) 672 } 673 } 674 } 675 676 func TestFSM_NodePoolUpsert(t *testing.T) { 677 ci.Parallel(t) 678 679 // Create FSM and create some node pools. 680 fsm := testFSM(t) 681 pools := []*structs.NodePool{ 682 mock.NodePool(), 683 mock.NodePool(), 684 mock.NodePool(), 685 } 686 req := structs.NodePoolUpsertRequest{ 687 NodePools: pools, 688 } 689 buf, err := structs.Encode(structs.NodePoolUpsertRequestType, req) 690 must.NoError(t, err) 691 692 resp := fsm.Apply(makeLog(buf)) 693 must.Nil(t, resp) 694 695 // Verify node pools were created. 696 ws := memdb.NewWatchSet() 697 for _, pool := range pools { 698 got, err := fsm.State().NodePoolByName(ws, pool.Name) 699 700 must.NoError(t, err) 701 must.Eq(t, pool, got, must.Cmp(cmpopts.IgnoreFields( 702 structs.NodePool{}, 703 "CreateIndex", 704 "ModifyIndex", 705 ))) 706 } 707 708 // Update one of the node pools. 709 updatedPool := pools[0].Copy() 710 updatedPool.Description = "updated" 711 updatedPool.Meta = map[string]string{ 712 "update": "true", 713 } 714 715 req = structs.NodePoolUpsertRequest{ 716 NodePools: []*structs.NodePool{updatedPool}, 717 } 718 buf, err = structs.Encode(structs.NodePoolUpsertRequestType, req) 719 must.NoError(t, err) 720 721 resp = fsm.Apply(makeLog(buf)) 722 must.Nil(t, resp) 723 724 // Verify node pool was updated. 725 ws = memdb.NewWatchSet() 726 got, err := fsm.State().NodePoolByName(ws, updatedPool.Name) 727 must.NoError(t, err) 728 must.Eq(t, updatedPool, got, must.Cmp(cmpopts.IgnoreFields( 729 structs.NodePool{}, 730 "CreateIndex", 731 "ModifyIndex", 732 ))) 733 } 734 735 func TestFSM_RegisterJob(t *testing.T) { 736 ci.Parallel(t) 737 fsm := testFSM(t) 738 739 job := mock.PeriodicJob() 740 req := structs.JobRegisterRequest{ 741 Job: job, 742 WriteRequest: structs.WriteRequest{ 743 Namespace: job.Namespace, 744 }, 745 } 746 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 747 if err != nil { 748 t.Fatalf("err: %v", err) 749 } 750 751 resp := fsm.Apply(makeLog(buf)) 752 if resp != nil { 753 t.Fatalf("resp: %v", resp) 754 } 755 756 // Verify we are registered 757 ws := memdb.NewWatchSet() 758 jobOut, err := fsm.State().JobByID(ws, req.Namespace, req.Job.ID) 759 if err != nil { 760 t.Fatalf("err: %v", err) 761 } 762 if jobOut == nil { 763 t.Fatalf("not found!") 764 } 765 if jobOut.CreateIndex != 1 { 766 t.Fatalf("bad index: %d", jobOut.CreateIndex) 767 } 768 769 // Verify it was added to the periodic runner. 770 tuple := structs.NamespacedID{ 771 ID: job.ID, 772 Namespace: job.Namespace, 773 } 774 if _, ok := fsm.periodicDispatcher.tracked[tuple]; !ok { 775 t.Fatal("job not added to periodic runner") 776 } 777 778 // Verify the launch time was tracked. 779 launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Namespace, req.Job.ID) 780 if err != nil { 781 t.Fatalf("err: %v", err) 782 } 783 if launchOut == nil { 784 t.Fatalf("not found!") 785 } 786 if launchOut.Launch.IsZero() { 787 t.Fatalf("bad launch time: %v", launchOut.Launch) 788 } 789 } 790 791 func TestFSM_RegisterPeriodicJob_NonLeader(t *testing.T) { 792 ci.Parallel(t) 793 fsm := testFSM(t) 794 795 // Disable the dispatcher 796 fsm.periodicDispatcher.SetEnabled(false) 797 798 job := mock.PeriodicJob() 799 req := structs.JobRegisterRequest{ 800 Job: job, 801 WriteRequest: structs.WriteRequest{ 802 Namespace: job.Namespace, 803 }, 804 } 805 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 806 if err != nil { 807 t.Fatalf("err: %v", err) 808 } 809 810 resp := fsm.Apply(makeLog(buf)) 811 if resp != nil { 812 t.Fatalf("resp: %v", resp) 813 } 814 815 // Verify we are registered 816 ws := memdb.NewWatchSet() 817 jobOut, err := fsm.State().JobByID(ws, req.Namespace, req.Job.ID) 818 if err != nil { 819 t.Fatalf("err: %v", err) 820 } 821 if jobOut == nil { 822 t.Fatalf("not found!") 823 } 824 if jobOut.CreateIndex != 1 { 825 t.Fatalf("bad index: %d", jobOut.CreateIndex) 826 } 827 828 // Verify it wasn't added to the periodic runner. 829 tuple := structs.NamespacedID{ 830 ID: job.ID, 831 Namespace: job.Namespace, 832 } 833 if _, ok := fsm.periodicDispatcher.tracked[tuple]; ok { 834 t.Fatal("job added to periodic runner") 835 } 836 837 // Verify the launch time was tracked. 838 launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Namespace, req.Job.ID) 839 if err != nil { 840 t.Fatalf("err: %v", err) 841 } 842 if launchOut == nil { 843 t.Fatalf("not found!") 844 } 845 if launchOut.Launch.IsZero() { 846 t.Fatalf("bad launch time: %v", launchOut.Launch) 847 } 848 } 849 850 func TestFSM_RegisterJob_BadNamespace(t *testing.T) { 851 ci.Parallel(t) 852 fsm := testFSM(t) 853 854 job := mock.Job() 855 job.Namespace = "foo" 856 req := structs.JobRegisterRequest{ 857 Job: job, 858 WriteRequest: structs.WriteRequest{ 859 Namespace: job.Namespace, 860 }, 861 } 862 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 863 if err != nil { 864 t.Fatalf("err: %v", err) 865 } 866 867 resp := fsm.Apply(makeLog(buf)) 868 if resp == nil { 869 t.Fatalf("no resp: %v", resp) 870 } 871 err, ok := resp.(error) 872 if !ok { 873 t.Fatalf("resp not of error type: %T %v", resp, resp) 874 } 875 if !strings.Contains(err.Error(), "nonexistent namespace") { 876 t.Fatalf("bad error: %v", err) 877 } 878 879 // Verify we are not registered 880 ws := memdb.NewWatchSet() 881 jobOut, err := fsm.State().JobByID(ws, req.Namespace, req.Job.ID) 882 if err != nil { 883 t.Fatalf("err: %v", err) 884 } 885 if jobOut != nil { 886 t.Fatalf("job found!") 887 } 888 } 889 890 func TestFSM_DeregisterJob_Error(t *testing.T) { 891 ci.Parallel(t) 892 fsm := testFSM(t) 893 894 job := mock.Job() 895 896 deregReq := structs.JobDeregisterRequest{ 897 JobID: job.ID, 898 Purge: true, 899 WriteRequest: structs.WriteRequest{ 900 Namespace: job.Namespace, 901 }, 902 } 903 buf, err := structs.Encode(structs.JobDeregisterRequestType, deregReq) 904 require.NoError(t, err) 905 906 resp := fsm.Apply(makeLog(buf)) 907 require.NotNil(t, resp) 908 respErr, ok := resp.(error) 909 require.Truef(t, ok, "expected response to be an error but found: %T", resp) 910 require.Error(t, respErr) 911 } 912 913 func TestFSM_DeregisterJob_Purge(t *testing.T) { 914 ci.Parallel(t) 915 fsm := testFSM(t) 916 917 job := mock.PeriodicJob() 918 req := structs.JobRegisterRequest{ 919 Job: job, 920 WriteRequest: structs.WriteRequest{ 921 Namespace: job.Namespace, 922 }, 923 } 924 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 925 if err != nil { 926 t.Fatalf("err: %v", err) 927 } 928 929 resp := fsm.Apply(makeLog(buf)) 930 if resp != nil { 931 t.Fatalf("resp: %v", resp) 932 } 933 934 req2 := structs.JobDeregisterRequest{ 935 JobID: job.ID, 936 Purge: true, 937 WriteRequest: structs.WriteRequest{ 938 Namespace: job.Namespace, 939 }, 940 } 941 buf, err = structs.Encode(structs.JobDeregisterRequestType, req2) 942 if err != nil { 943 t.Fatalf("err: %v", err) 944 } 945 946 resp = fsm.Apply(makeLog(buf)) 947 if resp != nil { 948 t.Fatalf("resp: %v", resp) 949 } 950 951 // Verify we are NOT registered 952 ws := memdb.NewWatchSet() 953 jobOut, err := fsm.State().JobByID(ws, req.Namespace, req.Job.ID) 954 if err != nil { 955 t.Fatalf("err: %v", err) 956 } 957 if jobOut != nil { 958 t.Fatalf("job found!") 959 } 960 961 // Verify it was removed from the periodic runner. 962 tuple := structs.NamespacedID{ 963 ID: job.ID, 964 Namespace: job.Namespace, 965 } 966 if _, ok := fsm.periodicDispatcher.tracked[tuple]; ok { 967 t.Fatal("job not removed from periodic runner") 968 } 969 970 // Verify it was removed from the periodic launch table. 971 launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Namespace, req.Job.ID) 972 if err != nil { 973 t.Fatalf("err: %v", err) 974 } 975 if launchOut != nil { 976 t.Fatalf("launch found!") 977 } 978 } 979 980 func TestFSM_DeregisterJob_NoPurge(t *testing.T) { 981 ci.Parallel(t) 982 fsm := testFSM(t) 983 984 job := mock.PeriodicJob() 985 req := structs.JobRegisterRequest{ 986 Job: job, 987 WriteRequest: structs.WriteRequest{ 988 Namespace: job.Namespace, 989 }, 990 } 991 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 992 if err != nil { 993 t.Fatalf("err: %v", err) 994 } 995 996 resp := fsm.Apply(makeLog(buf)) 997 if resp != nil { 998 t.Fatalf("resp: %v", resp) 999 } 1000 1001 req2 := structs.JobDeregisterRequest{ 1002 JobID: job.ID, 1003 Purge: false, 1004 WriteRequest: structs.WriteRequest{ 1005 Namespace: job.Namespace, 1006 }, 1007 } 1008 buf, err = structs.Encode(structs.JobDeregisterRequestType, req2) 1009 if err != nil { 1010 t.Fatalf("err: %v", err) 1011 } 1012 1013 resp = fsm.Apply(makeLog(buf)) 1014 if resp != nil { 1015 t.Fatalf("resp: %v", resp) 1016 } 1017 1018 // Verify we are NOT registered 1019 ws := memdb.NewWatchSet() 1020 jobOut, err := fsm.State().JobByID(ws, req.Namespace, req.Job.ID) 1021 if err != nil { 1022 t.Fatalf("err: %v", err) 1023 } 1024 if jobOut == nil { 1025 t.Fatalf("job not found!") 1026 } 1027 if !jobOut.Stop { 1028 t.Fatalf("job not stopped found!") 1029 } 1030 1031 // Verify it was removed from the periodic runner. 1032 tuple := structs.NamespacedID{ 1033 ID: job.ID, 1034 Namespace: job.Namespace, 1035 } 1036 if _, ok := fsm.periodicDispatcher.tracked[tuple]; ok { 1037 t.Fatal("job not removed from periodic runner") 1038 } 1039 1040 // Verify it was removed from the periodic launch table. 1041 launchOut, err := fsm.State().PeriodicLaunchByID(ws, req.Namespace, req.Job.ID) 1042 if err != nil { 1043 t.Fatalf("err: %v", err) 1044 } 1045 if launchOut == nil { 1046 t.Fatalf("launch not found!") 1047 } 1048 } 1049 1050 func TestFSM_BatchDeregisterJob(t *testing.T) { 1051 ci.Parallel(t) 1052 require := require.New(t) 1053 fsm := testFSM(t) 1054 1055 job := mock.PeriodicJob() 1056 req := structs.JobRegisterRequest{ 1057 Job: job, 1058 WriteRequest: structs.WriteRequest{ 1059 Namespace: job.Namespace, 1060 }, 1061 } 1062 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 1063 require.Nil(err) 1064 resp := fsm.Apply(makeLog(buf)) 1065 require.Nil(resp) 1066 1067 job2 := mock.Job() 1068 req2 := structs.JobRegisterRequest{ 1069 Job: job2, 1070 WriteRequest: structs.WriteRequest{ 1071 Namespace: job2.Namespace, 1072 }, 1073 } 1074 1075 buf, err = structs.Encode(structs.JobRegisterRequestType, req2) 1076 require.Nil(err) 1077 resp = fsm.Apply(makeLog(buf)) 1078 require.Nil(resp) 1079 1080 req3 := structs.JobBatchDeregisterRequest{ 1081 Jobs: map[structs.NamespacedID]*structs.JobDeregisterOptions{ 1082 { 1083 ID: job.ID, 1084 Namespace: job.Namespace, 1085 }: {}, 1086 { 1087 ID: job2.ID, 1088 Namespace: job2.Namespace, 1089 }: { 1090 Purge: true, 1091 }, 1092 }, 1093 WriteRequest: structs.WriteRequest{ 1094 Namespace: job.Namespace, 1095 }, 1096 } 1097 buf, err = structs.Encode(structs.JobBatchDeregisterRequestType, req3) 1098 require.Nil(err) 1099 1100 resp = fsm.Apply(makeLog(buf)) 1101 require.Nil(resp) 1102 1103 // Verify we are NOT registered 1104 ws := memdb.NewWatchSet() 1105 jobOut, err := fsm.State().JobByID(ws, req.Namespace, req.Job.ID) 1106 require.Nil(err) 1107 require.NotNil(jobOut) 1108 require.True(jobOut.Stop) 1109 1110 // Verify it was removed from the periodic runner. 1111 tuple := structs.NamespacedID{ 1112 ID: job.ID, 1113 Namespace: job.Namespace, 1114 } 1115 require.NotContains(fsm.periodicDispatcher.tracked, tuple) 1116 1117 // Verify it was not removed from the periodic launch table. 1118 launchOut, err := fsm.State().PeriodicLaunchByID(ws, job.Namespace, job.ID) 1119 require.Nil(err) 1120 require.NotNil(launchOut) 1121 1122 // Verify the other jbo was purged 1123 jobOut2, err := fsm.State().JobByID(ws, job2.Namespace, job2.ID) 1124 require.Nil(err) 1125 require.Nil(jobOut2) 1126 } 1127 1128 func TestFSM_UpdateEval(t *testing.T) { 1129 ci.Parallel(t) 1130 fsm := testFSM(t) 1131 fsm.evalBroker.SetEnabled(true) 1132 1133 req := structs.EvalUpdateRequest{ 1134 Evals: []*structs.Evaluation{mock.Eval()}, 1135 } 1136 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 1137 if err != nil { 1138 t.Fatalf("err: %v", err) 1139 } 1140 1141 resp := fsm.Apply(makeLog(buf)) 1142 if resp != nil { 1143 t.Fatalf("resp: %v", resp) 1144 } 1145 1146 // Verify we are registered 1147 ws := memdb.NewWatchSet() 1148 eval, err := fsm.State().EvalByID(ws, req.Evals[0].ID) 1149 if err != nil { 1150 t.Fatalf("err: %v", err) 1151 } 1152 if eval == nil { 1153 t.Fatalf("not found!") 1154 } 1155 if eval.CreateIndex != 1 { 1156 t.Fatalf("bad index: %d", eval.CreateIndex) 1157 } 1158 1159 // Verify enqueued 1160 stats := fsm.evalBroker.Stats() 1161 if stats.TotalReady != 1 { 1162 t.Fatalf("bad: %#v %#v", stats, eval) 1163 } 1164 } 1165 1166 func TestFSM_UpdateEval_Blocked(t *testing.T) { 1167 ci.Parallel(t) 1168 fsm := testFSM(t) 1169 fsm.evalBroker.SetEnabled(true) 1170 fsm.blockedEvals.SetEnabled(true) 1171 1172 // Create a blocked eval. 1173 eval := mock.Eval() 1174 eval.Status = structs.EvalStatusBlocked 1175 1176 req := structs.EvalUpdateRequest{ 1177 Evals: []*structs.Evaluation{eval}, 1178 } 1179 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 1180 if err != nil { 1181 t.Fatalf("err: %v", err) 1182 } 1183 1184 resp := fsm.Apply(makeLog(buf)) 1185 if resp != nil { 1186 t.Fatalf("resp: %v", resp) 1187 } 1188 1189 // Verify we are registered 1190 ws := memdb.NewWatchSet() 1191 out, err := fsm.State().EvalByID(ws, eval.ID) 1192 if err != nil { 1193 t.Fatalf("err: %v", err) 1194 } 1195 if out == nil { 1196 t.Fatalf("not found!") 1197 } 1198 if out.CreateIndex != 1 { 1199 t.Fatalf("bad index: %d", out.CreateIndex) 1200 } 1201 1202 // Verify the eval wasn't enqueued 1203 stats := fsm.evalBroker.Stats() 1204 if stats.TotalReady != 0 { 1205 t.Fatalf("bad: %#v %#v", stats, out) 1206 } 1207 1208 // Verify the eval was added to the blocked tracker. 1209 bStats := fsm.blockedEvals.Stats() 1210 if bStats.TotalBlocked != 1 { 1211 t.Fatalf("bad: %#v %#v", bStats, out) 1212 } 1213 } 1214 1215 func TestFSM_UpdateEval_Untrack(t *testing.T) { 1216 ci.Parallel(t) 1217 fsm := testFSM(t) 1218 fsm.evalBroker.SetEnabled(true) 1219 fsm.blockedEvals.SetEnabled(true) 1220 1221 // Mark an eval as blocked. 1222 bEval := mock.Eval() 1223 bEval.ClassEligibility = map[string]bool{"v1:123": true} 1224 fsm.blockedEvals.Block(bEval) 1225 1226 // Create a successful eval for the same job 1227 eval := mock.Eval() 1228 eval.JobID = bEval.JobID 1229 eval.Status = structs.EvalStatusComplete 1230 1231 req := structs.EvalUpdateRequest{ 1232 Evals: []*structs.Evaluation{eval}, 1233 } 1234 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 1235 if err != nil { 1236 t.Fatalf("err: %v", err) 1237 } 1238 1239 resp := fsm.Apply(makeLog(buf)) 1240 if resp != nil { 1241 t.Fatalf("resp: %v", resp) 1242 } 1243 1244 // Verify we are registered 1245 ws := memdb.NewWatchSet() 1246 out, err := fsm.State().EvalByID(ws, eval.ID) 1247 if err != nil { 1248 t.Fatalf("err: %v", err) 1249 } 1250 if out == nil { 1251 t.Fatalf("not found!") 1252 } 1253 if out.CreateIndex != 1 { 1254 t.Fatalf("bad index: %d", out.CreateIndex) 1255 } 1256 1257 // Verify the eval wasn't enqueued 1258 stats := fsm.evalBroker.Stats() 1259 if stats.TotalReady != 0 { 1260 t.Fatalf("bad: %#v %#v", stats, out) 1261 } 1262 1263 // Verify the eval was untracked in the blocked tracker. 1264 bStats := fsm.blockedEvals.Stats() 1265 if bStats.TotalBlocked != 0 { 1266 t.Fatalf("bad: %#v %#v", bStats, out) 1267 } 1268 } 1269 1270 func TestFSM_UpdateEval_NoUntrack(t *testing.T) { 1271 ci.Parallel(t) 1272 fsm := testFSM(t) 1273 fsm.evalBroker.SetEnabled(true) 1274 fsm.blockedEvals.SetEnabled(true) 1275 1276 // Mark an eval as blocked. 1277 bEval := mock.Eval() 1278 bEval.ClassEligibility = map[string]bool{"v1:123": true} 1279 fsm.blockedEvals.Block(bEval) 1280 1281 // Create a successful eval for the same job but with placement failures 1282 eval := mock.Eval() 1283 eval.JobID = bEval.JobID 1284 eval.Status = structs.EvalStatusComplete 1285 eval.FailedTGAllocs = make(map[string]*structs.AllocMetric) 1286 eval.FailedTGAllocs["test"] = new(structs.AllocMetric) 1287 1288 req := structs.EvalUpdateRequest{ 1289 Evals: []*structs.Evaluation{eval}, 1290 } 1291 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 1292 if err != nil { 1293 t.Fatalf("err: %v", err) 1294 } 1295 1296 resp := fsm.Apply(makeLog(buf)) 1297 if resp != nil { 1298 t.Fatalf("resp: %v", resp) 1299 } 1300 1301 // Verify we are registered 1302 ws := memdb.NewWatchSet() 1303 out, err := fsm.State().EvalByID(ws, eval.ID) 1304 if err != nil { 1305 t.Fatalf("err: %v", err) 1306 } 1307 if out == nil { 1308 t.Fatalf("not found!") 1309 } 1310 if out.CreateIndex != 1 { 1311 t.Fatalf("bad index: %d", out.CreateIndex) 1312 } 1313 1314 // Verify the eval wasn't enqueued 1315 stats := fsm.evalBroker.Stats() 1316 if stats.TotalReady != 0 { 1317 t.Fatalf("bad: %#v %#v", stats, out) 1318 } 1319 1320 // Verify the eval was not untracked in the blocked tracker. 1321 bStats := fsm.blockedEvals.Stats() 1322 if bStats.TotalBlocked != 1 { 1323 t.Fatalf("bad: %#v %#v", bStats, out) 1324 } 1325 } 1326 1327 func TestFSM_DeleteEval(t *testing.T) { 1328 ci.Parallel(t) 1329 fsm := testFSM(t) 1330 1331 eval := mock.Eval() 1332 req := structs.EvalUpdateRequest{ 1333 Evals: []*structs.Evaluation{eval}, 1334 } 1335 buf, err := structs.Encode(structs.EvalUpdateRequestType, req) 1336 if err != nil { 1337 t.Fatalf("err: %v", err) 1338 } 1339 1340 resp := fsm.Apply(makeLog(buf)) 1341 if resp != nil { 1342 t.Fatalf("resp: %v", resp) 1343 } 1344 1345 req2 := structs.EvalReapRequest{ 1346 Evals: []string{eval.ID}, 1347 } 1348 buf, err = structs.Encode(structs.EvalDeleteRequestType, req2) 1349 if err != nil { 1350 t.Fatalf("err: %v", err) 1351 } 1352 1353 resp = fsm.Apply(makeLog(buf)) 1354 if resp != nil { 1355 t.Fatalf("resp: %v", resp) 1356 } 1357 1358 // Verify we are NOT registered 1359 ws := memdb.NewWatchSet() 1360 eval, err = fsm.State().EvalByID(ws, req.Evals[0].ID) 1361 if err != nil { 1362 t.Fatalf("err: %v", err) 1363 } 1364 if eval != nil { 1365 t.Fatalf("eval found!") 1366 } 1367 } 1368 1369 func TestFSM_UpdateAllocFromClient_Unblock(t *testing.T) { 1370 ci.Parallel(t) 1371 fsm := testFSM(t) 1372 fsm.blockedEvals.SetEnabled(true) 1373 state := fsm.State() 1374 1375 node := mock.Node() 1376 state.UpsertNode(structs.MsgTypeTestSetup, 1, node) 1377 1378 // Mark an eval as blocked. 1379 eval := mock.Eval() 1380 eval.ClassEligibility = map[string]bool{node.ComputedClass: true} 1381 fsm.blockedEvals.Block(eval) 1382 1383 bStats := fsm.blockedEvals.Stats() 1384 if bStats.TotalBlocked != 1 { 1385 t.Fatalf("bad: %#v", bStats) 1386 } 1387 1388 // Create a completed eval 1389 alloc := mock.Alloc() 1390 alloc.NodeID = node.ID 1391 alloc2 := mock.Alloc() 1392 alloc2.NodeID = node.ID 1393 state.UpsertJobSummary(8, mock.JobSummary(alloc.JobID)) 1394 state.UpsertJobSummary(9, mock.JobSummary(alloc2.JobID)) 1395 state.UpsertAllocs(structs.MsgTypeTestSetup, 10, []*structs.Allocation{alloc, alloc2}) 1396 1397 clientAlloc := new(structs.Allocation) 1398 *clientAlloc = *alloc 1399 clientAlloc.ClientStatus = structs.AllocClientStatusComplete 1400 update2 := &structs.Allocation{ 1401 ID: alloc2.ID, 1402 NodeID: alloc2.NodeID, 1403 ClientStatus: structs.AllocClientStatusRunning, 1404 } 1405 1406 req := structs.AllocUpdateRequest{ 1407 Alloc: []*structs.Allocation{clientAlloc, update2}, 1408 } 1409 buf, err := structs.Encode(structs.AllocClientUpdateRequestType, req) 1410 if err != nil { 1411 t.Fatalf("err: %v", err) 1412 } 1413 1414 resp := fsm.Apply(makeLog(buf)) 1415 if resp != nil { 1416 t.Fatalf("resp: %v", resp) 1417 } 1418 1419 // Verify we are updated 1420 ws := memdb.NewWatchSet() 1421 out, err := fsm.State().AllocByID(ws, alloc.ID) 1422 if err != nil { 1423 t.Fatalf("err: %v", err) 1424 } 1425 clientAlloc.CreateIndex = out.CreateIndex 1426 clientAlloc.ModifyIndex = out.ModifyIndex 1427 if !reflect.DeepEqual(clientAlloc, out) { 1428 t.Fatalf("bad: %#v %#v", clientAlloc, out) 1429 } 1430 1431 out, err = fsm.State().AllocByID(ws, alloc2.ID) 1432 if err != nil { 1433 t.Fatalf("err: %v", err) 1434 } 1435 alloc2.CreateIndex = out.CreateIndex 1436 alloc2.ModifyIndex = out.ModifyIndex 1437 alloc2.ClientStatus = structs.AllocClientStatusRunning 1438 alloc2.TaskStates = nil 1439 if !reflect.DeepEqual(alloc2, out) { 1440 t.Fatalf("bad: %#v %#v", alloc2, out) 1441 } 1442 1443 // Verify the eval was unblocked. 1444 testutil.WaitForResult(func() (bool, error) { 1445 bStats = fsm.blockedEvals.Stats() 1446 if bStats.TotalBlocked != 0 { 1447 return false, fmt.Errorf("bad: %#v %#v", bStats, out) 1448 } 1449 return true, nil 1450 }, func(err error) { 1451 t.Fatalf("err: %s", err) 1452 }) 1453 } 1454 1455 func TestFSM_UpdateAllocFromClient(t *testing.T) { 1456 ci.Parallel(t) 1457 fsm := testFSM(t) 1458 state := fsm.State() 1459 require := require.New(t) 1460 1461 alloc := mock.Alloc() 1462 state.UpsertJobSummary(9, mock.JobSummary(alloc.JobID)) 1463 state.UpsertAllocs(structs.MsgTypeTestSetup, 10, []*structs.Allocation{alloc}) 1464 1465 clientAlloc := new(structs.Allocation) 1466 *clientAlloc = *alloc 1467 clientAlloc.ClientStatus = structs.AllocClientStatusFailed 1468 1469 eval := mock.Eval() 1470 eval.JobID = alloc.JobID 1471 eval.TriggeredBy = structs.EvalTriggerRetryFailedAlloc 1472 eval.Type = alloc.Job.Type 1473 1474 req := structs.AllocUpdateRequest{ 1475 Alloc: []*structs.Allocation{clientAlloc}, 1476 Evals: []*structs.Evaluation{eval}, 1477 } 1478 buf, err := structs.Encode(structs.AllocClientUpdateRequestType, req) 1479 require.Nil(err) 1480 1481 resp := fsm.Apply(makeLog(buf)) 1482 require.Nil(resp) 1483 1484 // Verify we are registered 1485 ws := memdb.NewWatchSet() 1486 out, err := fsm.State().AllocByID(ws, alloc.ID) 1487 require.Nil(err) 1488 clientAlloc.CreateIndex = out.CreateIndex 1489 clientAlloc.ModifyIndex = out.ModifyIndex 1490 require.Equal(clientAlloc, out) 1491 1492 // Verify eval was inserted 1493 ws = memdb.NewWatchSet() 1494 evals, err := fsm.State().EvalsByJob(ws, eval.Namespace, eval.JobID) 1495 require.Nil(err) 1496 require.Equal(1, len(evals)) 1497 res := evals[0] 1498 eval.CreateIndex = res.CreateIndex 1499 eval.ModifyIndex = res.ModifyIndex 1500 require.Equal(eval, res) 1501 } 1502 1503 func TestFSM_UpdateAllocDesiredTransition(t *testing.T) { 1504 ci.Parallel(t) 1505 fsm := testFSM(t) 1506 state := fsm.State() 1507 require := require.New(t) 1508 1509 alloc := mock.Alloc() 1510 alloc2 := mock.Alloc() 1511 alloc2.Job = alloc.Job 1512 alloc2.JobID = alloc.JobID 1513 state.UpsertJobSummary(9, mock.JobSummary(alloc.JobID)) 1514 state.UpsertAllocs(structs.MsgTypeTestSetup, 10, []*structs.Allocation{alloc, alloc2}) 1515 1516 t1 := &structs.DesiredTransition{ 1517 Migrate: pointer.Of(true), 1518 } 1519 1520 eval := &structs.Evaluation{ 1521 ID: uuid.Generate(), 1522 Namespace: alloc.Namespace, 1523 Priority: alloc.Job.Priority, 1524 Type: alloc.Job.Type, 1525 TriggeredBy: structs.EvalTriggerNodeDrain, 1526 JobID: alloc.Job.ID, 1527 JobModifyIndex: alloc.Job.ModifyIndex, 1528 Status: structs.EvalStatusPending, 1529 } 1530 req := structs.AllocUpdateDesiredTransitionRequest{ 1531 Allocs: map[string]*structs.DesiredTransition{ 1532 alloc.ID: t1, 1533 alloc2.ID: t1, 1534 }, 1535 Evals: []*structs.Evaluation{eval}, 1536 } 1537 buf, err := structs.Encode(structs.AllocUpdateDesiredTransitionRequestType, req) 1538 require.Nil(err) 1539 1540 resp := fsm.Apply(makeLog(buf)) 1541 require.Nil(resp) 1542 1543 // Verify we are registered 1544 ws := memdb.NewWatchSet() 1545 out1, err := fsm.State().AllocByID(ws, alloc.ID) 1546 require.Nil(err) 1547 out2, err := fsm.State().AllocByID(ws, alloc2.ID) 1548 require.Nil(err) 1549 evalOut, err := fsm.State().EvalByID(ws, eval.ID) 1550 require.Nil(err) 1551 require.NotNil(evalOut) 1552 require.Equal(eval.ID, evalOut.ID) 1553 1554 require.NotNil(out1.DesiredTransition.Migrate) 1555 require.NotNil(out2.DesiredTransition.Migrate) 1556 require.True(*out1.DesiredTransition.Migrate) 1557 require.True(*out2.DesiredTransition.Migrate) 1558 } 1559 1560 func TestFSM_UpsertVaultAccessor(t *testing.T) { 1561 ci.Parallel(t) 1562 fsm := testFSM(t) 1563 fsm.blockedEvals.SetEnabled(true) 1564 1565 va := mock.VaultAccessor() 1566 va2 := mock.VaultAccessor() 1567 req := structs.VaultAccessorsRequest{ 1568 Accessors: []*structs.VaultAccessor{va, va2}, 1569 } 1570 buf, err := structs.Encode(structs.VaultAccessorRegisterRequestType, req) 1571 if err != nil { 1572 t.Fatalf("err: %v", err) 1573 } 1574 1575 resp := fsm.Apply(makeLog(buf)) 1576 if resp != nil { 1577 t.Fatalf("resp: %v", resp) 1578 } 1579 1580 // Verify we are registered 1581 ws := memdb.NewWatchSet() 1582 out1, err := fsm.State().VaultAccessor(ws, va.Accessor) 1583 if err != nil { 1584 t.Fatalf("err: %v", err) 1585 } 1586 if out1 == nil { 1587 t.Fatalf("not found!") 1588 } 1589 if out1.CreateIndex != 1 { 1590 t.Fatalf("bad index: %d", out1.CreateIndex) 1591 } 1592 out2, err := fsm.State().VaultAccessor(ws, va2.Accessor) 1593 if err != nil { 1594 t.Fatalf("err: %v", err) 1595 } 1596 if out2 == nil { 1597 t.Fatalf("not found!") 1598 } 1599 if out1.CreateIndex != 1 { 1600 t.Fatalf("bad index: %d", out2.CreateIndex) 1601 } 1602 1603 tt := fsm.TimeTable() 1604 index := tt.NearestIndex(time.Now().UTC()) 1605 if index != 1 { 1606 t.Fatalf("bad: %d", index) 1607 } 1608 } 1609 1610 func TestFSM_DeregisterVaultAccessor(t *testing.T) { 1611 ci.Parallel(t) 1612 fsm := testFSM(t) 1613 fsm.blockedEvals.SetEnabled(true) 1614 1615 va := mock.VaultAccessor() 1616 va2 := mock.VaultAccessor() 1617 accessors := []*structs.VaultAccessor{va, va2} 1618 1619 // Insert the accessors 1620 if err := fsm.State().UpsertVaultAccessor(1000, accessors); err != nil { 1621 t.Fatalf("bad: %v", err) 1622 } 1623 1624 req := structs.VaultAccessorsRequest{ 1625 Accessors: accessors, 1626 } 1627 buf, err := structs.Encode(structs.VaultAccessorDeregisterRequestType, req) 1628 if err != nil { 1629 t.Fatalf("err: %v", err) 1630 } 1631 1632 resp := fsm.Apply(makeLog(buf)) 1633 if resp != nil { 1634 t.Fatalf("resp: %v", resp) 1635 } 1636 1637 ws := memdb.NewWatchSet() 1638 out1, err := fsm.State().VaultAccessor(ws, va.Accessor) 1639 if err != nil { 1640 t.Fatalf("err: %v", err) 1641 } 1642 if out1 != nil { 1643 t.Fatalf("not deleted!") 1644 } 1645 1646 tt := fsm.TimeTable() 1647 index := tt.NearestIndex(time.Now().UTC()) 1648 if index != 1 { 1649 t.Fatalf("bad: %d", index) 1650 } 1651 } 1652 1653 func TestFSM_UpsertSITokenAccessor(t *testing.T) { 1654 ci.Parallel(t) 1655 r := require.New(t) 1656 1657 fsm := testFSM(t) 1658 fsm.blockedEvals.SetEnabled(true) 1659 1660 a1 := mock.SITokenAccessor() 1661 a2 := mock.SITokenAccessor() 1662 request := structs.SITokenAccessorsRequest{ 1663 Accessors: []*structs.SITokenAccessor{a1, a2}, 1664 } 1665 buf, err := structs.Encode(structs.ServiceIdentityAccessorRegisterRequestType, request) 1666 r.NoError(err) 1667 1668 response := fsm.Apply(makeLog(buf)) 1669 r.Nil(response) 1670 1671 // Verify the accessors got registered 1672 ws := memdb.NewWatchSet() 1673 result1, err := fsm.State().SITokenAccessor(ws, a1.AccessorID) 1674 r.NoError(err) 1675 r.NotNil(result1) 1676 r.Equal(uint64(1), result1.CreateIndex) 1677 1678 result2, err := fsm.State().SITokenAccessor(ws, a2.AccessorID) 1679 r.NoError(err) 1680 r.NotNil(result2) 1681 r.Equal(uint64(1), result2.CreateIndex) 1682 1683 tt := fsm.TimeTable() 1684 latestIndex := tt.NearestIndex(time.Now()) 1685 r.Equal(uint64(1), latestIndex) 1686 } 1687 1688 func TestFSM_DeregisterSITokenAccessor(t *testing.T) { 1689 ci.Parallel(t) 1690 r := require.New(t) 1691 1692 fsm := testFSM(t) 1693 fsm.blockedEvals.SetEnabled(true) 1694 1695 a1 := mock.SITokenAccessor() 1696 a2 := mock.SITokenAccessor() 1697 accessors := []*structs.SITokenAccessor{a1, a2} 1698 var err error 1699 1700 // Insert the accessors 1701 err = fsm.State().UpsertSITokenAccessors(1000, accessors) 1702 r.NoError(err) 1703 1704 request := structs.SITokenAccessorsRequest{Accessors: accessors} 1705 buf, err := structs.Encode(structs.ServiceIdentityAccessorDeregisterRequestType, request) 1706 r.NoError(err) 1707 1708 response := fsm.Apply(makeLog(buf)) 1709 r.Nil(response) 1710 1711 ws := memdb.NewWatchSet() 1712 1713 result1, err := fsm.State().SITokenAccessor(ws, a1.AccessorID) 1714 r.NoError(err) 1715 r.Nil(result1) // should have been deleted 1716 1717 result2, err := fsm.State().SITokenAccessor(ws, a2.AccessorID) 1718 r.NoError(err) 1719 r.Nil(result2) // should have been deleted 1720 1721 tt := fsm.TimeTable() 1722 latestIndex := tt.NearestIndex(time.Now()) 1723 r.Equal(uint64(1), latestIndex) 1724 } 1725 1726 func TestFSM_ApplyPlanResults(t *testing.T) { 1727 ci.Parallel(t) 1728 fsm := testFSM(t) 1729 fsm.evalBroker.SetEnabled(true) 1730 // Create the request and create a deployment 1731 alloc := mock.Alloc() 1732 alloc.Resources = &structs.Resources{} // COMPAT(0.11): Remove in 0.11, used to bypass resource creation in state store 1733 job := alloc.Job 1734 alloc.Job = nil 1735 1736 d := mock.Deployment() 1737 d.JobID = job.ID 1738 d.JobModifyIndex = job.ModifyIndex 1739 d.JobVersion = job.Version 1740 1741 alloc.DeploymentID = d.ID 1742 1743 eval := mock.Eval() 1744 eval.JobID = job.ID 1745 fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 1, []*structs.Evaluation{eval}) 1746 1747 fsm.State().UpsertJobSummary(1, mock.JobSummary(alloc.JobID)) 1748 1749 // set up preempted jobs and allocs 1750 job1 := mock.Job() 1751 job2 := mock.Job() 1752 1753 alloc1 := mock.Alloc() 1754 alloc1.Job = job1 1755 alloc1.JobID = job1.ID 1756 alloc1.PreemptedByAllocation = alloc.ID 1757 1758 alloc2 := mock.Alloc() 1759 alloc2.Job = job2 1760 alloc2.JobID = job2.ID 1761 alloc2.PreemptedByAllocation = alloc.ID 1762 1763 fsm.State().UpsertAllocs(structs.MsgTypeTestSetup, 1, []*structs.Allocation{alloc1, alloc2}) 1764 1765 // evals for preempted jobs 1766 eval1 := mock.Eval() 1767 eval1.JobID = job1.ID 1768 1769 eval2 := mock.Eval() 1770 eval2.JobID = job2.ID 1771 1772 req := structs.ApplyPlanResultsRequest{ 1773 AllocUpdateRequest: structs.AllocUpdateRequest{ 1774 Job: job, 1775 Alloc: []*structs.Allocation{alloc}, 1776 }, 1777 Deployment: d, 1778 EvalID: eval.ID, 1779 NodePreemptions: []*structs.Allocation{alloc1, alloc2}, 1780 PreemptionEvals: []*structs.Evaluation{eval1, eval2}, 1781 } 1782 buf, err := structs.Encode(structs.ApplyPlanResultsRequestType, req) 1783 if err != nil { 1784 t.Fatalf("err: %v", err) 1785 } 1786 1787 resp := fsm.Apply(makeLog(buf)) 1788 if resp != nil { 1789 t.Fatalf("resp: %v", resp) 1790 } 1791 1792 // Verify the allocation is registered 1793 ws := memdb.NewWatchSet() 1794 assert := assert.New(t) 1795 out, err := fsm.State().AllocByID(ws, alloc.ID) 1796 assert.Nil(err) 1797 alloc.CreateIndex = out.CreateIndex 1798 alloc.ModifyIndex = out.ModifyIndex 1799 alloc.AllocModifyIndex = out.AllocModifyIndex 1800 1801 // Job should be re-attached 1802 alloc.Job = job 1803 assert.Equal(alloc, out) 1804 1805 // Verify that evals for preempted jobs have been created 1806 e1, err := fsm.State().EvalByID(ws, eval1.ID) 1807 require := require.New(t) 1808 require.Nil(err) 1809 require.NotNil(e1) 1810 1811 e2, err := fsm.State().EvalByID(ws, eval2.ID) 1812 require.Nil(err) 1813 require.NotNil(e2) 1814 1815 // Verify that eval broker has both evals 1816 _, ok := fsm.evalBroker.evals[e1.ID] 1817 require.True(ok) 1818 1819 _, ok = fsm.evalBroker.evals[e1.ID] 1820 require.True(ok) 1821 1822 dout, err := fsm.State().DeploymentByID(ws, d.ID) 1823 assert.Nil(err) 1824 tg, ok := dout.TaskGroups[alloc.TaskGroup] 1825 assert.True(ok) 1826 assert.NotNil(tg) 1827 assert.Equal(1, tg.PlacedAllocs) 1828 1829 // Ensure that the original job is used 1830 evictAlloc := alloc.Copy() 1831 job = mock.Job() 1832 job.Priority = 123 1833 eval = mock.Eval() 1834 eval.JobID = job.ID 1835 1836 fsm.State().UpsertEvals(structs.MsgTypeTestSetup, 2, []*structs.Evaluation{eval}) 1837 1838 evictAlloc.Job = nil 1839 evictAlloc.DesiredStatus = structs.AllocDesiredStatusEvict 1840 req2 := structs.ApplyPlanResultsRequest{ 1841 AllocUpdateRequest: structs.AllocUpdateRequest{ 1842 Job: job, 1843 Alloc: []*structs.Allocation{evictAlloc}, 1844 }, 1845 EvalID: eval.ID, 1846 } 1847 buf, err = structs.Encode(structs.ApplyPlanResultsRequestType, req2) 1848 assert.Nil(err) 1849 1850 log := makeLog(buf) 1851 //set the index to something other than 1 1852 log.Index = 25 1853 resp = fsm.Apply(log) 1854 assert.Nil(resp) 1855 1856 // Verify we are evicted 1857 out, err = fsm.State().AllocByID(ws, alloc.ID) 1858 assert.Nil(err) 1859 assert.Equal(structs.AllocDesiredStatusEvict, out.DesiredStatus) 1860 assert.NotNil(out.Job) 1861 assert.NotEqual(123, out.Job.Priority) 1862 1863 evalOut, err := fsm.State().EvalByID(ws, eval.ID) 1864 assert.Nil(err) 1865 assert.Equal(log.Index, evalOut.ModifyIndex) 1866 1867 } 1868 1869 func TestFSM_DeploymentStatusUpdate(t *testing.T) { 1870 ci.Parallel(t) 1871 fsm := testFSM(t) 1872 fsm.evalBroker.SetEnabled(true) 1873 state := fsm.State() 1874 1875 // Upsert a deployment 1876 d := mock.Deployment() 1877 if err := state.UpsertDeployment(1, d); err != nil { 1878 t.Fatalf("bad: %v", err) 1879 } 1880 1881 // Create a request to update the deployment, create an eval and job 1882 e := mock.Eval() 1883 j := mock.Job() 1884 status, desc := structs.DeploymentStatusFailed, "foo" 1885 req := &structs.DeploymentStatusUpdateRequest{ 1886 DeploymentUpdate: &structs.DeploymentStatusUpdate{ 1887 DeploymentID: d.ID, 1888 Status: status, 1889 StatusDescription: desc, 1890 }, 1891 Job: j, 1892 Eval: e, 1893 } 1894 buf, err := structs.Encode(structs.DeploymentStatusUpdateRequestType, req) 1895 if err != nil { 1896 t.Fatalf("err: %v", err) 1897 } 1898 resp := fsm.Apply(makeLog(buf)) 1899 if resp != nil { 1900 t.Fatalf("resp: %v", resp) 1901 } 1902 1903 // Check that the status was updated properly 1904 ws := memdb.NewWatchSet() 1905 dout, err := state.DeploymentByID(ws, d.ID) 1906 if err != nil { 1907 t.Fatalf("bad: %v", err) 1908 } 1909 if dout.Status != status || dout.StatusDescription != desc { 1910 t.Fatalf("bad: %#v", dout) 1911 } 1912 1913 // Check that the evaluation was created 1914 eout, _ := state.EvalByID(ws, e.ID) 1915 if err != nil { 1916 t.Fatalf("bad: %v", err) 1917 } 1918 if eout == nil { 1919 t.Fatalf("bad: %#v", eout) 1920 } 1921 1922 // Check that the job was created 1923 jout, _ := state.JobByID(ws, j.Namespace, j.ID) 1924 if err != nil { 1925 t.Fatalf("bad: %v", err) 1926 } 1927 if jout == nil { 1928 t.Fatalf("bad: %#v", jout) 1929 } 1930 1931 // Assert the eval was enqueued 1932 stats := fsm.evalBroker.Stats() 1933 if stats.TotalReady != 1 { 1934 t.Fatalf("bad: %#v %#v", stats, e) 1935 } 1936 } 1937 1938 func TestFSM_JobStabilityUpdate(t *testing.T) { 1939 ci.Parallel(t) 1940 fsm := testFSM(t) 1941 fsm.evalBroker.SetEnabled(true) 1942 state := fsm.State() 1943 1944 // Upsert a deployment 1945 job := mock.Job() 1946 if err := state.UpsertJob(structs.MsgTypeTestSetup, 1, nil, job); err != nil { 1947 t.Fatalf("bad: %v", err) 1948 } 1949 1950 // Create a request to update the job to stable 1951 req := &structs.JobStabilityRequest{ 1952 JobID: job.ID, 1953 JobVersion: job.Version, 1954 Stable: true, 1955 WriteRequest: structs.WriteRequest{ 1956 Namespace: job.Namespace, 1957 }, 1958 } 1959 buf, err := structs.Encode(structs.JobStabilityRequestType, req) 1960 if err != nil { 1961 t.Fatalf("err: %v", err) 1962 } 1963 resp := fsm.Apply(makeLog(buf)) 1964 if resp != nil { 1965 t.Fatalf("resp: %v", resp) 1966 } 1967 1968 // Check that the stability was updated properly 1969 ws := memdb.NewWatchSet() 1970 jout, _ := state.JobByIDAndVersion(ws, job.Namespace, job.ID, job.Version) 1971 if err != nil { 1972 t.Fatalf("bad: %v", err) 1973 } 1974 if jout == nil || !jout.Stable { 1975 t.Fatalf("bad: %#v", jout) 1976 } 1977 } 1978 1979 func TestFSM_DeploymentPromotion(t *testing.T) { 1980 ci.Parallel(t) 1981 fsm := testFSM(t) 1982 fsm.evalBroker.SetEnabled(true) 1983 state := fsm.State() 1984 1985 // Create a job with two task groups 1986 j := mock.Job() 1987 tg1 := j.TaskGroups[0] 1988 tg2 := tg1.Copy() 1989 tg2.Name = "foo" 1990 j.TaskGroups = append(j.TaskGroups, tg2) 1991 if err := state.UpsertJob(structs.MsgTypeTestSetup, 1, nil, j); err != nil { 1992 t.Fatalf("bad: %v", err) 1993 } 1994 1995 // Create a deployment 1996 d := mock.Deployment() 1997 d.JobID = j.ID 1998 d.TaskGroups = map[string]*structs.DeploymentState{ 1999 "web": { 2000 DesiredTotal: 10, 2001 DesiredCanaries: 1, 2002 }, 2003 "foo": { 2004 DesiredTotal: 10, 2005 DesiredCanaries: 1, 2006 }, 2007 } 2008 if err := state.UpsertDeployment(2, d); err != nil { 2009 t.Fatalf("bad: %v", err) 2010 } 2011 2012 // Create a set of allocations 2013 c1 := mock.Alloc() 2014 c1.JobID = j.ID 2015 c1.DeploymentID = d.ID 2016 d.TaskGroups[c1.TaskGroup].PlacedCanaries = append(d.TaskGroups[c1.TaskGroup].PlacedCanaries, c1.ID) 2017 c1.DeploymentStatus = &structs.AllocDeploymentStatus{ 2018 Healthy: pointer.Of(true), 2019 } 2020 c2 := mock.Alloc() 2021 c2.JobID = j.ID 2022 c2.DeploymentID = d.ID 2023 d.TaskGroups[c2.TaskGroup].PlacedCanaries = append(d.TaskGroups[c2.TaskGroup].PlacedCanaries, c2.ID) 2024 c2.TaskGroup = tg2.Name 2025 c2.DeploymentStatus = &structs.AllocDeploymentStatus{ 2026 Healthy: pointer.Of(true), 2027 } 2028 2029 if err := state.UpsertAllocs(structs.MsgTypeTestSetup, 3, []*structs.Allocation{c1, c2}); err != nil { 2030 t.Fatalf("err: %v", err) 2031 } 2032 2033 // Create an eval 2034 e := mock.Eval() 2035 2036 // Promote the canaries 2037 req := &structs.ApplyDeploymentPromoteRequest{ 2038 DeploymentPromoteRequest: structs.DeploymentPromoteRequest{ 2039 DeploymentID: d.ID, 2040 All: true, 2041 }, 2042 Eval: e, 2043 } 2044 buf, err := structs.Encode(structs.DeploymentPromoteRequestType, req) 2045 if err != nil { 2046 t.Fatalf("err: %v", err) 2047 } 2048 resp := fsm.Apply(makeLog(buf)) 2049 if resp != nil { 2050 t.Fatalf("resp: %v", resp) 2051 } 2052 2053 // Check that the status per task group was updated properly 2054 ws := memdb.NewWatchSet() 2055 dout, err := state.DeploymentByID(ws, d.ID) 2056 if err != nil { 2057 t.Fatalf("bad: %v", err) 2058 } 2059 if len(dout.TaskGroups) != 2 { 2060 t.Fatalf("bad: %#v", dout.TaskGroups) 2061 } 2062 for tg, state := range dout.TaskGroups { 2063 if !state.Promoted { 2064 t.Fatalf("bad: group %q not promoted %#v", tg, state) 2065 } 2066 } 2067 2068 // Check that the evaluation was created 2069 eout, _ := state.EvalByID(ws, e.ID) 2070 if err != nil { 2071 t.Fatalf("bad: %v", err) 2072 } 2073 if eout == nil { 2074 t.Fatalf("bad: %#v", eout) 2075 } 2076 2077 // Assert the eval was enqueued 2078 stats := fsm.evalBroker.Stats() 2079 if stats.TotalReady != 1 { 2080 t.Fatalf("bad: %#v %#v", stats, e) 2081 } 2082 } 2083 2084 func TestFSM_DeploymentAllocHealth(t *testing.T) { 2085 ci.Parallel(t) 2086 fsm := testFSM(t) 2087 fsm.evalBroker.SetEnabled(true) 2088 state := fsm.State() 2089 2090 // Insert a deployment 2091 d := mock.Deployment() 2092 if err := state.UpsertDeployment(1, d); err != nil { 2093 t.Fatalf("bad: %v", err) 2094 } 2095 2096 // Insert two allocations 2097 a1 := mock.Alloc() 2098 a1.DeploymentID = d.ID 2099 a2 := mock.Alloc() 2100 a2.DeploymentID = d.ID 2101 if err := state.UpsertAllocs(structs.MsgTypeTestSetup, 2, []*structs.Allocation{a1, a2}); err != nil { 2102 t.Fatalf("bad: %v", err) 2103 } 2104 2105 // Create a job to roll back to 2106 j := mock.Job() 2107 2108 // Create an eval that should be upserted 2109 e := mock.Eval() 2110 2111 // Create a status update for the deployment 2112 status, desc := structs.DeploymentStatusFailed, "foo" 2113 u := &structs.DeploymentStatusUpdate{ 2114 DeploymentID: d.ID, 2115 Status: status, 2116 StatusDescription: desc, 2117 } 2118 2119 // Set health against the deployment 2120 req := &structs.ApplyDeploymentAllocHealthRequest{ 2121 DeploymentAllocHealthRequest: structs.DeploymentAllocHealthRequest{ 2122 DeploymentID: d.ID, 2123 HealthyAllocationIDs: []string{a1.ID}, 2124 UnhealthyAllocationIDs: []string{a2.ID}, 2125 }, 2126 Job: j, 2127 Eval: e, 2128 DeploymentUpdate: u, 2129 } 2130 buf, err := structs.Encode(structs.DeploymentAllocHealthRequestType, req) 2131 if err != nil { 2132 t.Fatalf("err: %v", err) 2133 } 2134 resp := fsm.Apply(makeLog(buf)) 2135 if resp != nil { 2136 t.Fatalf("resp: %v", resp) 2137 } 2138 2139 // Check that the status was updated properly 2140 ws := memdb.NewWatchSet() 2141 dout, err := state.DeploymentByID(ws, d.ID) 2142 if err != nil { 2143 t.Fatalf("bad: %v", err) 2144 } 2145 if dout.Status != status || dout.StatusDescription != desc { 2146 t.Fatalf("bad: %#v", dout) 2147 } 2148 2149 // Check that the evaluation was created 2150 eout, _ := state.EvalByID(ws, e.ID) 2151 if err != nil { 2152 t.Fatalf("bad: %v", err) 2153 } 2154 if eout == nil { 2155 t.Fatalf("bad: %#v", eout) 2156 } 2157 2158 // Check that the job was created 2159 jout, _ := state.JobByID(ws, j.Namespace, j.ID) 2160 if err != nil { 2161 t.Fatalf("bad: %v", err) 2162 } 2163 if jout == nil { 2164 t.Fatalf("bad: %#v", jout) 2165 } 2166 2167 // Check the status of the allocs 2168 out1, err := state.AllocByID(ws, a1.ID) 2169 if err != nil { 2170 t.Fatalf("err: %v", err) 2171 } 2172 out2, err := state.AllocByID(ws, a2.ID) 2173 if err != nil { 2174 t.Fatalf("err: %v", err) 2175 } 2176 2177 if !out1.DeploymentStatus.IsHealthy() { 2178 t.Fatalf("bad: alloc %q not healthy", out1.ID) 2179 } 2180 if !out2.DeploymentStatus.IsUnhealthy() { 2181 t.Fatalf("bad: alloc %q not unhealthy", out2.ID) 2182 } 2183 2184 // Assert the eval was enqueued 2185 stats := fsm.evalBroker.Stats() 2186 if stats.TotalReady != 1 { 2187 t.Fatalf("bad: %#v %#v", stats, e) 2188 } 2189 } 2190 2191 func TestFSM_DeleteDeployment(t *testing.T) { 2192 ci.Parallel(t) 2193 fsm := testFSM(t) 2194 state := fsm.State() 2195 2196 // Upsert a deployments 2197 d := mock.Deployment() 2198 if err := state.UpsertDeployment(1, d); err != nil { 2199 t.Fatalf("bad: %v", err) 2200 } 2201 2202 req := structs.DeploymentDeleteRequest{ 2203 Deployments: []string{d.ID}, 2204 } 2205 buf, err := structs.Encode(structs.DeploymentDeleteRequestType, req) 2206 if err != nil { 2207 t.Fatalf("err: %v", err) 2208 } 2209 2210 resp := fsm.Apply(makeLog(buf)) 2211 if resp != nil { 2212 t.Fatalf("resp: %v", resp) 2213 } 2214 2215 // Verify we are NOT registered 2216 ws := memdb.NewWatchSet() 2217 deployment, err := state.DeploymentByID(ws, d.ID) 2218 if err != nil { 2219 t.Fatalf("err: %v", err) 2220 } 2221 if deployment != nil { 2222 t.Fatalf("deployment found!") 2223 } 2224 } 2225 2226 func TestFSM_UpsertACLPolicies(t *testing.T) { 2227 ci.Parallel(t) 2228 fsm := testFSM(t) 2229 2230 policy := mock.ACLPolicy() 2231 req := structs.ACLPolicyUpsertRequest{ 2232 Policies: []*structs.ACLPolicy{policy}, 2233 } 2234 buf, err := structs.Encode(structs.ACLPolicyUpsertRequestType, req) 2235 if err != nil { 2236 t.Fatalf("err: %v", err) 2237 } 2238 2239 resp := fsm.Apply(makeLog(buf)) 2240 if resp != nil { 2241 t.Fatalf("resp: %v", resp) 2242 } 2243 2244 // Verify we are registered 2245 ws := memdb.NewWatchSet() 2246 out, err := fsm.State().ACLPolicyByName(ws, policy.Name) 2247 assert.Nil(t, err) 2248 assert.NotNil(t, out) 2249 } 2250 2251 func TestFSM_DeleteACLPolicies(t *testing.T) { 2252 ci.Parallel(t) 2253 fsm := testFSM(t) 2254 2255 policy := mock.ACLPolicy() 2256 err := fsm.State().UpsertACLPolicies(structs.MsgTypeTestSetup, 1000, []*structs.ACLPolicy{policy}) 2257 assert.Nil(t, err) 2258 2259 req := structs.ACLPolicyDeleteRequest{ 2260 Names: []string{policy.Name}, 2261 } 2262 buf, err := structs.Encode(structs.ACLPolicyDeleteRequestType, req) 2263 if err != nil { 2264 t.Fatalf("err: %v", err) 2265 } 2266 2267 resp := fsm.Apply(makeLog(buf)) 2268 if resp != nil { 2269 t.Fatalf("resp: %v", resp) 2270 } 2271 2272 // Verify we are NOT registered 2273 ws := memdb.NewWatchSet() 2274 out, err := fsm.State().ACLPolicyByName(ws, policy.Name) 2275 assert.Nil(t, err) 2276 assert.Nil(t, out) 2277 } 2278 2279 func TestFSM_BootstrapACLTokens(t *testing.T) { 2280 ci.Parallel(t) 2281 fsm := testFSM(t) 2282 2283 token := mock.ACLToken() 2284 req := structs.ACLTokenBootstrapRequest{ 2285 Token: token, 2286 } 2287 buf, err := structs.Encode(structs.ACLTokenBootstrapRequestType, req) 2288 if err != nil { 2289 t.Fatalf("err: %v", err) 2290 } 2291 2292 resp := fsm.Apply(makeLog(buf)) 2293 if resp != nil { 2294 t.Fatalf("resp: %v", resp) 2295 } 2296 2297 // Verify we are registered 2298 out, err := fsm.State().ACLTokenByAccessorID(nil, token.AccessorID) 2299 assert.Nil(t, err) 2300 assert.NotNil(t, out) 2301 2302 // Test with reset 2303 token2 := mock.ACLToken() 2304 req = structs.ACLTokenBootstrapRequest{ 2305 Token: token2, 2306 ResetIndex: out.CreateIndex, 2307 } 2308 buf, err = structs.Encode(structs.ACLTokenBootstrapRequestType, req) 2309 if err != nil { 2310 t.Fatalf("err: %v", err) 2311 } 2312 2313 resp = fsm.Apply(makeLog(buf)) 2314 if resp != nil { 2315 t.Fatalf("resp: %v", resp) 2316 } 2317 2318 // Verify we are registered 2319 out2, err := fsm.State().ACLTokenByAccessorID(nil, token2.AccessorID) 2320 assert.Nil(t, err) 2321 assert.NotNil(t, out2) 2322 } 2323 2324 func TestFSM_UpsertACLTokens(t *testing.T) { 2325 ci.Parallel(t) 2326 fsm := testFSM(t) 2327 2328 token := mock.ACLToken() 2329 req := structs.ACLTokenUpsertRequest{ 2330 Tokens: []*structs.ACLToken{token}, 2331 } 2332 buf, err := structs.Encode(structs.ACLTokenUpsertRequestType, req) 2333 if err != nil { 2334 t.Fatalf("err: %v", err) 2335 } 2336 2337 resp := fsm.Apply(makeLog(buf)) 2338 if resp != nil { 2339 t.Fatalf("resp: %v", resp) 2340 } 2341 2342 // Verify we are registered 2343 ws := memdb.NewWatchSet() 2344 out, err := fsm.State().ACLTokenByAccessorID(ws, token.AccessorID) 2345 assert.Nil(t, err) 2346 assert.NotNil(t, out) 2347 } 2348 2349 func TestFSM_DeleteACLTokens(t *testing.T) { 2350 ci.Parallel(t) 2351 fsm := testFSM(t) 2352 2353 token := mock.ACLToken() 2354 err := fsm.State().UpsertACLTokens(structs.MsgTypeTestSetup, 1000, []*structs.ACLToken{token}) 2355 assert.Nil(t, err) 2356 2357 req := structs.ACLTokenDeleteRequest{ 2358 AccessorIDs: []string{token.AccessorID}, 2359 } 2360 buf, err := structs.Encode(structs.ACLTokenDeleteRequestType, req) 2361 if err != nil { 2362 t.Fatalf("err: %v", err) 2363 } 2364 2365 resp := fsm.Apply(makeLog(buf)) 2366 if resp != nil { 2367 t.Fatalf("resp: %v", resp) 2368 } 2369 2370 // Verify we are NOT registered 2371 ws := memdb.NewWatchSet() 2372 out, err := fsm.State().ACLTokenByAccessorID(ws, token.AccessorID) 2373 assert.Nil(t, err) 2374 assert.Nil(t, out) 2375 } 2376 2377 func testSnapshotRestore(t *testing.T, fsm *nomadFSM) *nomadFSM { 2378 // Snapshot 2379 snap, err := fsm.Snapshot() 2380 if err != nil { 2381 t.Fatalf("err: %v", err) 2382 } 2383 defer snap.Release() 2384 2385 // Persist 2386 buf := bytes.NewBuffer(nil) 2387 sink := &MockSink{buf, false} 2388 if err := snap.Persist(sink); err != nil { 2389 t.Fatalf("err: %v", err) 2390 } 2391 2392 // Try to restore on a new FSM 2393 fsm2 := testFSM(t) 2394 snap, err = fsm2.Snapshot() 2395 if err != nil { 2396 t.Fatalf("err: %v", err) 2397 } 2398 defer snap.Release() 2399 2400 abandonCh := fsm2.State().AbandonCh() 2401 2402 // Do a restore 2403 if err := fsm2.Restore(sink); err != nil { 2404 t.Fatalf("err: %v", err) 2405 } 2406 2407 select { 2408 case <-abandonCh: 2409 default: 2410 t.Fatalf("bad") 2411 } 2412 2413 return fsm2 2414 } 2415 2416 func TestFSM_SnapshotRestore_Nodes(t *testing.T) { 2417 ci.Parallel(t) 2418 // Add some state 2419 fsm := testFSM(t) 2420 state := fsm.State() 2421 node := mock.Node() 2422 state.UpsertNode(structs.MsgTypeTestSetup, 1000, node) 2423 2424 // Verify the contents 2425 fsm2 := testSnapshotRestore(t, fsm) 2426 state2 := fsm2.State() 2427 out, _ := state2.NodeByID(nil, node.ID) 2428 if !reflect.DeepEqual(node, out) { 2429 t.Fatalf("bad: \n%#v\n%#v", out, node) 2430 } 2431 } 2432 2433 func TestFSM_SnapshotRestore_NodePools(t *testing.T) { 2434 ci.Parallel(t) 2435 2436 // Add some state 2437 fsm := testFSM(t) 2438 state := fsm.State() 2439 pool := mock.NodePool() 2440 state.UpsertNodePools(structs.MsgTypeTestSetup, 1000, []*structs.NodePool{pool}) 2441 2442 // Verify the contents 2443 fsm2 := testSnapshotRestore(t, fsm) 2444 state2 := fsm2.State() 2445 out, _ := state2.NodePoolByName(nil, pool.Name) 2446 must.Eq(t, pool, out) 2447 } 2448 2449 func TestFSM_SnapshotRestore_Jobs(t *testing.T) { 2450 ci.Parallel(t) 2451 // Add some state 2452 fsm := testFSM(t) 2453 state := fsm.State() 2454 job1 := mock.Job() 2455 state.UpsertJob(structs.MsgTypeTestSetup, 1000, nil, job1) 2456 job2 := mock.Job() 2457 state.UpsertJob(structs.MsgTypeTestSetup, 1001, nil, job2) 2458 2459 // Verify the contents 2460 ws := memdb.NewWatchSet() 2461 fsm2 := testSnapshotRestore(t, fsm) 2462 state2 := fsm2.State() 2463 out1, _ := state2.JobByID(ws, job1.Namespace, job1.ID) 2464 out2, _ := state2.JobByID(ws, job2.Namespace, job2.ID) 2465 if !reflect.DeepEqual(job1, out1) { 2466 t.Fatalf("bad: \n%#v\n%#v", out1, job1) 2467 } 2468 if !reflect.DeepEqual(job2, out2) { 2469 t.Fatalf("bad: \n%#v\n%#v", out2, job2) 2470 } 2471 } 2472 2473 func TestFSM_SnapshotRestore_Evals(t *testing.T) { 2474 ci.Parallel(t) 2475 // Add some state 2476 fsm := testFSM(t) 2477 state := fsm.State() 2478 eval1 := mock.Eval() 2479 state.UpsertEvals(structs.MsgTypeTestSetup, 1000, []*structs.Evaluation{eval1}) 2480 eval2 := mock.Eval() 2481 state.UpsertEvals(structs.MsgTypeTestSetup, 1001, []*structs.Evaluation{eval2}) 2482 2483 // Verify the contents 2484 fsm2 := testSnapshotRestore(t, fsm) 2485 state2 := fsm2.State() 2486 ws := memdb.NewWatchSet() 2487 out1, _ := state2.EvalByID(ws, eval1.ID) 2488 out2, _ := state2.EvalByID(ws, eval2.ID) 2489 if !reflect.DeepEqual(eval1, out1) { 2490 t.Fatalf("bad: \n%#v\n%#v", out1, eval1) 2491 } 2492 if !reflect.DeepEqual(eval2, out2) { 2493 t.Fatalf("bad: \n%#v\n%#v", out2, eval2) 2494 } 2495 } 2496 2497 func TestFSM_SnapshotRestore_Allocs(t *testing.T) { 2498 ci.Parallel(t) 2499 // Add some state 2500 fsm := testFSM(t) 2501 state := fsm.State() 2502 alloc1 := mock.Alloc() 2503 alloc2 := mock.Alloc() 2504 state.UpsertJobSummary(998, mock.JobSummary(alloc1.JobID)) 2505 state.UpsertJobSummary(999, mock.JobSummary(alloc2.JobID)) 2506 state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{alloc1}) 2507 state.UpsertAllocs(structs.MsgTypeTestSetup, 1001, []*structs.Allocation{alloc2}) 2508 2509 // Verify the contents 2510 fsm2 := testSnapshotRestore(t, fsm) 2511 state2 := fsm2.State() 2512 ws := memdb.NewWatchSet() 2513 out1, _ := state2.AllocByID(ws, alloc1.ID) 2514 out2, _ := state2.AllocByID(ws, alloc2.ID) 2515 if !reflect.DeepEqual(alloc1, out1) { 2516 t.Fatalf("bad: \n%#v\n%#v", out1, alloc1) 2517 } 2518 if !reflect.DeepEqual(alloc2, out2) { 2519 t.Fatalf("bad: \n%#v\n%#v", out2, alloc2) 2520 } 2521 } 2522 2523 func TestFSM_SnapshotRestore_Allocs_Canonicalize(t *testing.T) { 2524 ci.Parallel(t) 2525 // Add some state 2526 fsm := testFSM(t) 2527 state := fsm.State() 2528 alloc := mock.Alloc() 2529 2530 // remove old versions to force migration path 2531 alloc.AllocatedResources = nil 2532 2533 state.UpsertJobSummary(998, mock.JobSummary(alloc.JobID)) 2534 state.UpsertAllocs(structs.MsgTypeTestSetup, 1000, []*structs.Allocation{alloc}) 2535 2536 // Verify the contents 2537 fsm2 := testSnapshotRestore(t, fsm) 2538 state2 := fsm2.State() 2539 ws := memdb.NewWatchSet() 2540 out, err := state2.AllocByID(ws, alloc.ID) 2541 require.NoError(t, err) 2542 2543 require.NotNil(t, out.AllocatedResources) 2544 require.Contains(t, out.AllocatedResources.Tasks, "web") 2545 2546 alloc.Canonicalize() 2547 require.Equal(t, alloc, out) 2548 } 2549 2550 func TestFSM_SnapshotRestore_Indexes(t *testing.T) { 2551 ci.Parallel(t) 2552 // Add some state 2553 fsm := testFSM(t) 2554 state := fsm.State() 2555 node1 := mock.Node() 2556 state.UpsertNode(structs.MsgTypeTestSetup, 1000, node1) 2557 2558 // Verify the contents 2559 fsm2 := testSnapshotRestore(t, fsm) 2560 state2 := fsm2.State() 2561 2562 index, err := state2.Index("nodes") 2563 if err != nil { 2564 t.Fatalf("err: %v", err) 2565 } 2566 if index != 1000 { 2567 t.Fatalf("bad: %d", index) 2568 } 2569 } 2570 2571 func TestFSM_SnapshotRestore_TimeTable(t *testing.T) { 2572 ci.Parallel(t) 2573 // Add some state 2574 fsm := testFSM(t) 2575 2576 tt := fsm.TimeTable() 2577 start := time.Now().UTC() 2578 tt.Witness(1000, start) 2579 tt.Witness(2000, start.Add(10*time.Minute)) 2580 2581 // Verify the contents 2582 fsm2 := testSnapshotRestore(t, fsm) 2583 2584 tt2 := fsm2.TimeTable() 2585 if tt2.NearestTime(1500) != start { 2586 t.Fatalf("bad") 2587 } 2588 if tt2.NearestIndex(start.Add(15*time.Minute)) != 2000 { 2589 t.Fatalf("bad") 2590 } 2591 } 2592 2593 func TestFSM_SnapshotRestore_PeriodicLaunches(t *testing.T) { 2594 ci.Parallel(t) 2595 // Add some state 2596 fsm := testFSM(t) 2597 state := fsm.State() 2598 job1 := mock.Job() 2599 launch1 := &structs.PeriodicLaunch{ 2600 ID: job1.ID, 2601 Namespace: job1.Namespace, 2602 Launch: time.Now(), 2603 } 2604 state.UpsertPeriodicLaunch(1000, launch1) 2605 job2 := mock.Job() 2606 launch2 := &structs.PeriodicLaunch{ 2607 ID: job2.ID, 2608 Namespace: job2.Namespace, 2609 Launch: time.Now(), 2610 } 2611 state.UpsertPeriodicLaunch(1001, launch2) 2612 2613 // Verify the contents 2614 fsm2 := testSnapshotRestore(t, fsm) 2615 state2 := fsm2.State() 2616 ws := memdb.NewWatchSet() 2617 out1, _ := state2.PeriodicLaunchByID(ws, launch1.Namespace, launch1.ID) 2618 out2, _ := state2.PeriodicLaunchByID(ws, launch2.Namespace, launch2.ID) 2619 2620 if !cmp.Equal(launch1, out1) { 2621 t.Fatalf("bad: %v", cmp.Diff(launch1, out1)) 2622 } 2623 if !cmp.Equal(launch2, out2) { 2624 t.Fatalf("bad: %v", cmp.Diff(launch2, out2)) 2625 } 2626 } 2627 2628 func TestFSM_SnapshotRestore_JobSummary(t *testing.T) { 2629 ci.Parallel(t) 2630 // Add some state 2631 fsm := testFSM(t) 2632 state := fsm.State() 2633 2634 job1 := mock.Job() 2635 state.UpsertJob(structs.MsgTypeTestSetup, 1000, nil, job1) 2636 ws := memdb.NewWatchSet() 2637 js1, _ := state.JobSummaryByID(ws, job1.Namespace, job1.ID) 2638 2639 job2 := mock.Job() 2640 state.UpsertJob(structs.MsgTypeTestSetup, 1001, nil, job2) 2641 js2, _ := state.JobSummaryByID(ws, job2.Namespace, job2.ID) 2642 2643 // Verify the contents 2644 fsm2 := testSnapshotRestore(t, fsm) 2645 state2 := fsm2.State() 2646 out1, _ := state2.JobSummaryByID(ws, job1.Namespace, job1.ID) 2647 out2, _ := state2.JobSummaryByID(ws, job2.Namespace, job2.ID) 2648 if !reflect.DeepEqual(js1, out1) { 2649 t.Fatalf("bad: \n%#v\n%#v", js1, out1) 2650 } 2651 if !reflect.DeepEqual(js2, out2) { 2652 t.Fatalf("bad: \n%#v\n%#v", js2, out2) 2653 } 2654 } 2655 2656 func TestFSM_SnapshotRestore_VaultAccessors(t *testing.T) { 2657 ci.Parallel(t) 2658 // Add some state 2659 fsm := testFSM(t) 2660 state := fsm.State() 2661 a1 := mock.VaultAccessor() 2662 a2 := mock.VaultAccessor() 2663 state.UpsertVaultAccessor(1000, []*structs.VaultAccessor{a1, a2}) 2664 2665 // Verify the contents 2666 fsm2 := testSnapshotRestore(t, fsm) 2667 state2 := fsm2.State() 2668 ws := memdb.NewWatchSet() 2669 out1, _ := state2.VaultAccessor(ws, a1.Accessor) 2670 out2, _ := state2.VaultAccessor(ws, a2.Accessor) 2671 if !reflect.DeepEqual(a1, out1) { 2672 t.Fatalf("bad: \n%#v\n%#v", out1, a1) 2673 } 2674 if !reflect.DeepEqual(a2, out2) { 2675 t.Fatalf("bad: \n%#v\n%#v", out2, a2) 2676 } 2677 } 2678 2679 func TestFSM_SnapshotRestore_JobVersions(t *testing.T) { 2680 ci.Parallel(t) 2681 // Add some state 2682 fsm := testFSM(t) 2683 state := fsm.State() 2684 job1 := mock.Job() 2685 state.UpsertJob(structs.MsgTypeTestSetup, 1000, nil, job1) 2686 job2 := mock.Job() 2687 job2.ID = job1.ID 2688 state.UpsertJob(structs.MsgTypeTestSetup, 1001, nil, job2) 2689 2690 // Verify the contents 2691 ws := memdb.NewWatchSet() 2692 fsm2 := testSnapshotRestore(t, fsm) 2693 state2 := fsm2.State() 2694 out1, _ := state2.JobByIDAndVersion(ws, job1.Namespace, job1.ID, job1.Version) 2695 out2, _ := state2.JobByIDAndVersion(ws, job2.Namespace, job2.ID, job2.Version) 2696 if !reflect.DeepEqual(job1, out1) { 2697 t.Fatalf("bad: \n%#v\n%#v", out1, job1) 2698 } 2699 if !reflect.DeepEqual(job2, out2) { 2700 t.Fatalf("bad: \n%#v\n%#v", out2, job2) 2701 } 2702 if job2.Version != 1 { 2703 t.Fatalf("bad: \n%#v\n%#v", 1, job2) 2704 } 2705 } 2706 2707 func TestFSM_SnapshotRestore_Deployments(t *testing.T) { 2708 ci.Parallel(t) 2709 // Add some state 2710 fsm := testFSM(t) 2711 state := fsm.State() 2712 d1 := mock.Deployment() 2713 d2 := mock.Deployment() 2714 2715 j := mock.Job() 2716 d1.JobID = j.ID 2717 d2.JobID = j.ID 2718 2719 state.UpsertJob(structs.MsgTypeTestSetup, 999, nil, j) 2720 state.UpsertDeployment(1000, d1) 2721 state.UpsertDeployment(1001, d2) 2722 2723 // Verify the contents 2724 fsm2 := testSnapshotRestore(t, fsm) 2725 state2 := fsm2.State() 2726 ws := memdb.NewWatchSet() 2727 out1, _ := state2.DeploymentByID(ws, d1.ID) 2728 out2, _ := state2.DeploymentByID(ws, d2.ID) 2729 if !reflect.DeepEqual(d1, out1) { 2730 t.Fatalf("bad: \n%#v\n%#v", out1, d1) 2731 } 2732 if !reflect.DeepEqual(d2, out2) { 2733 t.Fatalf("bad: \n%#v\n%#v", out2, d2) 2734 } 2735 } 2736 2737 func TestFSM_SnapshotRestore_ACLPolicy(t *testing.T) { 2738 ci.Parallel(t) 2739 // Add some state 2740 fsm := testFSM(t) 2741 state := fsm.State() 2742 p1 := mock.ACLPolicy() 2743 p2 := mock.ACLPolicy() 2744 state.UpsertACLPolicies(structs.MsgTypeTestSetup, 1000, []*structs.ACLPolicy{p1, p2}) 2745 2746 // Verify the contents 2747 fsm2 := testSnapshotRestore(t, fsm) 2748 state2 := fsm2.State() 2749 ws := memdb.NewWatchSet() 2750 out1, _ := state2.ACLPolicyByName(ws, p1.Name) 2751 out2, _ := state2.ACLPolicyByName(ws, p2.Name) 2752 assert.Equal(t, p1, out1) 2753 assert.Equal(t, p2, out2) 2754 } 2755 2756 func TestFSM_SnapshotRestore_ACLTokens(t *testing.T) { 2757 ci.Parallel(t) 2758 // Add some state 2759 fsm := testFSM(t) 2760 state := fsm.State() 2761 tk1 := mock.ACLToken() 2762 tk2 := mock.ACLToken() 2763 state.UpsertACLTokens(structs.MsgTypeTestSetup, 1000, []*structs.ACLToken{tk1, tk2}) 2764 2765 // Verify the contents 2766 fsm2 := testSnapshotRestore(t, fsm) 2767 state2 := fsm2.State() 2768 ws := memdb.NewWatchSet() 2769 out1, _ := state2.ACLTokenByAccessorID(ws, tk1.AccessorID) 2770 out2, _ := state2.ACLTokenByAccessorID(ws, tk2.AccessorID) 2771 assert.Equal(t, tk1, out1) 2772 assert.Equal(t, tk2, out2) 2773 } 2774 2775 func TestFSM_SnapshotRestore_SchedulerConfiguration(t *testing.T) { 2776 ci.Parallel(t) 2777 // Add some state 2778 fsm := testFSM(t) 2779 state := fsm.State() 2780 schedConfig := &structs.SchedulerConfiguration{ 2781 SchedulerAlgorithm: "spread", 2782 PreemptionConfig: structs.PreemptionConfig{ 2783 SystemSchedulerEnabled: true, 2784 }, 2785 } 2786 state.SchedulerSetConfig(1000, schedConfig) 2787 2788 // Verify the contents 2789 require := require.New(t) 2790 fsm2 := testSnapshotRestore(t, fsm) 2791 state2 := fsm2.State() 2792 index, out, err := state2.SchedulerConfig() 2793 require.Nil(err) 2794 require.EqualValues(1000, index) 2795 require.Equal(schedConfig, out) 2796 } 2797 2798 func TestFSM_SnapshotRestore_ClusterMetadata(t *testing.T) { 2799 ci.Parallel(t) 2800 2801 fsm := testFSM(t) 2802 state := fsm.State() 2803 clusterID := "12345678-1234-1234-1234-1234567890" 2804 now := time.Now().UnixNano() 2805 meta := &structs.ClusterMetadata{ClusterID: clusterID, CreateTime: now} 2806 state.ClusterSetMetadata(1000, meta) 2807 2808 // Verify the contents 2809 require := require.New(t) 2810 fsm2 := testSnapshotRestore(t, fsm) 2811 state2 := fsm2.State() 2812 out, err := state2.ClusterMetadata(memdb.NewWatchSet()) 2813 require.NoError(err) 2814 require.Equal(clusterID, out.ClusterID) 2815 } 2816 2817 func TestFSM_SnapshotRestore_ServiceRegistrations(t *testing.T) { 2818 ci.Parallel(t) 2819 2820 // Create our initial FSM which will be snapshotted. 2821 fsm := testFSM(t) 2822 testState := fsm.State() 2823 2824 // Generate and upsert some service registrations. 2825 serviceRegs := mock.ServiceRegistrations() 2826 require.NoError(t, testState.UpsertServiceRegistrations(structs.MsgTypeTestSetup, 10, serviceRegs)) 2827 2828 // Perform a snapshot restore. 2829 restoredFSM := testSnapshotRestore(t, fsm) 2830 restoredState := restoredFSM.State() 2831 2832 // List the service registrations from restored state and ensure everything 2833 // is as expected. 2834 iter, err := restoredState.GetServiceRegistrations(memdb.NewWatchSet()) 2835 require.NoError(t, err) 2836 2837 var restoredRegs []*structs.ServiceRegistration 2838 2839 for raw := iter.Next(); raw != nil; raw = iter.Next() { 2840 restoredRegs = append(restoredRegs, raw.(*structs.ServiceRegistration)) 2841 } 2842 require.ElementsMatch(t, restoredRegs, serviceRegs) 2843 } 2844 2845 func TestFSM_SnapshotRestore_ACLRoles(t *testing.T) { 2846 ci.Parallel(t) 2847 2848 // Create our initial FSM which will be snapshotted. 2849 fsm := testFSM(t) 2850 testState := fsm.State() 2851 2852 // Create the policies our ACL roles wants to link to. 2853 policy1 := mock.ACLPolicy() 2854 policy1.Name = "mocked-test-policy-1" 2855 policy2 := mock.ACLPolicy() 2856 policy2.Name = "mocked-test-policy-2" 2857 2858 require.NoError(t, testState.UpsertACLPolicies( 2859 structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2})) 2860 2861 // Generate and upsert some ACL roles. 2862 aclRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()} 2863 require.NoError(t, testState.UpsertACLRoles(structs.MsgTypeTestSetup, 10, aclRoles, false)) 2864 2865 // Perform a snapshot restore. 2866 restoredFSM := testSnapshotRestore(t, fsm) 2867 restoredState := restoredFSM.State() 2868 2869 // List the ACL roles from restored state and ensure everything is as 2870 // expected. 2871 iter, err := restoredState.GetACLRoles(memdb.NewWatchSet()) 2872 require.NoError(t, err) 2873 2874 var restoredACLRoles []*structs.ACLRole 2875 2876 for raw := iter.Next(); raw != nil; raw = iter.Next() { 2877 restoredACLRoles = append(restoredACLRoles, raw.(*structs.ACLRole)) 2878 } 2879 require.ElementsMatch(t, restoredACLRoles, aclRoles) 2880 } 2881 2882 func TestFSM_SnapshotRestore_ACLAuthMethods(t *testing.T) { 2883 ci.Parallel(t) 2884 2885 // Create our initial FSM which will be snapshotted. 2886 fsm := testFSM(t) 2887 testState := fsm.State() 2888 2889 // Generate and upsert some ACL auth methods. 2890 authMethods := []*structs.ACLAuthMethod{mock.ACLOIDCAuthMethod(), mock.ACLOIDCAuthMethod()} 2891 must.NoError(t, testState.UpsertACLAuthMethods(10, authMethods)) 2892 2893 // Perform a snapshot restore. 2894 restoredFSM := testSnapshotRestore(t, fsm) 2895 restoredState := restoredFSM.State() 2896 2897 // List the ACL auth methods from restored state and ensure everything is as 2898 // expected. 2899 iter, err := restoredState.GetACLAuthMethods(memdb.NewWatchSet()) 2900 must.NoError(t, err) 2901 2902 var restoredACLAuthMethods []*structs.ACLAuthMethod 2903 for raw := iter.Next(); raw != nil; raw = iter.Next() { 2904 restoredACLAuthMethods = append(restoredACLAuthMethods, raw.(*structs.ACLAuthMethod)) 2905 } 2906 must.SliceContainsAll(t, restoredACLAuthMethods, authMethods) 2907 } 2908 2909 func TestFSM_SnapshotRestore_ACLBindingRules(t *testing.T) { 2910 ci.Parallel(t) 2911 2912 // Create our initial FSM which will be snapshotted. 2913 fsm := testFSM(t) 2914 testState := fsm.State() 2915 2916 // Generate a some mocked ACL binding rules for testing and upsert these 2917 // straight into state. 2918 mockedACLBindingRoles := []*structs.ACLBindingRule{mock.ACLBindingRule(), mock.ACLBindingRule()} 2919 must.NoError(t, testState.UpsertACLBindingRules(10, mockedACLBindingRoles, true)) 2920 2921 // Perform a snapshot restore. 2922 restoredFSM := testSnapshotRestore(t, fsm) 2923 restoredState := restoredFSM.State() 2924 2925 // List the ACL binding rules from restored state and ensure everything is 2926 // as expected. 2927 iter, err := restoredState.GetACLBindingRules(memdb.NewWatchSet()) 2928 must.NoError(t, err) 2929 2930 var restoredACLBindingRules []*structs.ACLBindingRule 2931 2932 for raw := iter.Next(); raw != nil; raw = iter.Next() { 2933 restoredACLBindingRules = append(restoredACLBindingRules, raw.(*structs.ACLBindingRule)) 2934 } 2935 must.SliceContainsAll(t, restoredACLBindingRules, mockedACLBindingRoles) 2936 } 2937 2938 func TestFSM_ReconcileSummaries(t *testing.T) { 2939 ci.Parallel(t) 2940 // Add some state 2941 fsm := testFSM(t) 2942 state := fsm.State() 2943 2944 // Add a node 2945 node := mock.Node() 2946 require.NoError(t, state.UpsertNode(structs.MsgTypeTestSetup, 800, node)) 2947 2948 // Make a job so that none of the tasks can be placed 2949 job1 := mock.Job() 2950 job1.TaskGroups[0].Tasks[0].Resources.CPU = 5000 2951 require.NoError(t, state.UpsertJob(structs.MsgTypeTestSetup, 1000, nil, job1)) 2952 2953 // make a job which can make partial progress 2954 alloc := mock.Alloc() 2955 alloc.NodeID = node.ID 2956 require.NoError(t, state.UpsertJob(structs.MsgTypeTestSetup, 1010, nil, alloc.Job)) 2957 require.NoError(t, state.UpsertAllocs(structs.MsgTypeTestSetup, 1011, []*structs.Allocation{alloc})) 2958 2959 // Delete the summaries 2960 require.NoError(t, state.DeleteJobSummary(1030, job1.Namespace, job1.ID)) 2961 require.NoError(t, state.DeleteJobSummary(1040, alloc.Namespace, alloc.Job.ID)) 2962 2963 req := structs.GenericRequest{} 2964 buf, err := structs.Encode(structs.ReconcileJobSummariesRequestType, req) 2965 if err != nil { 2966 t.Fatalf("err: %v", err) 2967 } 2968 2969 resp := fsm.Apply(makeLog(buf)) 2970 if resp != nil { 2971 t.Fatalf("resp: %v", resp) 2972 } 2973 2974 ws := memdb.NewWatchSet() 2975 out1, err := state.JobSummaryByID(ws, job1.Namespace, job1.ID) 2976 require.NoError(t, err) 2977 2978 expected := structs.JobSummary{ 2979 JobID: job1.ID, 2980 Namespace: job1.Namespace, 2981 Summary: map[string]structs.TaskGroupSummary{ 2982 "web": { 2983 Queued: 10, 2984 }, 2985 }, 2986 CreateIndex: 1000, 2987 ModifyIndex: out1.ModifyIndex, 2988 } 2989 if !reflect.DeepEqual(&expected, out1) { 2990 t.Fatalf("expected: %#v, actual: %#v", &expected, out1) 2991 } 2992 2993 // This exercises the code path which adds the allocations made by the 2994 // planner and the number of unplaced allocations in the reconcile summaries 2995 // codepath 2996 out2, err := state.JobSummaryByID(ws, alloc.Namespace, alloc.Job.ID) 2997 require.NoError(t, err) 2998 2999 expected = structs.JobSummary{ 3000 JobID: alloc.Job.ID, 3001 Namespace: alloc.Job.Namespace, 3002 Summary: map[string]structs.TaskGroupSummary{ 3003 "web": { 3004 Queued: 9, 3005 Starting: 1, 3006 }, 3007 }, 3008 CreateIndex: 1010, 3009 ModifyIndex: out2.ModifyIndex, 3010 } 3011 if !reflect.DeepEqual(&expected, out2) { 3012 t.Fatalf("Diff % #v", pretty.Diff(&expected, out2)) 3013 } 3014 } 3015 3016 // COMPAT: Remove in 0.11 3017 func TestFSM_ReconcileParentJobSummary(t *testing.T) { 3018 // This test exercises code to handle https://github.com/hernad/nomad/issues/3886 3019 ci.Parallel(t) 3020 3021 require := require.New(t) 3022 // Add some state 3023 fsm := testFSM(t) 3024 state := fsm.State() 3025 3026 // Add a node 3027 node := mock.Node() 3028 state.UpsertNode(structs.MsgTypeTestSetup, 800, node) 3029 3030 // Make a parameterized job 3031 job1 := mock.BatchJob() 3032 job1.ID = "test" 3033 job1.ParameterizedJob = &structs.ParameterizedJobConfig{ 3034 Payload: "random", 3035 } 3036 job1.TaskGroups[0].Count = 1 3037 state.UpsertJob(structs.MsgTypeTestSetup, 1000, nil, job1) 3038 3039 // Make a child job 3040 childJob := job1.Copy() 3041 childJob.ID = job1.ID + "dispatch-23423423" 3042 childJob.ParentID = job1.ID 3043 childJob.Dispatched = true 3044 childJob.Status = structs.JobStatusRunning 3045 3046 // Create an alloc for child job 3047 alloc := mock.Alloc() 3048 alloc.NodeID = node.ID 3049 alloc.Job = childJob 3050 alloc.JobID = childJob.ID 3051 alloc.ClientStatus = structs.AllocClientStatusRunning 3052 3053 state.UpsertJob(structs.MsgTypeTestSetup, 1010, nil, childJob) 3054 state.UpsertAllocs(structs.MsgTypeTestSetup, 1011, []*structs.Allocation{alloc}) 3055 3056 // Make the summary incorrect in the state store 3057 summary, err := state.JobSummaryByID(nil, job1.Namespace, job1.ID) 3058 require.Nil(err) 3059 3060 summary.Children = nil 3061 summary.Summary = make(map[string]structs.TaskGroupSummary) 3062 summary.Summary["web"] = structs.TaskGroupSummary{ 3063 Queued: 1, 3064 } 3065 3066 req := structs.GenericRequest{} 3067 buf, err := structs.Encode(structs.ReconcileJobSummariesRequestType, req) 3068 require.Nil(err) 3069 3070 resp := fsm.Apply(makeLog(buf)) 3071 require.Nil(resp) 3072 3073 ws := memdb.NewWatchSet() 3074 out1, _ := state.JobSummaryByID(ws, job1.Namespace, job1.ID) 3075 expected := structs.JobSummary{ 3076 JobID: job1.ID, 3077 Namespace: job1.Namespace, 3078 Summary: make(map[string]structs.TaskGroupSummary), 3079 CreateIndex: 1000, 3080 ModifyIndex: out1.ModifyIndex, 3081 Children: &structs.JobChildrenSummary{ 3082 Running: 1, 3083 }, 3084 } 3085 require.Equal(&expected, out1) 3086 } 3087 3088 func TestFSM_LeakedDeployments(t *testing.T) { 3089 ci.Parallel(t) 3090 require := require.New(t) 3091 3092 // Add some state 3093 fsm := testFSM(t) 3094 state := fsm.State() 3095 d := mock.Deployment() 3096 require.NoError(state.UpsertDeployment(1000, d)) 3097 3098 // Verify the contents 3099 fsm2 := testSnapshotRestore(t, fsm) 3100 state2 := fsm2.State() 3101 out, _ := state2.DeploymentByID(nil, d.ID) 3102 require.NotNil(out) 3103 require.Equal(structs.DeploymentStatusCancelled, out.Status) 3104 } 3105 3106 func TestFSM_Autopilot(t *testing.T) { 3107 ci.Parallel(t) 3108 fsm := testFSM(t) 3109 3110 // Set the autopilot config using a request. 3111 req := structs.AutopilotSetConfigRequest{ 3112 Datacenter: "dc1", 3113 Config: structs.AutopilotConfig{ 3114 CleanupDeadServers: true, 3115 LastContactThreshold: 10 * time.Second, 3116 MaxTrailingLogs: 300, 3117 MinQuorum: 3, 3118 }, 3119 } 3120 buf, err := structs.Encode(structs.AutopilotRequestType, req) 3121 if err != nil { 3122 t.Fatalf("err: %v", err) 3123 } 3124 resp := fsm.Apply(makeLog(buf)) 3125 if _, ok := resp.(error); ok { 3126 t.Fatalf("bad: %v", resp) 3127 } 3128 3129 // Verify key is set directly in the state store. 3130 _, config, err := fsm.state.AutopilotConfig() 3131 if err != nil { 3132 t.Fatalf("err: %v", err) 3133 } 3134 if config.CleanupDeadServers != req.Config.CleanupDeadServers { 3135 t.Fatalf("bad: %v", config.CleanupDeadServers) 3136 } 3137 if config.LastContactThreshold != req.Config.LastContactThreshold { 3138 t.Fatalf("bad: %v", config.LastContactThreshold) 3139 } 3140 if config.MaxTrailingLogs != req.Config.MaxTrailingLogs { 3141 t.Fatalf("bad: %v", config.MaxTrailingLogs) 3142 } 3143 if config.MinQuorum != req.Config.MinQuorum { 3144 t.Fatalf("bad: %v", config.MinQuorum) 3145 } 3146 3147 // Now use CAS and provide an old index 3148 req.CAS = true 3149 req.Config.CleanupDeadServers = false 3150 req.Config.ModifyIndex = config.ModifyIndex - 1 3151 buf, err = structs.Encode(structs.AutopilotRequestType, req) 3152 if err != nil { 3153 t.Fatalf("err: %v", err) 3154 } 3155 resp = fsm.Apply(makeLog(buf)) 3156 if _, ok := resp.(error); ok { 3157 t.Fatalf("bad: %v", resp) 3158 } 3159 3160 _, config, err = fsm.state.AutopilotConfig() 3161 if err != nil { 3162 t.Fatalf("err: %v", err) 3163 } 3164 if !config.CleanupDeadServers { 3165 t.Fatalf("bad: %v", config.CleanupDeadServers) 3166 } 3167 } 3168 3169 func TestFSM_SchedulerConfig(t *testing.T) { 3170 ci.Parallel(t) 3171 fsm := testFSM(t) 3172 3173 require := require.New(t) 3174 3175 // Set the scheduler config using a request. 3176 req := structs.SchedulerSetConfigRequest{ 3177 Config: structs.SchedulerConfiguration{ 3178 PreemptionConfig: structs.PreemptionConfig{ 3179 SystemSchedulerEnabled: true, 3180 BatchSchedulerEnabled: true, 3181 }, 3182 }, 3183 } 3184 buf, err := structs.Encode(structs.SchedulerConfigRequestType, req) 3185 require.Nil(err) 3186 3187 resp := fsm.Apply(makeLog(buf)) 3188 if _, ok := resp.(error); ok { 3189 t.Fatalf("bad: %v", resp) 3190 } 3191 3192 // Verify key is set directly in the state store. 3193 _, config, err := fsm.state.SchedulerConfig() 3194 require.Nil(err) 3195 3196 require.Equal(config.PreemptionConfig.SystemSchedulerEnabled, req.Config.PreemptionConfig.SystemSchedulerEnabled) 3197 require.Equal(config.PreemptionConfig.BatchSchedulerEnabled, req.Config.PreemptionConfig.BatchSchedulerEnabled) 3198 3199 // Now use CAS and provide an old index 3200 req.CAS = true 3201 req.Config.PreemptionConfig = structs.PreemptionConfig{SystemSchedulerEnabled: false, BatchSchedulerEnabled: false} 3202 req.Config.ModifyIndex = config.ModifyIndex - 1 3203 buf, err = structs.Encode(structs.SchedulerConfigRequestType, req) 3204 require.Nil(err) 3205 3206 resp = fsm.Apply(makeLog(buf)) 3207 if _, ok := resp.(error); ok { 3208 t.Fatalf("bad: %v", resp) 3209 } 3210 3211 _, config, err = fsm.state.SchedulerConfig() 3212 require.Nil(err) 3213 // Verify that preemption is still enabled 3214 require.True(config.PreemptionConfig.SystemSchedulerEnabled) 3215 require.True(config.PreemptionConfig.BatchSchedulerEnabled) 3216 } 3217 3218 func TestFSM_ClusterMetadata(t *testing.T) { 3219 ci.Parallel(t) 3220 r := require.New(t) 3221 3222 fsm := testFSM(t) 3223 clusterID := "12345678-1234-1234-1234-1234567890" 3224 now := time.Now().UnixNano() 3225 meta := structs.ClusterMetadata{ 3226 ClusterID: clusterID, 3227 CreateTime: now, 3228 } 3229 buf, err := structs.Encode(structs.ClusterMetadataRequestType, meta) 3230 r.NoError(err) 3231 3232 result := fsm.Apply(makeLog(buf)) 3233 r.Nil(result) 3234 3235 // Verify the clusterID is set directly in the state store 3236 ws := memdb.NewWatchSet() 3237 storedMetadata, err := fsm.state.ClusterMetadata(ws) 3238 r.NoError(err) 3239 r.Equal(clusterID, storedMetadata.ClusterID) 3240 3241 // Assert cluster ID cannot be overwritten and is not regenerated 3242 erroneous := structs.ClusterMetadata{ 3243 ClusterID: "99999999-9999-9999-9999-9999999999", 3244 } 3245 buf, err = structs.Encode(structs.ClusterMetadataRequestType, erroneous) 3246 r.NoError(err) 3247 3248 result = fsm.Apply(makeLog(buf)) 3249 r.Error(result.(error)) 3250 3251 storedMetadata, err = fsm.state.ClusterMetadata(ws) 3252 r.NoError(err) 3253 r.Equal(clusterID, storedMetadata.ClusterID) 3254 r.Equal(now, storedMetadata.CreateTime) 3255 } 3256 3257 func TestFSM_UpsertNamespaces(t *testing.T) { 3258 assert := assert.New(t) 3259 ci.Parallel(t) 3260 fsm := testFSM(t) 3261 3262 ns1 := mock.Namespace() 3263 ns2 := mock.Namespace() 3264 req := structs.NamespaceUpsertRequest{ 3265 Namespaces: []*structs.Namespace{ns1, ns2}, 3266 } 3267 buf, err := structs.Encode(structs.NamespaceUpsertRequestType, req) 3268 assert.Nil(err) 3269 assert.Nil(fsm.Apply(makeLog(buf))) 3270 3271 // Verify we are registered 3272 ws := memdb.NewWatchSet() 3273 out, err := fsm.State().NamespaceByName(ws, ns1.Name) 3274 assert.Nil(err) 3275 assert.NotNil(out) 3276 3277 out, err = fsm.State().NamespaceByName(ws, ns2.Name) 3278 assert.Nil(err) 3279 assert.NotNil(out) 3280 } 3281 3282 func TestFSM_DeleteNamespaces(t *testing.T) { 3283 assert := assert.New(t) 3284 ci.Parallel(t) 3285 fsm := testFSM(t) 3286 3287 ns1 := mock.Namespace() 3288 ns2 := mock.Namespace() 3289 assert.Nil(fsm.State().UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2})) 3290 3291 req := structs.NamespaceDeleteRequest{ 3292 Namespaces: []string{ns1.Name, ns2.Name}, 3293 } 3294 buf, err := structs.Encode(structs.NamespaceDeleteRequestType, req) 3295 assert.Nil(err) 3296 assert.Nil(fsm.Apply(makeLog(buf))) 3297 3298 // Verify we are NOT registered 3299 ws := memdb.NewWatchSet() 3300 out, err := fsm.State().NamespaceByName(ws, ns1.Name) 3301 assert.Nil(err) 3302 assert.Nil(out) 3303 3304 out, err = fsm.State().NamespaceByName(ws, ns2.Name) 3305 assert.Nil(err) 3306 assert.Nil(out) 3307 } 3308 3309 func TestFSM_SnapshotRestore_Namespaces(t *testing.T) { 3310 ci.Parallel(t) 3311 // Add some state 3312 fsm := testFSM(t) 3313 state := fsm.State() 3314 ns1 := mock.Namespace() 3315 ns2 := mock.Namespace() 3316 state.UpsertNamespaces(1000, []*structs.Namespace{ns1, ns2}) 3317 3318 // Verify the contents 3319 fsm2 := testSnapshotRestore(t, fsm) 3320 state2 := fsm2.State() 3321 ws := memdb.NewWatchSet() 3322 out1, _ := state2.NamespaceByName(ws, ns1.Name) 3323 out2, _ := state2.NamespaceByName(ws, ns2.Name) 3324 if !reflect.DeepEqual(ns1, out1) { 3325 t.Fatalf("bad: \n%#v\n%#v", out1, ns1) 3326 } 3327 if !reflect.DeepEqual(ns2, out2) { 3328 t.Fatalf("bad: \n%#v\n%#v", out2, ns2) 3329 } 3330 } 3331 3332 func TestFSM_UpsertServiceRegistrations(t *testing.T) { 3333 ci.Parallel(t) 3334 fsm := testFSM(t) 3335 3336 // Generate our test service registrations. 3337 services := mock.ServiceRegistrations() 3338 3339 // Build and apply our message. 3340 req := structs.ServiceRegistrationUpsertRequest{Services: services} 3341 buf, err := structs.Encode(structs.ServiceRegistrationUpsertRequestType, req) 3342 assert.Nil(t, err) 3343 assert.Nil(t, fsm.Apply(makeLog(buf))) 3344 3345 // Check that both services are found within state. 3346 ws := memdb.NewWatchSet() 3347 out, err := fsm.State().GetServiceRegistrationByID(ws, services[0].Namespace, services[0].ID) 3348 assert.Nil(t, err) 3349 assert.NotNil(t, out) 3350 3351 out, err = fsm.State().GetServiceRegistrationByID(ws, services[1].Namespace, services[1].ID) 3352 assert.Nil(t, err) 3353 assert.NotNil(t, out) 3354 } 3355 3356 func TestFSM_DeleteServiceRegistrationsByID(t *testing.T) { 3357 ci.Parallel(t) 3358 fsm := testFSM(t) 3359 3360 // Generate our test service registrations. 3361 services := mock.ServiceRegistrations() 3362 3363 // Upsert the services. 3364 assert.NoError(t, fsm.State().UpsertServiceRegistrations(structs.MsgTypeTestSetup, uint64(10), services)) 3365 3366 // Build and apply our message. 3367 req := structs.ServiceRegistrationDeleteByIDRequest{ID: services[0].ID} 3368 buf, err := structs.Encode(structs.ServiceRegistrationDeleteByIDRequestType, req) 3369 assert.Nil(t, err) 3370 assert.Nil(t, fsm.Apply(makeLog(buf))) 3371 3372 // Check that the service has been deleted, whilst the other is still 3373 // available. 3374 ws := memdb.NewWatchSet() 3375 out, err := fsm.State().GetServiceRegistrationByID(ws, services[0].Namespace, services[0].ID) 3376 assert.Nil(t, err) 3377 assert.Nil(t, out) 3378 3379 out, err = fsm.State().GetServiceRegistrationByID(ws, services[1].Namespace, services[1].ID) 3380 assert.Nil(t, err) 3381 assert.NotNil(t, out) 3382 } 3383 3384 func TestFSM_DeleteServiceRegistrationsByNodeID(t *testing.T) { 3385 ci.Parallel(t) 3386 fsm := testFSM(t) 3387 3388 // Generate our test service registrations. Set them both to have the same 3389 // node ID. 3390 services := mock.ServiceRegistrations() 3391 services[1].NodeID = services[0].NodeID 3392 3393 // Upsert the services. 3394 assert.NoError(t, fsm.State().UpsertServiceRegistrations(structs.MsgTypeTestSetup, uint64(10), services)) 3395 3396 // Build and apply our message. 3397 req := structs.ServiceRegistrationDeleteByNodeIDRequest{NodeID: services[0].NodeID} 3398 buf, err := structs.Encode(structs.ServiceRegistrationDeleteByNodeIDRequestType, req) 3399 assert.Nil(t, err) 3400 assert.Nil(t, fsm.Apply(makeLog(buf))) 3401 3402 // Check both services have been removed. 3403 ws := memdb.NewWatchSet() 3404 out, err := fsm.State().GetServiceRegistrationByID(ws, services[0].Namespace, services[0].ID) 3405 assert.Nil(t, err) 3406 assert.Nil(t, out) 3407 3408 out, err = fsm.State().GetServiceRegistrationByID(ws, services[1].Namespace, services[1].ID) 3409 assert.Nil(t, err) 3410 assert.Nil(t, out) 3411 } 3412 3413 func TestFSM_SnapshotRestore_Variables(t *testing.T) { 3414 ci.Parallel(t) 3415 3416 // Create our initial FSM which will be snapshotted. 3417 fsm := testFSM(t) 3418 testState := fsm.State() 3419 3420 // Generate and upsert some variables. 3421 msvs := mock.VariablesEncrypted(3, 3) 3422 svs := msvs.List() 3423 3424 for _, sv := range svs { 3425 setResp := testState.VarSet(10, &structs.VarApplyStateRequest{ 3426 Op: structs.VarOpSet, 3427 Var: sv, 3428 }) 3429 require.NoError(t, setResp.Error) 3430 } 3431 3432 // Update the mock variables data with the actual create information 3433 iter, err := testState.Variables(memdb.NewWatchSet()) 3434 require.NoError(t, err) 3435 3436 for raw := iter.Next(); raw != nil; raw = iter.Next() { 3437 sv := raw.(*structs.VariableEncrypted) 3438 msvs[sv.Path].CreateIndex = sv.CreateIndex 3439 msvs[sv.Path].CreateTime = sv.CreateTime 3440 msvs[sv.Path].ModifyIndex = sv.ModifyIndex 3441 msvs[sv.Path].ModifyTime = sv.ModifyTime 3442 } 3443 svs = msvs.List() 3444 3445 // List the variables from restored state and ensure everything 3446 // is as expected. 3447 3448 // Perform a snapshot restore. 3449 restoredFSM := testSnapshotRestore(t, fsm) 3450 restoredState := restoredFSM.State() 3451 3452 // List the variables from restored state and ensure everything 3453 // is as expected. 3454 iter, err = restoredState.Variables(memdb.NewWatchSet()) 3455 require.NoError(t, err) 3456 3457 var restoredSVs []*structs.VariableEncrypted 3458 3459 for raw := iter.Next(); raw != nil; raw = iter.Next() { 3460 restoredSVs = append(restoredSVs, raw.(*structs.VariableEncrypted)) 3461 } 3462 require.ElementsMatch(t, restoredSVs, svs) 3463 } 3464 3465 func TestFSM_ApplyACLRolesUpsert(t *testing.T) { 3466 ci.Parallel(t) 3467 fsm := testFSM(t) 3468 3469 // Create the policies our ACL roles wants to link to. 3470 policy1 := mock.ACLPolicy() 3471 policy1.Name = "mocked-test-policy-1" 3472 policy2 := mock.ACLPolicy() 3473 policy2.Name = "mocked-test-policy-2" 3474 3475 require.NoError(t, fsm.State().UpsertACLPolicies( 3476 structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2})) 3477 3478 // Generate the upsert request and apply the change. 3479 req := structs.ACLRolesUpsertRequest{ 3480 ACLRoles: []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()}, 3481 } 3482 buf, err := structs.Encode(structs.ACLRolesUpsertRequestType, req) 3483 require.NoError(t, err) 3484 require.Nil(t, fsm.Apply(makeLog(buf))) 3485 3486 // Read out both ACL roles and perform an equality check using the hash. 3487 ws := memdb.NewWatchSet() 3488 out, err := fsm.State().GetACLRoleByName(ws, req.ACLRoles[0].Name) 3489 require.NoError(t, err) 3490 require.Equal(t, req.ACLRoles[0].Hash, out.Hash) 3491 3492 out, err = fsm.State().GetACLRoleByName(ws, req.ACLRoles[1].Name) 3493 require.NoError(t, err) 3494 require.Equal(t, req.ACLRoles[1].Hash, out.Hash) 3495 } 3496 3497 func TestFSM_ApplyACLRolesDeleteByID(t *testing.T) { 3498 ci.Parallel(t) 3499 fsm := testFSM(t) 3500 3501 // Create the policies our ACL roles wants to link to. 3502 policy1 := mock.ACLPolicy() 3503 policy1.Name = "mocked-test-policy-1" 3504 policy2 := mock.ACLPolicy() 3505 policy2.Name = "mocked-test-policy-2" 3506 3507 require.NoError(t, fsm.State().UpsertACLPolicies( 3508 structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy1, policy2})) 3509 3510 // Generate and upsert two ACL roles. 3511 aclRoles := []*structs.ACLRole{mock.ACLRole(), mock.ACLRole()} 3512 require.NoError(t, fsm.State().UpsertACLRoles(structs.MsgTypeTestSetup, 10, aclRoles, false)) 3513 3514 // Build and apply our message. 3515 req := structs.ACLRolesDeleteByIDRequest{ACLRoleIDs: []string{aclRoles[0].ID, aclRoles[1].ID}} 3516 buf, err := structs.Encode(structs.ACLRolesDeleteByIDRequestType, req) 3517 require.NoError(t, err) 3518 require.Nil(t, fsm.Apply(makeLog(buf))) 3519 3520 // List all ACL roles within state to ensure both have been removed. 3521 ws := memdb.NewWatchSet() 3522 iter, err := fsm.State().GetACLRoles(ws) 3523 require.NoError(t, err) 3524 3525 var count int 3526 for raw := iter.Next(); raw != nil; raw = iter.Next() { 3527 count++ 3528 } 3529 require.Equal(t, 0, count) 3530 } 3531 3532 func TestFSM_ACLEvents(t *testing.T) { 3533 ci.Parallel(t) 3534 3535 cases := []struct { 3536 desc string 3537 setupfn func(t *testing.T, fsm *nomadFSM) 3538 raftReq func(t *testing.T) []byte 3539 reqTopic structs.Topic 3540 eventfn func(t *testing.T, e []structs.Event) 3541 }{ 3542 { 3543 desc: "ACLToken upserted", 3544 raftReq: func(t *testing.T) []byte { 3545 req := structs.ACLTokenUpsertRequest{ 3546 Tokens: []*structs.ACLToken{mock.ACLToken()}, 3547 } 3548 buf, err := structs.Encode(structs.ACLTokenUpsertRequestType, req) 3549 require.NoError(t, err) 3550 return buf 3551 }, 3552 reqTopic: structs.TopicACLToken, 3553 eventfn: func(t *testing.T, e []structs.Event) { 3554 require.Len(t, e, 1) 3555 require.Equal(t, e[0].Topic, structs.TopicACLToken) 3556 require.Empty(t, e[0].Payload.(*structs.ACLTokenEvent).ACLToken.SecretID) 3557 require.Equal(t, e[0].Type, structs.TypeACLTokenUpserted) 3558 }, 3559 }, 3560 { 3561 desc: "ACLToken deleted", 3562 setupfn: func(t *testing.T, fsm *nomadFSM) { 3563 token := mock.ACLToken() 3564 token.SecretID = "26be01d3-df3a-45e9-9f49-4487a3dc3496" 3565 token.AccessorID = "b971acba-bbe5-4274-bdfa-8bb1f542a8c1" 3566 3567 require.NoError(t, 3568 fsm.State().UpsertACLTokens( 3569 structs.MsgTypeTestSetup, 10, []*structs.ACLToken{token})) 3570 }, 3571 raftReq: func(t *testing.T) []byte { 3572 req := structs.ACLTokenDeleteRequest{ 3573 AccessorIDs: []string{"b971acba-bbe5-4274-bdfa-8bb1f542a8c1"}, 3574 } 3575 buf, err := structs.Encode(structs.ACLTokenDeleteRequestType, req) 3576 require.NoError(t, err) 3577 return buf 3578 }, 3579 reqTopic: structs.TopicACLToken, 3580 eventfn: func(t *testing.T, e []structs.Event) { 3581 require.Len(t, e, 1) 3582 require.Equal(t, e[0].Topic, structs.TopicACLToken) 3583 require.Empty(t, e[0].Payload.(*structs.ACLTokenEvent).ACLToken.SecretID) 3584 require.Equal(t, e[0].Type, structs.TypeACLTokenDeleted) 3585 }, 3586 }, 3587 { 3588 desc: "ACLPolicy upserted", 3589 raftReq: func(t *testing.T) []byte { 3590 req := structs.ACLPolicyUpsertRequest{ 3591 Policies: []*structs.ACLPolicy{mock.ACLPolicy()}, 3592 } 3593 buf, err := structs.Encode(structs.ACLPolicyUpsertRequestType, req) 3594 require.NoError(t, err) 3595 return buf 3596 }, 3597 reqTopic: structs.TopicACLPolicy, 3598 eventfn: func(t *testing.T, e []structs.Event) { 3599 require.Len(t, e, 1) 3600 require.Equal(t, e[0].Topic, structs.TopicACLPolicy) 3601 require.Equal(t, e[0].Type, structs.TypeACLPolicyUpserted) 3602 }, 3603 }, 3604 { 3605 desc: "ACLPolicy deleted", 3606 setupfn: func(t *testing.T, fsm *nomadFSM) { 3607 policy := mock.ACLPolicy() 3608 policy.Name = "some-policy" 3609 3610 require.NoError(t, 3611 fsm.State().UpsertACLPolicies( 3612 structs.MsgTypeTestSetup, 10, []*structs.ACLPolicy{policy})) 3613 }, 3614 raftReq: func(t *testing.T) []byte { 3615 req := structs.ACLPolicyDeleteRequest{ 3616 Names: []string{"some-policy"}, 3617 } 3618 buf, err := structs.Encode(structs.ACLPolicyDeleteRequestType, req) 3619 require.NoError(t, err) 3620 return buf 3621 }, 3622 reqTopic: structs.TopicACLPolicy, 3623 eventfn: func(t *testing.T, e []structs.Event) { 3624 require.Len(t, e, 1) 3625 require.Equal(t, e[0].Topic, structs.TopicACLPolicy) 3626 require.Equal(t, e[0].Type, structs.TypeACLPolicyDeleted) 3627 }, 3628 }, 3629 } 3630 3631 for _, tc := range cases { 3632 t.Run(tc.desc, func(t *testing.T) { 3633 fsm := testFSM(t) 3634 3635 // Setup any state necessary 3636 if tc.setupfn != nil { 3637 tc.setupfn(t, fsm) 3638 } 3639 3640 // Apply the log 3641 resp := fsm.Apply(makeLog(tc.raftReq(t))) 3642 require.Nil(t, resp) 3643 3644 broker, err := fsm.State().EventBroker() 3645 require.NoError(t, err) 3646 3647 subReq := &stream.SubscribeRequest{ 3648 Topics: map[structs.Topic][]string{ 3649 tc.reqTopic: {"*"}, 3650 }, 3651 Namespace: "default", 3652 } 3653 3654 sub, err := broker.Subscribe(subReq) 3655 require.NoError(t, err) 3656 3657 var events []structs.Event 3658 3659 testutil.WaitForResult(func() (bool, error) { 3660 out, err := sub.NextNoBlock() 3661 require.NoError(t, err) 3662 3663 if out == nil { 3664 return false, fmt.Errorf("expected events got nil") 3665 } 3666 3667 events = out 3668 return true, nil 3669 }, func(err error) { 3670 require.Fail(t, err.Error()) 3671 }) 3672 3673 tc.eventfn(t, events) 3674 }) 3675 } 3676 } 3677 3678 // TestFSM_EventBroker_JobRegisterFSMEvents asserts that only a single job 3679 // register event is emitted when registering a job 3680 func TestFSM_EventBroker_JobRegisterFSMEvents(t *testing.T) { 3681 ci.Parallel(t) 3682 fsm := testFSM(t) 3683 3684 job := mock.Job() 3685 eval := mock.Eval() 3686 eval.JobID = job.ID 3687 3688 req := structs.JobRegisterRequest{ 3689 Job: job, 3690 Eval: eval, 3691 } 3692 buf, err := structs.Encode(structs.JobRegisterRequestType, req) 3693 require.NoError(t, err) 3694 3695 resp := fsm.Apply(makeLog(buf)) 3696 require.Nil(t, resp) 3697 3698 broker, err := fsm.State().EventBroker() 3699 require.NoError(t, err) 3700 3701 subReq := &stream.SubscribeRequest{ 3702 Topics: map[structs.Topic][]string{ 3703 structs.TopicJob: {"*"}, 3704 }, 3705 Namespace: "default", 3706 } 3707 3708 sub, err := broker.Subscribe(subReq) 3709 require.NoError(t, err) 3710 3711 ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(500*time.Millisecond)) 3712 defer cancel() 3713 3714 // consume the queue 3715 var events []structs.Event 3716 for { 3717 out, err := sub.Next(ctx) 3718 if len(out.Events) == 0 { 3719 break 3720 } 3721 3722 // consume the queue until the deadline has exceeded or until we've 3723 // received more events than expected 3724 if err == context.DeadlineExceeded || len(events) > 1 { 3725 break 3726 } 3727 3728 events = append(events, out.Events...) 3729 } 3730 3731 require.Len(t, events, 1) 3732 require.Equal(t, structs.TypeJobRegistered, events[0].Type) 3733 } 3734 3735 func TestFSM_UpsertACLAuthMethods(t *testing.T) { 3736 ci.Parallel(t) 3737 fsm := testFSM(t) 3738 3739 am1 := mock.ACLOIDCAuthMethod() 3740 am2 := mock.ACLOIDCAuthMethod() 3741 req := structs.ACLAuthMethodUpsertRequest{ 3742 AuthMethods: []*structs.ACLAuthMethod{am1, am2}, 3743 } 3744 buf, err := structs.Encode(structs.ACLAuthMethodsUpsertRequestType, req) 3745 must.Nil(t, err) 3746 must.Nil(t, fsm.Apply(makeLog(buf))) 3747 3748 // Verify we are registered 3749 ws := memdb.NewWatchSet() 3750 out, err := fsm.State().GetACLAuthMethodByName(ws, am1.Name) 3751 must.Nil(t, err) 3752 must.NotNil(t, out) 3753 3754 out, err = fsm.State().GetACLAuthMethodByName(ws, am2.Name) 3755 must.Nil(t, err) 3756 must.NotNil(t, out) 3757 } 3758 3759 func TestFSM_DeleteACLAuthMethods(t *testing.T) { 3760 ci.Parallel(t) 3761 fsm := testFSM(t) 3762 3763 am1 := mock.ACLOIDCAuthMethod() 3764 am2 := mock.ACLOIDCAuthMethod() 3765 must.Nil(t, fsm.State().UpsertACLAuthMethods(1000, []*structs.ACLAuthMethod{am1, am2})) 3766 3767 req := structs.ACLAuthMethodDeleteRequest{ 3768 Names: []string{am1.Name, am2.Name}, 3769 } 3770 buf, err := structs.Encode(structs.ACLAuthMethodsDeleteRequestType, req) 3771 must.Nil(t, err) 3772 must.Nil(t, fsm.Apply(makeLog(buf))) 3773 3774 // Verify we are NOT registered 3775 ws := memdb.NewWatchSet() 3776 out, err := fsm.State().GetACLAuthMethodByName(ws, am1.Name) 3777 must.Nil(t, err) 3778 must.Nil(t, out) 3779 3780 out, err = fsm.State().GetACLAuthMethodByName(ws, am2.Name) 3781 must.Nil(t, err) 3782 must.Nil(t, out) 3783 } 3784 3785 func TestFSM_UpsertACLBindingRules(t *testing.T) { 3786 ci.Parallel(t) 3787 fsm := testFSM(t) 3788 3789 // Create an auth method and upsert so the binding rules can link to this. 3790 authMethod := mock.ACLOIDCAuthMethod() 3791 must.NoError(t, fsm.state.UpsertACLAuthMethods(10, []*structs.ACLAuthMethod{authMethod})) 3792 3793 aclBindingRule1 := mock.ACLBindingRule() 3794 aclBindingRule1.AuthMethod = authMethod.Name 3795 aclBindingRule2 := mock.ACLBindingRule() 3796 aclBindingRule2.AuthMethod = authMethod.Name 3797 3798 req := structs.ACLBindingRulesUpsertRequest{ 3799 ACLBindingRules: []*structs.ACLBindingRule{aclBindingRule1, aclBindingRule2}, 3800 } 3801 buf, err := structs.Encode(structs.ACLBindingRulesUpsertRequestType, req) 3802 must.NoError(t, err) 3803 must.Nil(t, fsm.Apply(makeLog(buf))) 3804 3805 // Ensure the ACL binding rules have been upserted correctly. 3806 ws := memdb.NewWatchSet() 3807 out, err := fsm.State().GetACLBindingRule(ws, aclBindingRule1.ID) 3808 must.Nil(t, err) 3809 must.Eq(t, aclBindingRule1, out) 3810 3811 out, err = fsm.State().GetACLBindingRule(ws, aclBindingRule2.ID) 3812 must.Nil(t, err) 3813 must.Eq(t, aclBindingRule2, out) 3814 } 3815 3816 func TestFSM_DeleteACLBindingRules(t *testing.T) { 3817 ci.Parallel(t) 3818 fsm := testFSM(t) 3819 3820 aclBindingRule1 := mock.ACLBindingRule() 3821 aclBindingRule2 := mock.ACLBindingRule() 3822 must.NoError(t, fsm.State().UpsertACLBindingRules( 3823 10, []*structs.ACLBindingRule{aclBindingRule1, aclBindingRule2}, true)) 3824 3825 req := structs.ACLBindingRulesDeleteRequest{ 3826 ACLBindingRuleIDs: []string{aclBindingRule1.ID, aclBindingRule2.ID}, 3827 } 3828 buf, err := structs.Encode(structs.ACLBindingRulesDeleteRequestType, req) 3829 must.NoError(t, err) 3830 must.Nil(t, fsm.Apply(makeLog(buf))) 3831 3832 // Ensure neither ACL binding rule is now found. 3833 ws := memdb.NewWatchSet() 3834 out, err := fsm.State().GetACLBindingRule(ws, aclBindingRule1.ID) 3835 must.NoError(t, err) 3836 must.Nil(t, out) 3837 3838 out, err = fsm.State().GetACLBindingRule(ws, aclBindingRule2.ID) 3839 must.NoError(t, err) 3840 must.Nil(t, out) 3841 }