github.com/ThomasObenaus/nomad@v0.11.1/nomad/state/state_store_test.go (about) 1 package state 2 3 import ( 4 "context" 5 "fmt" 6 "reflect" 7 "sort" 8 "strings" 9 "testing" 10 "time" 11 12 "github.com/hashicorp/go-memdb" 13 "github.com/kr/pretty" 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/require" 16 17 "github.com/hashicorp/nomad/helper" 18 "github.com/hashicorp/nomad/helper/uuid" 19 "github.com/hashicorp/nomad/nomad/mock" 20 "github.com/hashicorp/nomad/nomad/structs" 21 ) 22 23 func testStateStore(t *testing.T) *StateStore { 24 return TestStateStore(t) 25 } 26 27 func TestStateStore_Blocking_Error(t *testing.T) { 28 t.Parallel() 29 30 expected := fmt.Errorf("test error") 31 errFn := func(memdb.WatchSet, *StateStore) (interface{}, uint64, error) { 32 return nil, 0, expected 33 } 34 35 state := testStateStore(t) 36 _, idx, err := state.BlockingQuery(errFn, 10, context.Background()) 37 assert.EqualError(t, err, expected.Error()) 38 assert.Zero(t, idx) 39 } 40 41 func TestStateStore_Blocking_Timeout(t *testing.T) { 42 t.Parallel() 43 44 noopFn := func(memdb.WatchSet, *StateStore) (interface{}, uint64, error) { 45 return nil, 5, nil 46 } 47 48 state := testStateStore(t) 49 timeout := time.Now().Add(250 * time.Millisecond) 50 deadlineCtx, cancel := context.WithDeadline(context.Background(), timeout) 51 defer cancel() 52 53 _, idx, err := state.BlockingQuery(noopFn, 10, deadlineCtx) 54 assert.EqualError(t, err, context.DeadlineExceeded.Error()) 55 assert.EqualValues(t, 5, idx) 56 assert.WithinDuration(t, timeout, time.Now(), 100*time.Millisecond) 57 } 58 59 func TestStateStore_Blocking_MinQuery(t *testing.T) { 60 t.Parallel() 61 62 node := mock.Node() 63 count := 0 64 queryFn := func(ws memdb.WatchSet, s *StateStore) (interface{}, uint64, error) { 65 _, err := s.NodeByID(ws, node.ID) 66 if err != nil { 67 return nil, 0, err 68 } 69 70 count++ 71 if count == 1 { 72 return false, 5, nil 73 } else if count > 2 { 74 return false, 20, fmt.Errorf("called too many times") 75 } 76 77 return true, 11, nil 78 } 79 80 state := testStateStore(t) 81 timeout := time.Now().Add(100 * time.Millisecond) 82 deadlineCtx, cancel := context.WithDeadline(context.Background(), timeout) 83 defer cancel() 84 85 time.AfterFunc(5*time.Millisecond, func() { 86 state.UpsertNode(11, node) 87 }) 88 89 resp, idx, err := state.BlockingQuery(queryFn, 10, deadlineCtx) 90 if assert.Nil(t, err) { 91 assert.Equal(t, 2, count) 92 assert.EqualValues(t, 11, idx) 93 assert.True(t, resp.(bool)) 94 } 95 } 96 97 // COMPAT 0.11: Uses AllocUpdateRequest.Alloc 98 // This test checks that: 99 // 1) The job is denormalized 100 // 2) Allocations are created 101 func TestStateStore_UpsertPlanResults_AllocationsCreated_Denormalized(t *testing.T) { 102 t.Parallel() 103 104 state := testStateStore(t) 105 alloc := mock.Alloc() 106 job := alloc.Job 107 alloc.Job = nil 108 109 if err := state.UpsertJob(999, job); err != nil { 110 t.Fatalf("err: %v", err) 111 } 112 113 eval := mock.Eval() 114 eval.JobID = job.ID 115 116 // Create an eval 117 if err := state.UpsertEvals(1, []*structs.Evaluation{eval}); err != nil { 118 t.Fatalf("err: %v", err) 119 } 120 121 // Create a plan result 122 res := structs.ApplyPlanResultsRequest{ 123 AllocUpdateRequest: structs.AllocUpdateRequest{ 124 Alloc: []*structs.Allocation{alloc}, 125 Job: job, 126 }, 127 EvalID: eval.ID, 128 } 129 assert := assert.New(t) 130 err := state.UpsertPlanResults(1000, &res) 131 assert.Nil(err) 132 133 ws := memdb.NewWatchSet() 134 out, err := state.AllocByID(ws, alloc.ID) 135 assert.Nil(err) 136 assert.Equal(alloc, out) 137 138 index, err := state.Index("allocs") 139 assert.Nil(err) 140 assert.EqualValues(1000, index) 141 142 if watchFired(ws) { 143 t.Fatalf("bad") 144 } 145 146 evalOut, err := state.EvalByID(ws, eval.ID) 147 assert.Nil(err) 148 assert.NotNil(evalOut) 149 assert.EqualValues(1000, evalOut.ModifyIndex) 150 } 151 152 // This test checks that: 153 // 1) The job is denormalized 154 // 2) Allocations are denormalized and updated with the diff 155 // That stopped allocs Job is unmodified 156 func TestStateStore_UpsertPlanResults_AllocationsDenormalized(t *testing.T) { 157 t.Parallel() 158 159 state := testStateStore(t) 160 alloc := mock.Alloc() 161 job := alloc.Job 162 alloc.Job = nil 163 164 stoppedAlloc := mock.Alloc() 165 stoppedAlloc.Job = job 166 stoppedAllocDiff := &structs.AllocationDiff{ 167 ID: stoppedAlloc.ID, 168 DesiredDescription: "desired desc", 169 ClientStatus: structs.AllocClientStatusLost, 170 } 171 preemptedAlloc := mock.Alloc() 172 preemptedAlloc.Job = job 173 preemptedAllocDiff := &structs.AllocationDiff{ 174 ID: preemptedAlloc.ID, 175 PreemptedByAllocation: alloc.ID, 176 } 177 178 require := require.New(t) 179 require.NoError(state.UpsertAllocs(900, []*structs.Allocation{stoppedAlloc, preemptedAlloc})) 180 require.NoError(state.UpsertJob(999, job)) 181 182 // modify job and ensure that stopped and preempted alloc point to original Job 183 mJob := job.Copy() 184 mJob.TaskGroups[0].Name = "other" 185 186 require.NoError(state.UpsertJob(1001, mJob)) 187 188 eval := mock.Eval() 189 eval.JobID = job.ID 190 191 // Create an eval 192 require.NoError(state.UpsertEvals(1, []*structs.Evaluation{eval})) 193 194 // Create a plan result 195 res := structs.ApplyPlanResultsRequest{ 196 AllocUpdateRequest: structs.AllocUpdateRequest{ 197 AllocsUpdated: []*structs.Allocation{alloc}, 198 AllocsStopped: []*structs.AllocationDiff{stoppedAllocDiff}, 199 Job: mJob, 200 }, 201 EvalID: eval.ID, 202 AllocsPreempted: []*structs.AllocationDiff{preemptedAllocDiff}, 203 } 204 assert := assert.New(t) 205 planModifyIndex := uint64(1000) 206 err := state.UpsertPlanResults(planModifyIndex, &res) 207 require.NoError(err) 208 209 ws := memdb.NewWatchSet() 210 out, err := state.AllocByID(ws, alloc.ID) 211 require.NoError(err) 212 assert.Equal(alloc, out) 213 214 outJob, err := state.JobByID(ws, job.Namespace, job.ID) 215 require.NoError(err) 216 require.Equal(mJob.TaskGroups, outJob.TaskGroups) 217 require.NotEmpty(job.TaskGroups, outJob.TaskGroups) 218 219 updatedStoppedAlloc, err := state.AllocByID(ws, stoppedAlloc.ID) 220 require.NoError(err) 221 assert.Equal(stoppedAllocDiff.DesiredDescription, updatedStoppedAlloc.DesiredDescription) 222 assert.Equal(structs.AllocDesiredStatusStop, updatedStoppedAlloc.DesiredStatus) 223 assert.Equal(stoppedAllocDiff.ClientStatus, updatedStoppedAlloc.ClientStatus) 224 assert.Equal(planModifyIndex, updatedStoppedAlloc.AllocModifyIndex) 225 assert.Equal(planModifyIndex, updatedStoppedAlloc.AllocModifyIndex) 226 assert.Equal(job.TaskGroups, updatedStoppedAlloc.Job.TaskGroups) 227 228 updatedPreemptedAlloc, err := state.AllocByID(ws, preemptedAlloc.ID) 229 require.NoError(err) 230 assert.Equal(structs.AllocDesiredStatusEvict, updatedPreemptedAlloc.DesiredStatus) 231 assert.Equal(preemptedAllocDiff.PreemptedByAllocation, updatedPreemptedAlloc.PreemptedByAllocation) 232 assert.Equal(planModifyIndex, updatedPreemptedAlloc.AllocModifyIndex) 233 assert.Equal(planModifyIndex, updatedPreemptedAlloc.AllocModifyIndex) 234 assert.Equal(job.TaskGroups, updatedPreemptedAlloc.Job.TaskGroups) 235 236 index, err := state.Index("allocs") 237 require.NoError(err) 238 assert.EqualValues(planModifyIndex, index) 239 240 require.False(watchFired(ws)) 241 242 evalOut, err := state.EvalByID(ws, eval.ID) 243 require.NoError(err) 244 require.NotNil(evalOut) 245 assert.EqualValues(planModifyIndex, evalOut.ModifyIndex) 246 247 } 248 249 // This test checks that the deployment is created and allocations count towards 250 // the deployment 251 func TestStateStore_UpsertPlanResults_Deployment(t *testing.T) { 252 t.Parallel() 253 254 state := testStateStore(t) 255 alloc := mock.Alloc() 256 alloc2 := mock.Alloc() 257 job := alloc.Job 258 alloc.Job = nil 259 alloc2.Job = nil 260 261 d := mock.Deployment() 262 alloc.DeploymentID = d.ID 263 alloc2.DeploymentID = d.ID 264 265 if err := state.UpsertJob(999, job); err != nil { 266 t.Fatalf("err: %v", err) 267 } 268 269 eval := mock.Eval() 270 eval.JobID = job.ID 271 272 // Create an eval 273 if err := state.UpsertEvals(1, []*structs.Evaluation{eval}); err != nil { 274 t.Fatalf("err: %v", err) 275 } 276 277 // Create a plan result 278 res := structs.ApplyPlanResultsRequest{ 279 AllocUpdateRequest: structs.AllocUpdateRequest{ 280 Alloc: []*structs.Allocation{alloc, alloc2}, 281 Job: job, 282 }, 283 Deployment: d, 284 EvalID: eval.ID, 285 } 286 287 err := state.UpsertPlanResults(1000, &res) 288 if err != nil { 289 t.Fatalf("err: %v", err) 290 } 291 292 ws := memdb.NewWatchSet() 293 assert := assert.New(t) 294 out, err := state.AllocByID(ws, alloc.ID) 295 assert.Nil(err) 296 assert.Equal(alloc, out) 297 298 dout, err := state.DeploymentByID(ws, d.ID) 299 assert.Nil(err) 300 assert.NotNil(dout) 301 302 tg, ok := dout.TaskGroups[alloc.TaskGroup] 303 assert.True(ok) 304 assert.NotNil(tg) 305 assert.Equal(2, tg.PlacedAllocs) 306 307 evalOut, err := state.EvalByID(ws, eval.ID) 308 assert.Nil(err) 309 assert.NotNil(evalOut) 310 assert.EqualValues(1000, evalOut.ModifyIndex) 311 312 if watchFired(ws) { 313 t.Fatalf("bad") 314 } 315 316 // Update the allocs to be part of a new deployment 317 d2 := d.Copy() 318 d2.ID = uuid.Generate() 319 320 allocNew := alloc.Copy() 321 allocNew.DeploymentID = d2.ID 322 allocNew2 := alloc2.Copy() 323 allocNew2.DeploymentID = d2.ID 324 325 // Create another plan 326 res = structs.ApplyPlanResultsRequest{ 327 AllocUpdateRequest: structs.AllocUpdateRequest{ 328 Alloc: []*structs.Allocation{allocNew, allocNew2}, 329 Job: job, 330 }, 331 Deployment: d2, 332 EvalID: eval.ID, 333 } 334 335 err = state.UpsertPlanResults(1001, &res) 336 if err != nil { 337 t.Fatalf("err: %v", err) 338 } 339 340 dout, err = state.DeploymentByID(ws, d2.ID) 341 assert.Nil(err) 342 assert.NotNil(dout) 343 344 tg, ok = dout.TaskGroups[alloc.TaskGroup] 345 assert.True(ok) 346 assert.NotNil(tg) 347 assert.Equal(2, tg.PlacedAllocs) 348 349 evalOut, err = state.EvalByID(ws, eval.ID) 350 assert.Nil(err) 351 assert.NotNil(evalOut) 352 assert.EqualValues(1001, evalOut.ModifyIndex) 353 } 354 355 // This test checks that: 356 // 1) Preempted allocations in plan results are updated 357 // 2) Evals are inserted for preempted jobs 358 func TestStateStore_UpsertPlanResults_PreemptedAllocs(t *testing.T) { 359 t.Parallel() 360 require := require.New(t) 361 362 state := testStateStore(t) 363 alloc := mock.Alloc() 364 job := alloc.Job 365 alloc.Job = nil 366 367 // Insert job 368 err := state.UpsertJob(999, job) 369 require.NoError(err) 370 371 // Create an eval 372 eval := mock.Eval() 373 eval.JobID = job.ID 374 err = state.UpsertEvals(1, []*structs.Evaluation{eval}) 375 require.NoError(err) 376 377 // Insert alloc that will be preempted in the plan 378 preemptedAlloc := mock.Alloc() 379 err = state.UpsertAllocs(2, []*structs.Allocation{preemptedAlloc}) 380 require.NoError(err) 381 382 minimalPreemptedAlloc := &structs.Allocation{ 383 ID: preemptedAlloc.ID, 384 PreemptedByAllocation: alloc.ID, 385 ModifyTime: time.Now().Unix(), 386 } 387 388 // Create eval for preempted job 389 eval2 := mock.Eval() 390 eval2.JobID = preemptedAlloc.JobID 391 392 // Create a plan result 393 res := structs.ApplyPlanResultsRequest{ 394 AllocUpdateRequest: structs.AllocUpdateRequest{ 395 Alloc: []*structs.Allocation{alloc}, 396 Job: job, 397 }, 398 EvalID: eval.ID, 399 NodePreemptions: []*structs.Allocation{minimalPreemptedAlloc}, 400 PreemptionEvals: []*structs.Evaluation{eval2}, 401 } 402 403 err = state.UpsertPlanResults(1000, &res) 404 require.NoError(err) 405 406 ws := memdb.NewWatchSet() 407 408 // Verify alloc and eval created by plan 409 out, err := state.AllocByID(ws, alloc.ID) 410 require.NoError(err) 411 require.Equal(alloc, out) 412 413 index, err := state.Index("allocs") 414 require.NoError(err) 415 require.EqualValues(1000, index) 416 417 evalOut, err := state.EvalByID(ws, eval.ID) 418 require.NoError(err) 419 require.NotNil(evalOut) 420 require.EqualValues(1000, evalOut.ModifyIndex) 421 422 // Verify preempted alloc 423 preempted, err := state.AllocByID(ws, preemptedAlloc.ID) 424 require.NoError(err) 425 require.Equal(preempted.DesiredStatus, structs.AllocDesiredStatusEvict) 426 require.Equal(preempted.DesiredDescription, fmt.Sprintf("Preempted by alloc ID %v", alloc.ID)) 427 require.Equal(preempted.Job.ID, preemptedAlloc.Job.ID) 428 require.Equal(preempted.Job, preemptedAlloc.Job) 429 430 // Verify eval for preempted job 431 preemptedJobEval, err := state.EvalByID(ws, eval2.ID) 432 require.NoError(err) 433 require.NotNil(preemptedJobEval) 434 require.EqualValues(1000, preemptedJobEval.ModifyIndex) 435 436 } 437 438 // This test checks that deployment updates are applied correctly 439 func TestStateStore_UpsertPlanResults_DeploymentUpdates(t *testing.T) { 440 t.Parallel() 441 state := testStateStore(t) 442 443 // Create a job that applies to all 444 job := mock.Job() 445 if err := state.UpsertJob(998, job); err != nil { 446 t.Fatalf("err: %v", err) 447 } 448 449 // Create a deployment that we will update its status 450 doutstanding := mock.Deployment() 451 doutstanding.JobID = job.ID 452 453 if err := state.UpsertDeployment(1000, doutstanding); err != nil { 454 t.Fatalf("err: %v", err) 455 } 456 457 eval := mock.Eval() 458 eval.JobID = job.ID 459 460 // Create an eval 461 if err := state.UpsertEvals(1, []*structs.Evaluation{eval}); err != nil { 462 t.Fatalf("err: %v", err) 463 } 464 alloc := mock.Alloc() 465 alloc.Job = nil 466 467 dnew := mock.Deployment() 468 dnew.JobID = job.ID 469 alloc.DeploymentID = dnew.ID 470 471 // Update the old deployment 472 update := &structs.DeploymentStatusUpdate{ 473 DeploymentID: doutstanding.ID, 474 Status: "foo", 475 StatusDescription: "bar", 476 } 477 478 // Create a plan result 479 res := structs.ApplyPlanResultsRequest{ 480 AllocUpdateRequest: structs.AllocUpdateRequest{ 481 Alloc: []*structs.Allocation{alloc}, 482 Job: job, 483 }, 484 Deployment: dnew, 485 DeploymentUpdates: []*structs.DeploymentStatusUpdate{update}, 486 EvalID: eval.ID, 487 } 488 489 err := state.UpsertPlanResults(1000, &res) 490 if err != nil { 491 t.Fatalf("err: %v", err) 492 } 493 assert := assert.New(t) 494 ws := memdb.NewWatchSet() 495 496 // Check the deployments are correctly updated. 497 dout, err := state.DeploymentByID(ws, dnew.ID) 498 assert.Nil(err) 499 assert.NotNil(dout) 500 501 tg, ok := dout.TaskGroups[alloc.TaskGroup] 502 assert.True(ok) 503 assert.NotNil(tg) 504 assert.Equal(1, tg.PlacedAllocs) 505 506 doutstandingout, err := state.DeploymentByID(ws, doutstanding.ID) 507 assert.Nil(err) 508 assert.NotNil(doutstandingout) 509 assert.Equal(update.Status, doutstandingout.Status) 510 assert.Equal(update.StatusDescription, doutstandingout.StatusDescription) 511 assert.EqualValues(1000, doutstandingout.ModifyIndex) 512 513 evalOut, err := state.EvalByID(ws, eval.ID) 514 assert.Nil(err) 515 assert.NotNil(evalOut) 516 assert.EqualValues(1000, evalOut.ModifyIndex) 517 if watchFired(ws) { 518 t.Fatalf("bad") 519 } 520 } 521 522 func TestStateStore_UpsertDeployment(t *testing.T) { 523 t.Parallel() 524 525 state := testStateStore(t) 526 deployment := mock.Deployment() 527 528 // Create a watchset so we can test that upsert fires the watch 529 ws := memdb.NewWatchSet() 530 _, err := state.DeploymentsByJobID(ws, deployment.Namespace, deployment.ID, true) 531 if err != nil { 532 t.Fatalf("bad: %v", err) 533 } 534 535 err = state.UpsertDeployment(1000, deployment) 536 if err != nil { 537 t.Fatalf("err: %v", err) 538 } 539 if !watchFired(ws) { 540 t.Fatalf("bad") 541 } 542 543 ws = memdb.NewWatchSet() 544 out, err := state.DeploymentByID(ws, deployment.ID) 545 if err != nil { 546 t.Fatalf("err: %v", err) 547 } 548 549 if !reflect.DeepEqual(deployment, out) { 550 t.Fatalf("bad: %#v %#v", deployment, out) 551 } 552 553 index, err := state.Index("deployment") 554 if err != nil { 555 t.Fatalf("err: %v", err) 556 } 557 if index != 1000 { 558 t.Fatalf("bad: %d", index) 559 } 560 561 if watchFired(ws) { 562 t.Fatalf("bad") 563 } 564 } 565 566 // Tests that deployments of older create index and same job id are not returned 567 func TestStateStore_OldDeployment(t *testing.T) { 568 t.Parallel() 569 570 state := testStateStore(t) 571 job := mock.Job() 572 job.ID = "job1" 573 state.UpsertJob(1000, job) 574 575 deploy1 := mock.Deployment() 576 deploy1.JobID = job.ID 577 deploy1.JobCreateIndex = job.CreateIndex 578 579 deploy2 := mock.Deployment() 580 deploy2.JobID = job.ID 581 deploy2.JobCreateIndex = 11 582 583 require := require.New(t) 584 585 // Insert both deployments 586 err := state.UpsertDeployment(1001, deploy1) 587 require.Nil(err) 588 589 err = state.UpsertDeployment(1002, deploy2) 590 require.Nil(err) 591 592 ws := memdb.NewWatchSet() 593 // Should return both deployments 594 deploys, err := state.DeploymentsByJobID(ws, deploy1.Namespace, job.ID, true) 595 require.Nil(err) 596 require.Len(deploys, 2) 597 598 // Should only return deploy1 599 deploys, err = state.DeploymentsByJobID(ws, deploy1.Namespace, job.ID, false) 600 require.Nil(err) 601 require.Len(deploys, 1) 602 require.Equal(deploy1.ID, deploys[0].ID) 603 } 604 605 func TestStateStore_DeleteDeployment(t *testing.T) { 606 t.Parallel() 607 608 state := testStateStore(t) 609 d1 := mock.Deployment() 610 d2 := mock.Deployment() 611 612 err := state.UpsertDeployment(1000, d1) 613 if err != nil { 614 t.Fatalf("err: %v", err) 615 } 616 if err := state.UpsertDeployment(1001, d2); err != nil { 617 t.Fatalf("err: %v", err) 618 } 619 620 // Create a watchset so we can test that delete fires the watch 621 ws := memdb.NewWatchSet() 622 if _, err := state.DeploymentByID(ws, d1.ID); err != nil { 623 t.Fatalf("bad: %v", err) 624 } 625 626 err = state.DeleteDeployment(1002, []string{d1.ID, d2.ID}) 627 if err != nil { 628 t.Fatalf("err: %v", err) 629 } 630 631 if !watchFired(ws) { 632 t.Fatalf("bad") 633 } 634 635 ws = memdb.NewWatchSet() 636 out, err := state.DeploymentByID(ws, d1.ID) 637 if err != nil { 638 t.Fatalf("err: %v", err) 639 } 640 641 if out != nil { 642 t.Fatalf("bad: %#v %#v", d1, out) 643 } 644 645 index, err := state.Index("deployment") 646 if err != nil { 647 t.Fatalf("err: %v", err) 648 } 649 if index != 1002 { 650 t.Fatalf("bad: %d", index) 651 } 652 653 if watchFired(ws) { 654 t.Fatalf("bad") 655 } 656 } 657 658 func TestStateStore_Deployments(t *testing.T) { 659 t.Parallel() 660 661 state := testStateStore(t) 662 var deployments []*structs.Deployment 663 664 for i := 0; i < 10; i++ { 665 deployment := mock.Deployment() 666 deployments = append(deployments, deployment) 667 668 err := state.UpsertDeployment(1000+uint64(i), deployment) 669 if err != nil { 670 t.Fatalf("err: %v", err) 671 } 672 } 673 674 ws := memdb.NewWatchSet() 675 iter, err := state.Deployments(ws) 676 if err != nil { 677 t.Fatalf("err: %v", err) 678 } 679 680 var out []*structs.Deployment 681 for { 682 raw := iter.Next() 683 if raw == nil { 684 break 685 } 686 out = append(out, raw.(*structs.Deployment)) 687 } 688 689 lessThan := func(i, j int) bool { 690 return deployments[i].ID < deployments[j].ID 691 } 692 sort.Slice(deployments, lessThan) 693 sort.Slice(out, lessThan) 694 695 if !reflect.DeepEqual(deployments, out) { 696 t.Fatalf("bad: %#v %#v", deployments, out) 697 } 698 699 if watchFired(ws) { 700 t.Fatalf("bad") 701 } 702 } 703 704 func TestStateStore_DeploymentsByIDPrefix(t *testing.T) { 705 t.Parallel() 706 707 state := testStateStore(t) 708 deploy := mock.Deployment() 709 710 deploy.ID = "11111111-662e-d0ab-d1c9-3e434af7bdb4" 711 err := state.UpsertDeployment(1000, deploy) 712 if err != nil { 713 t.Fatalf("err: %v", err) 714 } 715 716 // Create a watchset so we can test that getters don't cause it to fire 717 ws := memdb.NewWatchSet() 718 iter, err := state.DeploymentsByIDPrefix(ws, deploy.Namespace, deploy.ID) 719 if err != nil { 720 t.Fatalf("err: %v", err) 721 } 722 723 gatherDeploys := func(iter memdb.ResultIterator) []*structs.Deployment { 724 var deploys []*structs.Deployment 725 for { 726 raw := iter.Next() 727 if raw == nil { 728 break 729 } 730 deploy := raw.(*structs.Deployment) 731 deploys = append(deploys, deploy) 732 } 733 return deploys 734 } 735 736 deploys := gatherDeploys(iter) 737 if len(deploys) != 1 { 738 t.Fatalf("err: %v", err) 739 } 740 741 if watchFired(ws) { 742 t.Fatalf("bad") 743 } 744 745 iter, err = state.DeploymentsByIDPrefix(ws, deploy.Namespace, "11") 746 if err != nil { 747 t.Fatalf("err: %v", err) 748 } 749 750 deploys = gatherDeploys(iter) 751 if len(deploys) != 1 { 752 t.Fatalf("err: %v", err) 753 } 754 755 deploy = mock.Deployment() 756 deploy.ID = "11222222-662e-d0ab-d1c9-3e434af7bdb4" 757 err = state.UpsertDeployment(1001, deploy) 758 if err != nil { 759 t.Fatalf("err: %v", err) 760 } 761 762 if !watchFired(ws) { 763 t.Fatalf("bad") 764 } 765 766 ws = memdb.NewWatchSet() 767 iter, err = state.DeploymentsByIDPrefix(ws, deploy.Namespace, "11") 768 if err != nil { 769 t.Fatalf("err: %v", err) 770 } 771 772 deploys = gatherDeploys(iter) 773 if len(deploys) != 2 { 774 t.Fatalf("err: %v", err) 775 } 776 777 iter, err = state.DeploymentsByIDPrefix(ws, deploy.Namespace, "1111") 778 if err != nil { 779 t.Fatalf("err: %v", err) 780 } 781 782 deploys = gatherDeploys(iter) 783 if len(deploys) != 1 { 784 t.Fatalf("err: %v", err) 785 } 786 787 if watchFired(ws) { 788 t.Fatalf("bad") 789 } 790 } 791 792 func TestStateStore_UpsertNode_Node(t *testing.T) { 793 t.Parallel() 794 795 require := require.New(t) 796 state := testStateStore(t) 797 node := mock.Node() 798 799 // Create a watchset so we can test that upsert fires the watch 800 ws := memdb.NewWatchSet() 801 _, err := state.NodeByID(ws, node.ID) 802 require.NoError(err) 803 804 require.NoError(state.UpsertNode(1000, node)) 805 require.True(watchFired(ws)) 806 807 ws = memdb.NewWatchSet() 808 out, err := state.NodeByID(ws, node.ID) 809 require.NoError(err) 810 811 out2, err := state.NodeBySecretID(ws, node.SecretID) 812 require.NoError(err) 813 require.EqualValues(node, out) 814 require.EqualValues(node, out2) 815 require.Len(out.Events, 1) 816 require.Equal(NodeRegisterEventRegistered, out.Events[0].Message) 817 818 index, err := state.Index("nodes") 819 require.NoError(err) 820 require.EqualValues(1000, index) 821 require.False(watchFired(ws)) 822 823 // Transition the node to down and then up and ensure we get a re-register 824 // event 825 down := out.Copy() 826 down.Status = structs.NodeStatusDown 827 require.NoError(state.UpsertNode(1001, down)) 828 require.NoError(state.UpsertNode(1002, out)) 829 830 out, err = state.NodeByID(ws, node.ID) 831 require.NoError(err) 832 require.Len(out.Events, 2) 833 require.Equal(NodeRegisterEventReregistered, out.Events[1].Message) 834 } 835 836 func TestStateStore_DeleteNode_Node(t *testing.T) { 837 t.Parallel() 838 839 state := testStateStore(t) 840 841 // Create and insert two nodes, which we'll delete 842 node0 := mock.Node() 843 node1 := mock.Node() 844 err := state.UpsertNode(1000, node0) 845 require.NoError(t, err) 846 err = state.UpsertNode(1001, node1) 847 require.NoError(t, err) 848 849 // Create a watchset so we can test that delete fires the watch 850 ws := memdb.NewWatchSet() 851 852 // Check that both nodes are not nil 853 out, err := state.NodeByID(ws, node0.ID) 854 require.NoError(t, err) 855 require.NotNil(t, out) 856 out, err = state.NodeByID(ws, node1.ID) 857 require.NoError(t, err) 858 require.NotNil(t, out) 859 860 // Delete both nodes in a batch, fires the watch 861 err = state.DeleteNode(1002, []string{node0.ID, node1.ID}) 862 require.NoError(t, err) 863 require.True(t, watchFired(ws)) 864 865 // Check that both nodes are nil 866 ws = memdb.NewWatchSet() 867 out, err = state.NodeByID(ws, node0.ID) 868 require.NoError(t, err) 869 require.Nil(t, out) 870 out, err = state.NodeByID(ws, node1.ID) 871 require.NoError(t, err) 872 require.Nil(t, out) 873 874 // Ensure that the index is still at 1002, from DeleteNode 875 index, err := state.Index("nodes") 876 require.NoError(t, err) 877 require.Equal(t, uint64(1002), index) 878 require.False(t, watchFired(ws)) 879 } 880 881 func TestStateStore_UpdateNodeStatus_Node(t *testing.T) { 882 t.Parallel() 883 require := require.New(t) 884 885 state := testStateStore(t) 886 node := mock.Node() 887 888 require.NoError(state.UpsertNode(800, node)) 889 890 // Create a watchset so we can test that update node status fires the watch 891 ws := memdb.NewWatchSet() 892 _, err := state.NodeByID(ws, node.ID) 893 require.NoError(err) 894 895 event := &structs.NodeEvent{ 896 Message: "Node ready foo", 897 Subsystem: structs.NodeEventSubsystemCluster, 898 Timestamp: time.Now(), 899 } 900 901 require.NoError(state.UpdateNodeStatus(801, node.ID, structs.NodeStatusReady, 70, event)) 902 require.True(watchFired(ws)) 903 904 ws = memdb.NewWatchSet() 905 out, err := state.NodeByID(ws, node.ID) 906 require.NoError(err) 907 require.Equal(structs.NodeStatusReady, out.Status) 908 require.EqualValues(801, out.ModifyIndex) 909 require.EqualValues(70, out.StatusUpdatedAt) 910 require.Len(out.Events, 2) 911 require.Equal(event.Message, out.Events[1].Message) 912 913 index, err := state.Index("nodes") 914 require.NoError(err) 915 require.EqualValues(801, index) 916 require.False(watchFired(ws)) 917 } 918 919 func TestStateStore_BatchUpdateNodeDrain(t *testing.T) { 920 t.Parallel() 921 require := require.New(t) 922 923 state := testStateStore(t) 924 925 n1, n2 := mock.Node(), mock.Node() 926 require.Nil(state.UpsertNode(1000, n1)) 927 require.Nil(state.UpsertNode(1001, n2)) 928 929 // Create a watchset so we can test that update node drain fires the watch 930 ws := memdb.NewWatchSet() 931 _, err := state.NodeByID(ws, n1.ID) 932 require.Nil(err) 933 934 expectedDrain := &structs.DrainStrategy{ 935 DrainSpec: structs.DrainSpec{ 936 Deadline: -1 * time.Second, 937 }, 938 } 939 940 update := map[string]*structs.DrainUpdate{ 941 n1.ID: { 942 DrainStrategy: expectedDrain, 943 }, 944 n2.ID: { 945 DrainStrategy: expectedDrain, 946 }, 947 } 948 949 event := &structs.NodeEvent{ 950 Message: "Drain strategy enabled", 951 Subsystem: structs.NodeEventSubsystemDrain, 952 Timestamp: time.Now(), 953 } 954 events := map[string]*structs.NodeEvent{ 955 n1.ID: event, 956 n2.ID: event, 957 } 958 959 require.Nil(state.BatchUpdateNodeDrain(1002, 7, update, events)) 960 require.True(watchFired(ws)) 961 962 ws = memdb.NewWatchSet() 963 for _, id := range []string{n1.ID, n2.ID} { 964 out, err := state.NodeByID(ws, id) 965 require.Nil(err) 966 require.True(out.Drain) 967 require.NotNil(out.DrainStrategy) 968 require.Equal(out.DrainStrategy, expectedDrain) 969 require.Len(out.Events, 2) 970 require.EqualValues(1002, out.ModifyIndex) 971 require.EqualValues(7, out.StatusUpdatedAt) 972 } 973 974 index, err := state.Index("nodes") 975 require.Nil(err) 976 require.EqualValues(1002, index) 977 require.False(watchFired(ws)) 978 } 979 980 func TestStateStore_UpdateNodeDrain_Node(t *testing.T) { 981 t.Parallel() 982 require := require.New(t) 983 984 state := testStateStore(t) 985 node := mock.Node() 986 987 require.Nil(state.UpsertNode(1000, node)) 988 989 // Create a watchset so we can test that update node drain fires the watch 990 ws := memdb.NewWatchSet() 991 _, err := state.NodeByID(ws, node.ID) 992 require.Nil(err) 993 994 expectedDrain := &structs.DrainStrategy{ 995 DrainSpec: structs.DrainSpec{ 996 Deadline: -1 * time.Second, 997 }, 998 } 999 1000 event := &structs.NodeEvent{ 1001 Message: "Drain strategy enabled", 1002 Subsystem: structs.NodeEventSubsystemDrain, 1003 Timestamp: time.Now(), 1004 } 1005 require.Nil(state.UpdateNodeDrain(1001, node.ID, expectedDrain, false, 7, event)) 1006 require.True(watchFired(ws)) 1007 1008 ws = memdb.NewWatchSet() 1009 out, err := state.NodeByID(ws, node.ID) 1010 require.Nil(err) 1011 require.True(out.Drain) 1012 require.NotNil(out.DrainStrategy) 1013 require.Equal(out.DrainStrategy, expectedDrain) 1014 require.Len(out.Events, 2) 1015 require.EqualValues(1001, out.ModifyIndex) 1016 require.EqualValues(7, out.StatusUpdatedAt) 1017 1018 index, err := state.Index("nodes") 1019 require.Nil(err) 1020 require.EqualValues(1001, index) 1021 require.False(watchFired(ws)) 1022 } 1023 1024 func TestStateStore_AddSingleNodeEvent(t *testing.T) { 1025 t.Parallel() 1026 require := require.New(t) 1027 1028 state := testStateStore(t) 1029 1030 node := mock.Node() 1031 1032 // We create a new node event every time we register a node 1033 err := state.UpsertNode(1000, node) 1034 require.Nil(err) 1035 1036 require.Equal(1, len(node.Events)) 1037 require.Equal(structs.NodeEventSubsystemCluster, node.Events[0].Subsystem) 1038 require.Equal(NodeRegisterEventRegistered, node.Events[0].Message) 1039 1040 // Create a watchset so we can test that AddNodeEvent fires the watch 1041 ws := memdb.NewWatchSet() 1042 _, err = state.NodeByID(ws, node.ID) 1043 require.Nil(err) 1044 1045 nodeEvent := &structs.NodeEvent{ 1046 Message: "failed", 1047 Subsystem: "Driver", 1048 Timestamp: time.Now(), 1049 } 1050 nodeEvents := map[string][]*structs.NodeEvent{ 1051 node.ID: {nodeEvent}, 1052 } 1053 err = state.UpsertNodeEvents(uint64(1001), nodeEvents) 1054 require.Nil(err) 1055 1056 require.True(watchFired(ws)) 1057 1058 ws = memdb.NewWatchSet() 1059 out, err := state.NodeByID(ws, node.ID) 1060 require.Nil(err) 1061 1062 require.Equal(2, len(out.Events)) 1063 require.Equal(nodeEvent, out.Events[1]) 1064 } 1065 1066 // To prevent stale node events from accumulating, we limit the number of 1067 // stored node events to 10. 1068 func TestStateStore_NodeEvents_RetentionWindow(t *testing.T) { 1069 t.Parallel() 1070 require := require.New(t) 1071 1072 state := testStateStore(t) 1073 1074 node := mock.Node() 1075 1076 err := state.UpsertNode(1000, node) 1077 if err != nil { 1078 t.Fatalf("err: %v", err) 1079 } 1080 require.Equal(1, len(node.Events)) 1081 require.Equal(structs.NodeEventSubsystemCluster, node.Events[0].Subsystem) 1082 require.Equal(NodeRegisterEventRegistered, node.Events[0].Message) 1083 1084 var out *structs.Node 1085 for i := 1; i <= 20; i++ { 1086 ws := memdb.NewWatchSet() 1087 out, err = state.NodeByID(ws, node.ID) 1088 require.Nil(err) 1089 1090 nodeEvent := &structs.NodeEvent{ 1091 Message: fmt.Sprintf("%dith failed", i), 1092 Subsystem: "Driver", 1093 Timestamp: time.Now(), 1094 } 1095 1096 nodeEvents := map[string][]*structs.NodeEvent{ 1097 out.ID: {nodeEvent}, 1098 } 1099 err := state.UpsertNodeEvents(uint64(i), nodeEvents) 1100 require.Nil(err) 1101 1102 require.True(watchFired(ws)) 1103 ws = memdb.NewWatchSet() 1104 out, err = state.NodeByID(ws, node.ID) 1105 require.Nil(err) 1106 } 1107 1108 ws := memdb.NewWatchSet() 1109 out, err = state.NodeByID(ws, node.ID) 1110 require.Nil(err) 1111 1112 require.Equal(10, len(out.Events)) 1113 require.Equal(uint64(11), out.Events[0].CreateIndex) 1114 require.Equal(uint64(20), out.Events[len(out.Events)-1].CreateIndex) 1115 } 1116 1117 func TestStateStore_UpdateNodeDrain_ResetEligiblity(t *testing.T) { 1118 t.Parallel() 1119 require := require.New(t) 1120 1121 state := testStateStore(t) 1122 node := mock.Node() 1123 require.Nil(state.UpsertNode(1000, node)) 1124 1125 // Create a watchset so we can test that update node drain fires the watch 1126 ws := memdb.NewWatchSet() 1127 _, err := state.NodeByID(ws, node.ID) 1128 require.Nil(err) 1129 1130 drain := &structs.DrainStrategy{ 1131 DrainSpec: structs.DrainSpec{ 1132 Deadline: -1 * time.Second, 1133 }, 1134 } 1135 1136 event1 := &structs.NodeEvent{ 1137 Message: "Drain strategy enabled", 1138 Subsystem: structs.NodeEventSubsystemDrain, 1139 Timestamp: time.Now(), 1140 } 1141 require.Nil(state.UpdateNodeDrain(1001, node.ID, drain, false, 7, event1)) 1142 require.True(watchFired(ws)) 1143 1144 // Remove the drain 1145 event2 := &structs.NodeEvent{ 1146 Message: "Drain strategy disabled", 1147 Subsystem: structs.NodeEventSubsystemDrain, 1148 Timestamp: time.Now(), 1149 } 1150 require.Nil(state.UpdateNodeDrain(1002, node.ID, nil, true, 9, event2)) 1151 1152 ws = memdb.NewWatchSet() 1153 out, err := state.NodeByID(ws, node.ID) 1154 require.Nil(err) 1155 require.False(out.Drain) 1156 require.Nil(out.DrainStrategy) 1157 require.Equal(out.SchedulingEligibility, structs.NodeSchedulingEligible) 1158 require.Len(out.Events, 3) 1159 require.EqualValues(1002, out.ModifyIndex) 1160 require.EqualValues(9, out.StatusUpdatedAt) 1161 1162 index, err := state.Index("nodes") 1163 require.Nil(err) 1164 require.EqualValues(1002, index) 1165 require.False(watchFired(ws)) 1166 } 1167 1168 func TestStateStore_UpdateNodeEligibility(t *testing.T) { 1169 t.Parallel() 1170 require := require.New(t) 1171 1172 state := testStateStore(t) 1173 node := mock.Node() 1174 1175 err := state.UpsertNode(1000, node) 1176 if err != nil { 1177 t.Fatalf("err: %v", err) 1178 } 1179 1180 expectedEligibility := structs.NodeSchedulingIneligible 1181 1182 // Create a watchset so we can test that update node drain fires the watch 1183 ws := memdb.NewWatchSet() 1184 if _, err := state.NodeByID(ws, node.ID); err != nil { 1185 t.Fatalf("bad: %v", err) 1186 } 1187 1188 event := &structs.NodeEvent{ 1189 Message: "Node marked as ineligible", 1190 Subsystem: structs.NodeEventSubsystemCluster, 1191 Timestamp: time.Now(), 1192 } 1193 require.Nil(state.UpdateNodeEligibility(1001, node.ID, expectedEligibility, 7, event)) 1194 require.True(watchFired(ws)) 1195 1196 ws = memdb.NewWatchSet() 1197 out, err := state.NodeByID(ws, node.ID) 1198 require.Nil(err) 1199 require.Equal(out.SchedulingEligibility, expectedEligibility) 1200 require.Len(out.Events, 2) 1201 require.Equal(out.Events[1], event) 1202 require.EqualValues(1001, out.ModifyIndex) 1203 require.EqualValues(7, out.StatusUpdatedAt) 1204 1205 index, err := state.Index("nodes") 1206 require.Nil(err) 1207 require.EqualValues(1001, index) 1208 require.False(watchFired(ws)) 1209 1210 // Set a drain strategy 1211 expectedDrain := &structs.DrainStrategy{ 1212 DrainSpec: structs.DrainSpec{ 1213 Deadline: -1 * time.Second, 1214 }, 1215 } 1216 require.Nil(state.UpdateNodeDrain(1002, node.ID, expectedDrain, false, 7, nil)) 1217 1218 // Try to set the node to eligible 1219 err = state.UpdateNodeEligibility(1003, node.ID, structs.NodeSchedulingEligible, 9, nil) 1220 require.NotNil(err) 1221 require.Contains(err.Error(), "while it is draining") 1222 } 1223 1224 func TestStateStore_Nodes(t *testing.T) { 1225 t.Parallel() 1226 1227 state := testStateStore(t) 1228 var nodes []*structs.Node 1229 1230 for i := 0; i < 10; i++ { 1231 node := mock.Node() 1232 nodes = append(nodes, node) 1233 1234 err := state.UpsertNode(1000+uint64(i), node) 1235 if err != nil { 1236 t.Fatalf("err: %v", err) 1237 } 1238 } 1239 1240 // Create a watchset so we can test that getters don't cause it to fire 1241 ws := memdb.NewWatchSet() 1242 iter, err := state.Nodes(ws) 1243 if err != nil { 1244 t.Fatalf("bad: %v", err) 1245 } 1246 1247 var out []*structs.Node 1248 for { 1249 raw := iter.Next() 1250 if raw == nil { 1251 break 1252 } 1253 out = append(out, raw.(*structs.Node)) 1254 } 1255 1256 sort.Sort(NodeIDSort(nodes)) 1257 sort.Sort(NodeIDSort(out)) 1258 1259 if !reflect.DeepEqual(nodes, out) { 1260 t.Fatalf("bad: %#v %#v", nodes, out) 1261 } 1262 1263 if watchFired(ws) { 1264 t.Fatalf("bad") 1265 } 1266 } 1267 1268 func TestStateStore_NodesByIDPrefix(t *testing.T) { 1269 t.Parallel() 1270 1271 state := testStateStore(t) 1272 node := mock.Node() 1273 1274 node.ID = "11111111-662e-d0ab-d1c9-3e434af7bdb4" 1275 err := state.UpsertNode(1000, node) 1276 if err != nil { 1277 t.Fatalf("err: %v", err) 1278 } 1279 1280 // Create a watchset so we can test that getters don't cause it to fire 1281 ws := memdb.NewWatchSet() 1282 iter, err := state.NodesByIDPrefix(ws, node.ID) 1283 if err != nil { 1284 t.Fatalf("err: %v", err) 1285 } 1286 1287 gatherNodes := func(iter memdb.ResultIterator) []*structs.Node { 1288 var nodes []*structs.Node 1289 for { 1290 raw := iter.Next() 1291 if raw == nil { 1292 break 1293 } 1294 node := raw.(*structs.Node) 1295 nodes = append(nodes, node) 1296 } 1297 return nodes 1298 } 1299 1300 nodes := gatherNodes(iter) 1301 if len(nodes) != 1 { 1302 t.Fatalf("err: %v", err) 1303 } 1304 1305 if watchFired(ws) { 1306 t.Fatalf("bad") 1307 } 1308 1309 iter, err = state.NodesByIDPrefix(ws, "11") 1310 if err != nil { 1311 t.Fatalf("err: %v", err) 1312 } 1313 1314 nodes = gatherNodes(iter) 1315 if len(nodes) != 1 { 1316 t.Fatalf("err: %v", err) 1317 } 1318 1319 node = mock.Node() 1320 node.ID = "11222222-662e-d0ab-d1c9-3e434af7bdb4" 1321 err = state.UpsertNode(1001, node) 1322 if err != nil { 1323 t.Fatalf("err: %v", err) 1324 } 1325 1326 if !watchFired(ws) { 1327 t.Fatalf("bad") 1328 } 1329 1330 ws = memdb.NewWatchSet() 1331 iter, err = state.NodesByIDPrefix(ws, "11") 1332 if err != nil { 1333 t.Fatalf("err: %v", err) 1334 } 1335 1336 nodes = gatherNodes(iter) 1337 if len(nodes) != 2 { 1338 t.Fatalf("err: %v", err) 1339 } 1340 1341 iter, err = state.NodesByIDPrefix(ws, "1111") 1342 if err != nil { 1343 t.Fatalf("err: %v", err) 1344 } 1345 1346 nodes = gatherNodes(iter) 1347 if len(nodes) != 1 { 1348 t.Fatalf("err: %v", err) 1349 } 1350 1351 if watchFired(ws) { 1352 t.Fatalf("bad") 1353 } 1354 } 1355 1356 func TestStateStore_RestoreNode(t *testing.T) { 1357 t.Parallel() 1358 1359 state := testStateStore(t) 1360 node := mock.Node() 1361 1362 restore, err := state.Restore() 1363 if err != nil { 1364 t.Fatalf("err: %v", err) 1365 } 1366 1367 err = restore.NodeRestore(node) 1368 if err != nil { 1369 t.Fatalf("err: %v", err) 1370 } 1371 restore.Commit() 1372 1373 ws := memdb.NewWatchSet() 1374 out, err := state.NodeByID(ws, node.ID) 1375 if err != nil { 1376 t.Fatalf("err: %v", err) 1377 } 1378 1379 if !reflect.DeepEqual(out, node) { 1380 t.Fatalf("Bad: %#v %#v", out, node) 1381 } 1382 } 1383 1384 func TestStateStore_UpsertJob_Job(t *testing.T) { 1385 t.Parallel() 1386 1387 state := testStateStore(t) 1388 job := mock.Job() 1389 1390 // Create a watchset so we can test that upsert fires the watch 1391 ws := memdb.NewWatchSet() 1392 _, err := state.JobByID(ws, job.Namespace, job.ID) 1393 if err != nil { 1394 t.Fatalf("bad: %v", err) 1395 } 1396 1397 if err := state.UpsertJob(1000, job); err != nil { 1398 t.Fatalf("err: %v", err) 1399 } 1400 if !watchFired(ws) { 1401 t.Fatalf("bad") 1402 } 1403 1404 ws = memdb.NewWatchSet() 1405 out, err := state.JobByID(ws, job.Namespace, job.ID) 1406 if err != nil { 1407 t.Fatalf("err: %v", err) 1408 } 1409 1410 if !reflect.DeepEqual(job, out) { 1411 t.Fatalf("bad: %#v %#v", job, out) 1412 } 1413 1414 index, err := state.Index("jobs") 1415 if err != nil { 1416 t.Fatalf("err: %v", err) 1417 } 1418 if index != 1000 { 1419 t.Fatalf("bad: %d", index) 1420 } 1421 1422 summary, err := state.JobSummaryByID(ws, job.Namespace, job.ID) 1423 if err != nil { 1424 t.Fatalf("err: %v", err) 1425 } 1426 if summary == nil { 1427 t.Fatalf("nil summary") 1428 } 1429 if summary.JobID != job.ID { 1430 t.Fatalf("bad summary id: %v", summary.JobID) 1431 } 1432 _, ok := summary.Summary["web"] 1433 if !ok { 1434 t.Fatalf("nil summary for task group") 1435 } 1436 if watchFired(ws) { 1437 t.Fatalf("bad") 1438 } 1439 1440 // Check the job versions 1441 allVersions, err := state.JobVersionsByID(ws, job.Namespace, job.ID) 1442 if err != nil { 1443 t.Fatalf("err: %v", err) 1444 } 1445 if len(allVersions) != 1 { 1446 t.Fatalf("got %d; want 1", len(allVersions)) 1447 } 1448 1449 if a := allVersions[0]; a.ID != job.ID || a.Version != 0 { 1450 t.Fatalf("bad: %v", a) 1451 } 1452 1453 // Test the looking up the job by version returns the same results 1454 vout, err := state.JobByIDAndVersion(ws, job.Namespace, job.ID, 0) 1455 if err != nil { 1456 t.Fatalf("err: %v", err) 1457 } 1458 1459 if !reflect.DeepEqual(out, vout) { 1460 t.Fatalf("bad: %#v %#v", out, vout) 1461 } 1462 } 1463 1464 func TestStateStore_UpdateUpsertJob_Job(t *testing.T) { 1465 t.Parallel() 1466 1467 state := testStateStore(t) 1468 job := mock.Job() 1469 1470 // Create a watchset so we can test that upsert fires the watch 1471 ws := memdb.NewWatchSet() 1472 _, err := state.JobByID(ws, job.Namespace, job.ID) 1473 if err != nil { 1474 t.Fatalf("bad: %v", err) 1475 } 1476 1477 if err := state.UpsertJob(1000, job); err != nil { 1478 t.Fatalf("err: %v", err) 1479 } 1480 1481 job2 := mock.Job() 1482 job2.ID = job.ID 1483 job2.AllAtOnce = true 1484 err = state.UpsertJob(1001, job2) 1485 if err != nil { 1486 t.Fatalf("err: %v", err) 1487 } 1488 1489 if !watchFired(ws) { 1490 t.Fatalf("bad") 1491 } 1492 1493 ws = memdb.NewWatchSet() 1494 out, err := state.JobByID(ws, job.Namespace, job.ID) 1495 if err != nil { 1496 t.Fatalf("err: %v", err) 1497 } 1498 1499 if !reflect.DeepEqual(job2, out) { 1500 t.Fatalf("bad: %#v %#v", job2, out) 1501 } 1502 1503 if out.CreateIndex != 1000 { 1504 t.Fatalf("bad: %#v", out) 1505 } 1506 if out.ModifyIndex != 1001 { 1507 t.Fatalf("bad: %#v", out) 1508 } 1509 if out.Version != 1 { 1510 t.Fatalf("bad: %#v", out) 1511 } 1512 1513 index, err := state.Index("jobs") 1514 if err != nil { 1515 t.Fatalf("err: %v", err) 1516 } 1517 if index != 1001 { 1518 t.Fatalf("bad: %d", index) 1519 } 1520 1521 // Test the looking up the job by version returns the same results 1522 vout, err := state.JobByIDAndVersion(ws, job.Namespace, job.ID, 1) 1523 if err != nil { 1524 t.Fatalf("err: %v", err) 1525 } 1526 1527 if !reflect.DeepEqual(out, vout) { 1528 t.Fatalf("bad: %#v %#v", out, vout) 1529 } 1530 1531 // Test that the job summary remains the same if the job is updated but 1532 // count remains same 1533 summary, err := state.JobSummaryByID(ws, job.Namespace, job.ID) 1534 if err != nil { 1535 t.Fatalf("err: %v", err) 1536 } 1537 if summary == nil { 1538 t.Fatalf("nil summary") 1539 } 1540 if summary.JobID != job.ID { 1541 t.Fatalf("bad summary id: %v", summary.JobID) 1542 } 1543 _, ok := summary.Summary["web"] 1544 if !ok { 1545 t.Fatalf("nil summary for task group") 1546 } 1547 1548 // Check the job versions 1549 allVersions, err := state.JobVersionsByID(ws, job.Namespace, job.ID) 1550 if err != nil { 1551 t.Fatalf("err: %v", err) 1552 } 1553 if len(allVersions) != 2 { 1554 t.Fatalf("got %d; want 1", len(allVersions)) 1555 } 1556 1557 if a := allVersions[0]; a.ID != job.ID || a.Version != 1 || !a.AllAtOnce { 1558 t.Fatalf("bad: %+v", a) 1559 } 1560 if a := allVersions[1]; a.ID != job.ID || a.Version != 0 || a.AllAtOnce { 1561 t.Fatalf("bad: %+v", a) 1562 } 1563 1564 if watchFired(ws) { 1565 t.Fatalf("bad") 1566 } 1567 } 1568 1569 func TestStateStore_UpdateUpsertJob_PeriodicJob(t *testing.T) { 1570 t.Parallel() 1571 1572 state := testStateStore(t) 1573 job := mock.PeriodicJob() 1574 1575 // Create a watchset so we can test that upsert fires the watch 1576 ws := memdb.NewWatchSet() 1577 _, err := state.JobByID(ws, job.Namespace, job.ID) 1578 if err != nil { 1579 t.Fatalf("bad: %v", err) 1580 } 1581 1582 if err := state.UpsertJob(1000, job); err != nil { 1583 t.Fatalf("err: %v", err) 1584 } 1585 1586 // Create a child and an evaluation 1587 job2 := job.Copy() 1588 job2.Periodic = nil 1589 job2.ID = fmt.Sprintf("%v/%s-1490635020", job.ID, structs.PeriodicLaunchSuffix) 1590 err = state.UpsertJob(1001, job2) 1591 if err != nil { 1592 t.Fatalf("err: %v", err) 1593 } 1594 1595 eval := mock.Eval() 1596 eval.JobID = job2.ID 1597 err = state.UpsertEvals(1002, []*structs.Evaluation{eval}) 1598 if err != nil { 1599 t.Fatalf("err: %v", err) 1600 } 1601 1602 job3 := job.Copy() 1603 job3.TaskGroups[0].Tasks[0].Name = "new name" 1604 err = state.UpsertJob(1003, job3) 1605 if err != nil { 1606 t.Fatalf("err: %v", err) 1607 } 1608 1609 if !watchFired(ws) { 1610 t.Fatalf("bad") 1611 } 1612 1613 ws = memdb.NewWatchSet() 1614 out, err := state.JobByID(ws, job.Namespace, job.ID) 1615 if err != nil { 1616 t.Fatalf("err: %v", err) 1617 } 1618 1619 if s, e := out.Status, structs.JobStatusRunning; s != e { 1620 t.Fatalf("got status %v; want %v", s, e) 1621 } 1622 1623 } 1624 1625 func TestStateStore_UpsertJob_BadNamespace(t *testing.T) { 1626 t.Parallel() 1627 1628 assert := assert.New(t) 1629 state := testStateStore(t) 1630 job := mock.Job() 1631 job.Namespace = "foo" 1632 1633 err := state.UpsertJob(1000, job) 1634 assert.Contains(err.Error(), "nonexistent namespace") 1635 1636 ws := memdb.NewWatchSet() 1637 out, err := state.JobByID(ws, job.Namespace, job.ID) 1638 assert.Nil(err) 1639 assert.Nil(out) 1640 } 1641 1642 // Upsert a job that is the child of a parent job and ensures its summary gets 1643 // updated. 1644 func TestStateStore_UpsertJob_ChildJob(t *testing.T) { 1645 t.Parallel() 1646 1647 state := testStateStore(t) 1648 1649 // Create a watchset so we can test that upsert fires the watch 1650 parent := mock.Job() 1651 ws := memdb.NewWatchSet() 1652 _, err := state.JobByID(ws, parent.Namespace, parent.ID) 1653 if err != nil { 1654 t.Fatalf("bad: %v", err) 1655 } 1656 1657 if err := state.UpsertJob(1000, parent); err != nil { 1658 t.Fatalf("err: %v", err) 1659 } 1660 1661 child := mock.Job() 1662 child.ParentID = parent.ID 1663 if err := state.UpsertJob(1001, child); err != nil { 1664 t.Fatalf("err: %v", err) 1665 } 1666 1667 summary, err := state.JobSummaryByID(ws, parent.Namespace, parent.ID) 1668 if err != nil { 1669 t.Fatalf("err: %v", err) 1670 } 1671 if summary == nil { 1672 t.Fatalf("nil summary") 1673 } 1674 if summary.JobID != parent.ID { 1675 t.Fatalf("bad summary id: %v", parent.ID) 1676 } 1677 if summary.Children == nil { 1678 t.Fatalf("nil children summary") 1679 } 1680 if summary.Children.Pending != 1 || summary.Children.Running != 0 || summary.Children.Dead != 0 { 1681 t.Fatalf("bad children summary: %v", summary.Children) 1682 } 1683 if !watchFired(ws) { 1684 t.Fatalf("bad") 1685 } 1686 } 1687 1688 func TestStateStore_UpdateUpsertJob_JobVersion(t *testing.T) { 1689 t.Parallel() 1690 1691 state := testStateStore(t) 1692 1693 // Create a job and mark it as stable 1694 job := mock.Job() 1695 job.Stable = true 1696 job.Name = "0" 1697 1698 // Create a watchset so we can test that upsert fires the watch 1699 ws := memdb.NewWatchSet() 1700 _, err := state.JobVersionsByID(ws, job.Namespace, job.ID) 1701 if err != nil { 1702 t.Fatalf("bad: %v", err) 1703 } 1704 1705 if err := state.UpsertJob(1000, job); err != nil { 1706 t.Fatalf("err: %v", err) 1707 } 1708 1709 if !watchFired(ws) { 1710 t.Fatalf("bad") 1711 } 1712 1713 var finalJob *structs.Job 1714 for i := 1; i < 300; i++ { 1715 finalJob = mock.Job() 1716 finalJob.ID = job.ID 1717 finalJob.Name = fmt.Sprintf("%d", i) 1718 err = state.UpsertJob(uint64(1000+i), finalJob) 1719 if err != nil { 1720 t.Fatalf("err: %v", err) 1721 } 1722 } 1723 1724 ws = memdb.NewWatchSet() 1725 out, err := state.JobByID(ws, job.Namespace, job.ID) 1726 if err != nil { 1727 t.Fatalf("err: %v", err) 1728 } 1729 1730 if !reflect.DeepEqual(finalJob, out) { 1731 t.Fatalf("bad: %#v %#v", finalJob, out) 1732 } 1733 1734 if out.CreateIndex != 1000 { 1735 t.Fatalf("bad: %#v", out) 1736 } 1737 if out.ModifyIndex != 1299 { 1738 t.Fatalf("bad: %#v", out) 1739 } 1740 if out.Version != 299 { 1741 t.Fatalf("bad: %#v", out) 1742 } 1743 1744 index, err := state.Index("job_version") 1745 if err != nil { 1746 t.Fatalf("err: %v", err) 1747 } 1748 if index != 1299 { 1749 t.Fatalf("bad: %d", index) 1750 } 1751 1752 // Check the job versions 1753 allVersions, err := state.JobVersionsByID(ws, job.Namespace, job.ID) 1754 if err != nil { 1755 t.Fatalf("err: %v", err) 1756 } 1757 if len(allVersions) != structs.JobTrackedVersions { 1758 t.Fatalf("got %d; want %d", len(allVersions), structs.JobTrackedVersions) 1759 } 1760 1761 if a := allVersions[0]; a.ID != job.ID || a.Version != 299 || a.Name != "299" { 1762 t.Fatalf("bad: %+v", a) 1763 } 1764 if a := allVersions[1]; a.ID != job.ID || a.Version != 298 || a.Name != "298" { 1765 t.Fatalf("bad: %+v", a) 1766 } 1767 1768 // Ensure we didn't delete the stable job 1769 if a := allVersions[structs.JobTrackedVersions-1]; a.ID != job.ID || 1770 a.Version != 0 || a.Name != "0" || !a.Stable { 1771 t.Fatalf("bad: %+v", a) 1772 } 1773 1774 if watchFired(ws) { 1775 t.Fatalf("bad") 1776 } 1777 } 1778 1779 func TestStateStore_DeleteJob_Job(t *testing.T) { 1780 t.Parallel() 1781 1782 state := testStateStore(t) 1783 job := mock.Job() 1784 1785 err := state.UpsertJob(1000, job) 1786 if err != nil { 1787 t.Fatalf("err: %v", err) 1788 } 1789 1790 // Create a watchset so we can test that delete fires the watch 1791 ws := memdb.NewWatchSet() 1792 if _, err := state.JobByID(ws, job.Namespace, job.ID); err != nil { 1793 t.Fatalf("bad: %v", err) 1794 } 1795 1796 err = state.DeleteJob(1001, job.Namespace, job.ID) 1797 if err != nil { 1798 t.Fatalf("err: %v", err) 1799 } 1800 1801 if !watchFired(ws) { 1802 t.Fatalf("bad") 1803 } 1804 1805 ws = memdb.NewWatchSet() 1806 out, err := state.JobByID(ws, job.Namespace, job.ID) 1807 if err != nil { 1808 t.Fatalf("err: %v", err) 1809 } 1810 1811 if out != nil { 1812 t.Fatalf("bad: %#v %#v", job, out) 1813 } 1814 1815 index, err := state.Index("jobs") 1816 if err != nil { 1817 t.Fatalf("err: %v", err) 1818 } 1819 if index != 1001 { 1820 t.Fatalf("bad: %d", index) 1821 } 1822 1823 summary, err := state.JobSummaryByID(ws, job.Namespace, job.ID) 1824 if err != nil { 1825 t.Fatalf("err: %v", err) 1826 } 1827 if summary != nil { 1828 t.Fatalf("expected summary to be nil, but got: %v", summary) 1829 } 1830 1831 index, err = state.Index("job_summary") 1832 if err != nil { 1833 t.Fatalf("err: %v", err) 1834 } 1835 if index != 1001 { 1836 t.Fatalf("bad: %d", index) 1837 } 1838 1839 versions, err := state.JobVersionsByID(ws, job.Namespace, job.ID) 1840 if err != nil { 1841 t.Fatalf("err: %v", err) 1842 } 1843 if len(versions) != 0 { 1844 t.Fatalf("expected no job versions") 1845 } 1846 1847 index, err = state.Index("job_summary") 1848 if err != nil { 1849 t.Fatalf("err: %v", err) 1850 } 1851 if index != 1001 { 1852 t.Fatalf("bad: %d", index) 1853 } 1854 1855 if watchFired(ws) { 1856 t.Fatalf("bad") 1857 } 1858 } 1859 1860 func TestStateStore_DeleteJobTxn_BatchDeletes(t *testing.T) { 1861 t.Parallel() 1862 1863 state := testStateStore(t) 1864 1865 const testJobCount = 10 1866 const jobVersionCount = 4 1867 1868 stateIndex := uint64(1000) 1869 1870 jobs := make([]*structs.Job, testJobCount) 1871 for i := 0; i < testJobCount; i++ { 1872 stateIndex++ 1873 job := mock.BatchJob() 1874 1875 err := state.UpsertJob(stateIndex, job) 1876 require.NoError(t, err) 1877 1878 jobs[i] = job 1879 1880 // Create some versions 1881 for vi := 1; vi < jobVersionCount; vi++ { 1882 stateIndex++ 1883 1884 job := job.Copy() 1885 job.TaskGroups[0].Tasks[0].Env = map[string]string{ 1886 "Version": fmt.Sprintf("%d", vi), 1887 } 1888 1889 require.NoError(t, state.UpsertJob(stateIndex, job)) 1890 } 1891 } 1892 1893 ws := memdb.NewWatchSet() 1894 1895 // Sanity check that jobs are present in DB 1896 job, err := state.JobByID(ws, jobs[0].Namespace, jobs[0].ID) 1897 require.NoError(t, err) 1898 require.Equal(t, jobs[0].ID, job.ID) 1899 1900 jobVersions, err := state.JobVersionsByID(ws, jobs[0].Namespace, jobs[0].ID) 1901 require.NoError(t, err) 1902 require.Equal(t, jobVersionCount, len(jobVersions)) 1903 1904 // Actually delete 1905 const deletionIndex = uint64(10001) 1906 err = state.WithWriteTransaction(func(txn Txn) error { 1907 for i, job := range jobs { 1908 err := state.DeleteJobTxn(deletionIndex, job.Namespace, job.ID, txn) 1909 require.NoError(t, err, "failed at %d %e", i, err) 1910 } 1911 return nil 1912 }) 1913 assert.NoError(t, err) 1914 1915 assert.True(t, watchFired(ws)) 1916 1917 ws = memdb.NewWatchSet() 1918 out, err := state.JobByID(ws, jobs[0].Namespace, jobs[0].ID) 1919 require.NoError(t, err) 1920 require.Nil(t, out) 1921 1922 jobVersions, err = state.JobVersionsByID(ws, jobs[0].Namespace, jobs[0].ID) 1923 require.NoError(t, err) 1924 require.Empty(t, jobVersions) 1925 1926 index, err := state.Index("jobs") 1927 require.NoError(t, err) 1928 require.Equal(t, deletionIndex, index) 1929 } 1930 1931 func TestStateStore_DeleteJob_MultipleVersions(t *testing.T) { 1932 t.Parallel() 1933 1934 state := testStateStore(t) 1935 assert := assert.New(t) 1936 1937 // Create a job and mark it as stable 1938 job := mock.Job() 1939 job.Stable = true 1940 job.Priority = 0 1941 1942 // Create a watchset so we can test that upsert fires the watch 1943 ws := memdb.NewWatchSet() 1944 _, err := state.JobVersionsByID(ws, job.Namespace, job.ID) 1945 assert.Nil(err) 1946 assert.Nil(state.UpsertJob(1000, job)) 1947 assert.True(watchFired(ws)) 1948 1949 var finalJob *structs.Job 1950 for i := 1; i < 20; i++ { 1951 finalJob = mock.Job() 1952 finalJob.ID = job.ID 1953 finalJob.Priority = i 1954 assert.Nil(state.UpsertJob(uint64(1000+i), finalJob)) 1955 } 1956 1957 assert.Nil(state.DeleteJob(1020, job.Namespace, job.ID)) 1958 assert.True(watchFired(ws)) 1959 1960 ws = memdb.NewWatchSet() 1961 out, err := state.JobByID(ws, job.Namespace, job.ID) 1962 assert.Nil(err) 1963 assert.Nil(out) 1964 1965 index, err := state.Index("jobs") 1966 assert.Nil(err) 1967 assert.EqualValues(1020, index) 1968 1969 summary, err := state.JobSummaryByID(ws, job.Namespace, job.ID) 1970 assert.Nil(err) 1971 assert.Nil(summary) 1972 1973 index, err = state.Index("job_version") 1974 assert.Nil(err) 1975 assert.EqualValues(1020, index) 1976 1977 versions, err := state.JobVersionsByID(ws, job.Namespace, job.ID) 1978 assert.Nil(err) 1979 assert.Len(versions, 0) 1980 1981 index, err = state.Index("job_summary") 1982 assert.Nil(err) 1983 assert.EqualValues(1020, index) 1984 1985 assert.False(watchFired(ws)) 1986 } 1987 1988 func TestStateStore_DeleteJob_ChildJob(t *testing.T) { 1989 t.Parallel() 1990 1991 state := testStateStore(t) 1992 1993 parent := mock.Job() 1994 if err := state.UpsertJob(998, parent); err != nil { 1995 t.Fatalf("err: %v", err) 1996 } 1997 1998 child := mock.Job() 1999 child.ParentID = parent.ID 2000 2001 if err := state.UpsertJob(999, child); err != nil { 2002 t.Fatalf("err: %v", err) 2003 } 2004 2005 // Create a watchset so we can test that delete fires the watch 2006 ws := memdb.NewWatchSet() 2007 if _, err := state.JobSummaryByID(ws, parent.Namespace, parent.ID); err != nil { 2008 t.Fatalf("bad: %v", err) 2009 } 2010 2011 err := state.DeleteJob(1001, child.Namespace, child.ID) 2012 if err != nil { 2013 t.Fatalf("err: %v", err) 2014 } 2015 if !watchFired(ws) { 2016 t.Fatalf("bad") 2017 } 2018 2019 ws = memdb.NewWatchSet() 2020 summary, err := state.JobSummaryByID(ws, parent.Namespace, parent.ID) 2021 if err != nil { 2022 t.Fatalf("err: %v", err) 2023 } 2024 if summary == nil { 2025 t.Fatalf("nil summary") 2026 } 2027 if summary.JobID != parent.ID { 2028 t.Fatalf("bad summary id: %v", parent.ID) 2029 } 2030 if summary.Children == nil { 2031 t.Fatalf("nil children summary") 2032 } 2033 if summary.Children.Pending != 0 || summary.Children.Running != 0 || summary.Children.Dead != 1 { 2034 t.Fatalf("bad children summary: %v", summary.Children) 2035 } 2036 if watchFired(ws) { 2037 t.Fatalf("bad") 2038 } 2039 } 2040 2041 func TestStateStore_Jobs(t *testing.T) { 2042 t.Parallel() 2043 2044 state := testStateStore(t) 2045 var jobs []*structs.Job 2046 2047 for i := 0; i < 10; i++ { 2048 job := mock.Job() 2049 jobs = append(jobs, job) 2050 2051 err := state.UpsertJob(1000+uint64(i), job) 2052 if err != nil { 2053 t.Fatalf("err: %v", err) 2054 } 2055 } 2056 2057 ws := memdb.NewWatchSet() 2058 iter, err := state.Jobs(ws) 2059 if err != nil { 2060 t.Fatalf("err: %v", err) 2061 } 2062 2063 var out []*structs.Job 2064 for { 2065 raw := iter.Next() 2066 if raw == nil { 2067 break 2068 } 2069 out = append(out, raw.(*structs.Job)) 2070 } 2071 2072 sort.Sort(JobIDSort(jobs)) 2073 sort.Sort(JobIDSort(out)) 2074 2075 if !reflect.DeepEqual(jobs, out) { 2076 t.Fatalf("bad: %#v %#v", jobs, out) 2077 } 2078 if watchFired(ws) { 2079 t.Fatalf("bad") 2080 } 2081 } 2082 2083 func TestStateStore_JobVersions(t *testing.T) { 2084 t.Parallel() 2085 2086 state := testStateStore(t) 2087 var jobs []*structs.Job 2088 2089 for i := 0; i < 10; i++ { 2090 job := mock.Job() 2091 jobs = append(jobs, job) 2092 2093 err := state.UpsertJob(1000+uint64(i), job) 2094 if err != nil { 2095 t.Fatalf("err: %v", err) 2096 } 2097 } 2098 2099 ws := memdb.NewWatchSet() 2100 iter, err := state.JobVersions(ws) 2101 if err != nil { 2102 t.Fatalf("err: %v", err) 2103 } 2104 2105 var out []*structs.Job 2106 for { 2107 raw := iter.Next() 2108 if raw == nil { 2109 break 2110 } 2111 out = append(out, raw.(*structs.Job)) 2112 } 2113 2114 sort.Sort(JobIDSort(jobs)) 2115 sort.Sort(JobIDSort(out)) 2116 2117 if !reflect.DeepEqual(jobs, out) { 2118 t.Fatalf("bad: %#v %#v", jobs, out) 2119 } 2120 if watchFired(ws) { 2121 t.Fatalf("bad") 2122 } 2123 } 2124 2125 func TestStateStore_JobsByIDPrefix(t *testing.T) { 2126 t.Parallel() 2127 2128 state := testStateStore(t) 2129 job := mock.Job() 2130 2131 job.ID = "redis" 2132 err := state.UpsertJob(1000, job) 2133 if err != nil { 2134 t.Fatalf("err: %v", err) 2135 } 2136 2137 ws := memdb.NewWatchSet() 2138 iter, err := state.JobsByIDPrefix(ws, job.Namespace, job.ID) 2139 if err != nil { 2140 t.Fatalf("err: %v", err) 2141 } 2142 2143 gatherJobs := func(iter memdb.ResultIterator) []*structs.Job { 2144 var jobs []*structs.Job 2145 for { 2146 raw := iter.Next() 2147 if raw == nil { 2148 break 2149 } 2150 jobs = append(jobs, raw.(*structs.Job)) 2151 } 2152 return jobs 2153 } 2154 2155 jobs := gatherJobs(iter) 2156 if len(jobs) != 1 { 2157 t.Fatalf("err: %v", err) 2158 } 2159 2160 iter, err = state.JobsByIDPrefix(ws, job.Namespace, "re") 2161 if err != nil { 2162 t.Fatalf("err: %v", err) 2163 } 2164 2165 jobs = gatherJobs(iter) 2166 if len(jobs) != 1 { 2167 t.Fatalf("err: %v", err) 2168 } 2169 if watchFired(ws) { 2170 t.Fatalf("bad") 2171 } 2172 2173 job = mock.Job() 2174 job.ID = "riak" 2175 err = state.UpsertJob(1001, job) 2176 if err != nil { 2177 t.Fatalf("err: %v", err) 2178 } 2179 2180 if !watchFired(ws) { 2181 t.Fatalf("bad") 2182 } 2183 2184 ws = memdb.NewWatchSet() 2185 iter, err = state.JobsByIDPrefix(ws, job.Namespace, "r") 2186 if err != nil { 2187 t.Fatalf("err: %v", err) 2188 } 2189 2190 jobs = gatherJobs(iter) 2191 if len(jobs) != 2 { 2192 t.Fatalf("err: %v", err) 2193 } 2194 2195 iter, err = state.JobsByIDPrefix(ws, job.Namespace, "ri") 2196 if err != nil { 2197 t.Fatalf("err: %v", err) 2198 } 2199 2200 jobs = gatherJobs(iter) 2201 if len(jobs) != 1 { 2202 t.Fatalf("err: %v", err) 2203 } 2204 if watchFired(ws) { 2205 t.Fatalf("bad") 2206 } 2207 } 2208 2209 func TestStateStore_JobsByPeriodic(t *testing.T) { 2210 t.Parallel() 2211 2212 state := testStateStore(t) 2213 var periodic, nonPeriodic []*structs.Job 2214 2215 for i := 0; i < 10; i++ { 2216 job := mock.Job() 2217 nonPeriodic = append(nonPeriodic, job) 2218 2219 err := state.UpsertJob(1000+uint64(i), job) 2220 if err != nil { 2221 t.Fatalf("err: %v", err) 2222 } 2223 } 2224 2225 for i := 0; i < 10; i++ { 2226 job := mock.PeriodicJob() 2227 periodic = append(periodic, job) 2228 2229 err := state.UpsertJob(2000+uint64(i), job) 2230 if err != nil { 2231 t.Fatalf("err: %v", err) 2232 } 2233 } 2234 2235 ws := memdb.NewWatchSet() 2236 iter, err := state.JobsByPeriodic(ws, true) 2237 if err != nil { 2238 t.Fatalf("err: %v", err) 2239 } 2240 2241 var outPeriodic []*structs.Job 2242 for { 2243 raw := iter.Next() 2244 if raw == nil { 2245 break 2246 } 2247 outPeriodic = append(outPeriodic, raw.(*structs.Job)) 2248 } 2249 2250 iter, err = state.JobsByPeriodic(ws, false) 2251 if err != nil { 2252 t.Fatalf("err: %v", err) 2253 } 2254 2255 var outNonPeriodic []*structs.Job 2256 for { 2257 raw := iter.Next() 2258 if raw == nil { 2259 break 2260 } 2261 outNonPeriodic = append(outNonPeriodic, raw.(*structs.Job)) 2262 } 2263 2264 sort.Sort(JobIDSort(periodic)) 2265 sort.Sort(JobIDSort(nonPeriodic)) 2266 sort.Sort(JobIDSort(outPeriodic)) 2267 sort.Sort(JobIDSort(outNonPeriodic)) 2268 2269 if !reflect.DeepEqual(periodic, outPeriodic) { 2270 t.Fatalf("bad: %#v %#v", periodic, outPeriodic) 2271 } 2272 2273 if !reflect.DeepEqual(nonPeriodic, outNonPeriodic) { 2274 t.Fatalf("bad: %#v %#v", nonPeriodic, outNonPeriodic) 2275 } 2276 if watchFired(ws) { 2277 t.Fatalf("bad") 2278 } 2279 } 2280 2281 func TestStateStore_JobsByScheduler(t *testing.T) { 2282 t.Parallel() 2283 2284 state := testStateStore(t) 2285 var serviceJobs []*structs.Job 2286 var sysJobs []*structs.Job 2287 2288 for i := 0; i < 10; i++ { 2289 job := mock.Job() 2290 serviceJobs = append(serviceJobs, job) 2291 2292 err := state.UpsertJob(1000+uint64(i), job) 2293 if err != nil { 2294 t.Fatalf("err: %v", err) 2295 } 2296 } 2297 2298 for i := 0; i < 10; i++ { 2299 job := mock.SystemJob() 2300 job.Status = structs.JobStatusRunning 2301 sysJobs = append(sysJobs, job) 2302 2303 err := state.UpsertJob(2000+uint64(i), job) 2304 if err != nil { 2305 t.Fatalf("err: %v", err) 2306 } 2307 } 2308 2309 ws := memdb.NewWatchSet() 2310 iter, err := state.JobsByScheduler(ws, "service") 2311 if err != nil { 2312 t.Fatalf("err: %v", err) 2313 } 2314 2315 var outService []*structs.Job 2316 for { 2317 raw := iter.Next() 2318 if raw == nil { 2319 break 2320 } 2321 outService = append(outService, raw.(*structs.Job)) 2322 } 2323 2324 iter, err = state.JobsByScheduler(ws, "system") 2325 if err != nil { 2326 t.Fatalf("err: %v", err) 2327 } 2328 2329 var outSystem []*structs.Job 2330 for { 2331 raw := iter.Next() 2332 if raw == nil { 2333 break 2334 } 2335 outSystem = append(outSystem, raw.(*structs.Job)) 2336 } 2337 2338 sort.Sort(JobIDSort(serviceJobs)) 2339 sort.Sort(JobIDSort(sysJobs)) 2340 sort.Sort(JobIDSort(outService)) 2341 sort.Sort(JobIDSort(outSystem)) 2342 2343 if !reflect.DeepEqual(serviceJobs, outService) { 2344 t.Fatalf("bad: %#v %#v", serviceJobs, outService) 2345 } 2346 2347 if !reflect.DeepEqual(sysJobs, outSystem) { 2348 t.Fatalf("bad: %#v %#v", sysJobs, outSystem) 2349 } 2350 if watchFired(ws) { 2351 t.Fatalf("bad") 2352 } 2353 } 2354 2355 func TestStateStore_JobsByGC(t *testing.T) { 2356 t.Parallel() 2357 2358 state := testStateStore(t) 2359 gc, nonGc := make(map[string]struct{}), make(map[string]struct{}) 2360 2361 for i := 0; i < 20; i++ { 2362 var job *structs.Job 2363 if i%2 == 0 { 2364 job = mock.Job() 2365 } else { 2366 job = mock.PeriodicJob() 2367 } 2368 nonGc[job.ID] = struct{}{} 2369 2370 if err := state.UpsertJob(1000+uint64(i), job); err != nil { 2371 t.Fatalf("err: %v", err) 2372 } 2373 } 2374 2375 for i := 0; i < 20; i += 2 { 2376 job := mock.Job() 2377 job.Type = structs.JobTypeBatch 2378 gc[job.ID] = struct{}{} 2379 2380 if err := state.UpsertJob(2000+uint64(i), job); err != nil { 2381 t.Fatalf("err: %v", err) 2382 } 2383 2384 // Create an eval for it 2385 eval := mock.Eval() 2386 eval.JobID = job.ID 2387 eval.Status = structs.EvalStatusComplete 2388 if err := state.UpsertEvals(2000+uint64(i+1), []*structs.Evaluation{eval}); err != nil { 2389 t.Fatalf("err: %v", err) 2390 } 2391 2392 } 2393 2394 ws := memdb.NewWatchSet() 2395 iter, err := state.JobsByGC(ws, true) 2396 if err != nil { 2397 t.Fatalf("err: %v", err) 2398 } 2399 2400 outGc := make(map[string]struct{}) 2401 for i := iter.Next(); i != nil; i = iter.Next() { 2402 j := i.(*structs.Job) 2403 outGc[j.ID] = struct{}{} 2404 } 2405 2406 iter, err = state.JobsByGC(ws, false) 2407 if err != nil { 2408 t.Fatalf("err: %v", err) 2409 } 2410 2411 outNonGc := make(map[string]struct{}) 2412 for i := iter.Next(); i != nil; i = iter.Next() { 2413 j := i.(*structs.Job) 2414 outNonGc[j.ID] = struct{}{} 2415 } 2416 2417 if !reflect.DeepEqual(gc, outGc) { 2418 t.Fatalf("bad: %#v %#v", gc, outGc) 2419 } 2420 2421 if !reflect.DeepEqual(nonGc, outNonGc) { 2422 t.Fatalf("bad: %#v %#v", nonGc, outNonGc) 2423 } 2424 if watchFired(ws) { 2425 t.Fatalf("bad") 2426 } 2427 } 2428 2429 func TestStateStore_RestoreJob(t *testing.T) { 2430 t.Parallel() 2431 2432 state := testStateStore(t) 2433 job := mock.Job() 2434 2435 restore, err := state.Restore() 2436 if err != nil { 2437 t.Fatalf("err: %v", err) 2438 } 2439 2440 err = restore.JobRestore(job) 2441 if err != nil { 2442 t.Fatalf("err: %v", err) 2443 } 2444 restore.Commit() 2445 2446 ws := memdb.NewWatchSet() 2447 out, err := state.JobByID(ws, job.Namespace, job.ID) 2448 if err != nil { 2449 t.Fatalf("err: %v", err) 2450 } 2451 2452 if !reflect.DeepEqual(out, job) { 2453 t.Fatalf("Bad: %#v %#v", out, job) 2454 } 2455 } 2456 2457 func TestStateStore_UpsertPeriodicLaunch(t *testing.T) { 2458 t.Parallel() 2459 2460 state := testStateStore(t) 2461 job := mock.Job() 2462 launch := &structs.PeriodicLaunch{ 2463 ID: job.ID, 2464 Namespace: job.Namespace, 2465 Launch: time.Now(), 2466 } 2467 2468 // Create a watchset so we can test that upsert fires the watch 2469 ws := memdb.NewWatchSet() 2470 if _, err := state.PeriodicLaunchByID(ws, job.Namespace, launch.ID); err != nil { 2471 t.Fatalf("bad: %v", err) 2472 } 2473 2474 err := state.UpsertPeriodicLaunch(1000, launch) 2475 if err != nil { 2476 t.Fatalf("err: %v", err) 2477 } 2478 2479 if !watchFired(ws) { 2480 t.Fatalf("bad") 2481 } 2482 2483 ws = memdb.NewWatchSet() 2484 out, err := state.PeriodicLaunchByID(ws, job.Namespace, job.ID) 2485 if err != nil { 2486 t.Fatalf("err: %v", err) 2487 } 2488 if out.CreateIndex != 1000 { 2489 t.Fatalf("bad: %#v", out) 2490 } 2491 if out.ModifyIndex != 1000 { 2492 t.Fatalf("bad: %#v", out) 2493 } 2494 2495 if !reflect.DeepEqual(launch, out) { 2496 t.Fatalf("bad: %#v %#v", job, out) 2497 } 2498 2499 index, err := state.Index("periodic_launch") 2500 if err != nil { 2501 t.Fatalf("err: %v", err) 2502 } 2503 if index != 1000 { 2504 t.Fatalf("bad: %d", index) 2505 } 2506 2507 if watchFired(ws) { 2508 t.Fatalf("bad") 2509 } 2510 } 2511 2512 func TestStateStore_UpdateUpsertPeriodicLaunch(t *testing.T) { 2513 t.Parallel() 2514 2515 state := testStateStore(t) 2516 job := mock.Job() 2517 launch := &structs.PeriodicLaunch{ 2518 ID: job.ID, 2519 Namespace: job.Namespace, 2520 Launch: time.Now(), 2521 } 2522 2523 err := state.UpsertPeriodicLaunch(1000, launch) 2524 if err != nil { 2525 t.Fatalf("err: %v", err) 2526 } 2527 2528 // Create a watchset so we can test that upsert fires the watch 2529 ws := memdb.NewWatchSet() 2530 if _, err := state.PeriodicLaunchByID(ws, job.Namespace, launch.ID); err != nil { 2531 t.Fatalf("bad: %v", err) 2532 } 2533 2534 launch2 := &structs.PeriodicLaunch{ 2535 ID: job.ID, 2536 Namespace: job.Namespace, 2537 Launch: launch.Launch.Add(1 * time.Second), 2538 } 2539 err = state.UpsertPeriodicLaunch(1001, launch2) 2540 if err != nil { 2541 t.Fatalf("err: %v", err) 2542 } 2543 2544 if !watchFired(ws) { 2545 t.Fatalf("bad") 2546 } 2547 2548 ws = memdb.NewWatchSet() 2549 out, err := state.PeriodicLaunchByID(ws, job.Namespace, job.ID) 2550 if err != nil { 2551 t.Fatalf("err: %v", err) 2552 } 2553 if out.CreateIndex != 1000 { 2554 t.Fatalf("bad: %#v", out) 2555 } 2556 if out.ModifyIndex != 1001 { 2557 t.Fatalf("bad: %#v", out) 2558 } 2559 2560 if !reflect.DeepEqual(launch2, out) { 2561 t.Fatalf("bad: %#v %#v", launch2, out) 2562 } 2563 2564 index, err := state.Index("periodic_launch") 2565 if err != nil { 2566 t.Fatalf("err: %v", err) 2567 } 2568 if index != 1001 { 2569 t.Fatalf("bad: %d", index) 2570 } 2571 2572 if watchFired(ws) { 2573 t.Fatalf("bad") 2574 } 2575 } 2576 2577 func TestStateStore_DeletePeriodicLaunch(t *testing.T) { 2578 t.Parallel() 2579 2580 state := testStateStore(t) 2581 job := mock.Job() 2582 launch := &structs.PeriodicLaunch{ 2583 ID: job.ID, 2584 Namespace: job.Namespace, 2585 Launch: time.Now(), 2586 } 2587 2588 err := state.UpsertPeriodicLaunch(1000, launch) 2589 if err != nil { 2590 t.Fatalf("err: %v", err) 2591 } 2592 2593 // Create a watchset so we can test that delete fires the watch 2594 ws := memdb.NewWatchSet() 2595 if _, err := state.PeriodicLaunchByID(ws, job.Namespace, launch.ID); err != nil { 2596 t.Fatalf("bad: %v", err) 2597 } 2598 2599 err = state.DeletePeriodicLaunch(1001, launch.Namespace, launch.ID) 2600 if err != nil { 2601 t.Fatalf("err: %v", err) 2602 } 2603 2604 if !watchFired(ws) { 2605 t.Fatalf("bad") 2606 } 2607 2608 ws = memdb.NewWatchSet() 2609 out, err := state.PeriodicLaunchByID(ws, job.Namespace, job.ID) 2610 if err != nil { 2611 t.Fatalf("err: %v", err) 2612 } 2613 2614 if out != nil { 2615 t.Fatalf("bad: %#v %#v", job, out) 2616 } 2617 2618 index, err := state.Index("periodic_launch") 2619 if err != nil { 2620 t.Fatalf("err: %v", err) 2621 } 2622 if index != 1001 { 2623 t.Fatalf("bad: %d", index) 2624 } 2625 2626 if watchFired(ws) { 2627 t.Fatalf("bad") 2628 } 2629 } 2630 2631 func TestStateStore_PeriodicLaunches(t *testing.T) { 2632 t.Parallel() 2633 2634 state := testStateStore(t) 2635 var launches []*structs.PeriodicLaunch 2636 2637 for i := 0; i < 10; i++ { 2638 job := mock.Job() 2639 launch := &structs.PeriodicLaunch{ 2640 ID: job.ID, 2641 Namespace: job.Namespace, 2642 Launch: time.Now(), 2643 } 2644 launches = append(launches, launch) 2645 2646 err := state.UpsertPeriodicLaunch(1000+uint64(i), launch) 2647 if err != nil { 2648 t.Fatalf("err: %v", err) 2649 } 2650 } 2651 2652 ws := memdb.NewWatchSet() 2653 iter, err := state.PeriodicLaunches(ws) 2654 if err != nil { 2655 t.Fatalf("err: %v", err) 2656 } 2657 2658 out := make(map[string]*structs.PeriodicLaunch, 10) 2659 for { 2660 raw := iter.Next() 2661 if raw == nil { 2662 break 2663 } 2664 launch := raw.(*structs.PeriodicLaunch) 2665 if _, ok := out[launch.ID]; ok { 2666 t.Fatalf("duplicate: %v", launch.ID) 2667 } 2668 2669 out[launch.ID] = launch 2670 } 2671 2672 for _, launch := range launches { 2673 l, ok := out[launch.ID] 2674 if !ok { 2675 t.Fatalf("bad %v", launch.ID) 2676 } 2677 2678 if !reflect.DeepEqual(launch, l) { 2679 t.Fatalf("bad: %#v %#v", launch, l) 2680 } 2681 2682 delete(out, launch.ID) 2683 } 2684 2685 if len(out) != 0 { 2686 t.Fatalf("leftover: %#v", out) 2687 } 2688 2689 if watchFired(ws) { 2690 t.Fatalf("bad") 2691 } 2692 } 2693 2694 func TestStateStore_RestorePeriodicLaunch(t *testing.T) { 2695 t.Parallel() 2696 2697 state := testStateStore(t) 2698 job := mock.Job() 2699 launch := &structs.PeriodicLaunch{ 2700 ID: job.ID, 2701 Namespace: job.Namespace, 2702 Launch: time.Now(), 2703 } 2704 2705 restore, err := state.Restore() 2706 if err != nil { 2707 t.Fatalf("err: %v", err) 2708 } 2709 2710 err = restore.PeriodicLaunchRestore(launch) 2711 if err != nil { 2712 t.Fatalf("err: %v", err) 2713 } 2714 restore.Commit() 2715 2716 ws := memdb.NewWatchSet() 2717 out, err := state.PeriodicLaunchByID(ws, job.Namespace, job.ID) 2718 if err != nil { 2719 t.Fatalf("err: %v", err) 2720 } 2721 2722 if !reflect.DeepEqual(out, launch) { 2723 t.Fatalf("Bad: %#v %#v", out, job) 2724 } 2725 2726 if watchFired(ws) { 2727 t.Fatalf("bad") 2728 } 2729 } 2730 2731 func TestStateStore_RestoreJobVersion(t *testing.T) { 2732 t.Parallel() 2733 2734 state := testStateStore(t) 2735 job := mock.Job() 2736 2737 restore, err := state.Restore() 2738 if err != nil { 2739 t.Fatalf("err: %v", err) 2740 } 2741 2742 err = restore.JobVersionRestore(job) 2743 if err != nil { 2744 t.Fatalf("err: %v", err) 2745 } 2746 restore.Commit() 2747 2748 ws := memdb.NewWatchSet() 2749 out, err := state.JobByIDAndVersion(ws, job.Namespace, job.ID, job.Version) 2750 if err != nil { 2751 t.Fatalf("err: %v", err) 2752 } 2753 2754 if !reflect.DeepEqual(out, job) { 2755 t.Fatalf("Bad: %#v %#v", out, job) 2756 } 2757 2758 if watchFired(ws) { 2759 t.Fatalf("bad") 2760 } 2761 } 2762 2763 func TestStateStore_RestoreDeployment(t *testing.T) { 2764 t.Parallel() 2765 2766 state := testStateStore(t) 2767 d := mock.Deployment() 2768 2769 restore, err := state.Restore() 2770 if err != nil { 2771 t.Fatalf("err: %v", err) 2772 } 2773 2774 err = restore.DeploymentRestore(d) 2775 if err != nil { 2776 t.Fatalf("err: %v", err) 2777 } 2778 restore.Commit() 2779 2780 ws := memdb.NewWatchSet() 2781 out, err := state.DeploymentByID(ws, d.ID) 2782 if err != nil { 2783 t.Fatalf("err: %v", err) 2784 } 2785 2786 if !reflect.DeepEqual(out, d) { 2787 t.Fatalf("Bad: %#v %#v", out, d) 2788 } 2789 2790 if watchFired(ws) { 2791 t.Fatalf("bad") 2792 } 2793 } 2794 2795 func TestStateStore_RestoreJobSummary(t *testing.T) { 2796 t.Parallel() 2797 2798 state := testStateStore(t) 2799 job := mock.Job() 2800 jobSummary := &structs.JobSummary{ 2801 JobID: job.ID, 2802 Namespace: job.Namespace, 2803 Summary: map[string]structs.TaskGroupSummary{ 2804 "web": { 2805 Starting: 10, 2806 }, 2807 }, 2808 } 2809 restore, err := state.Restore() 2810 if err != nil { 2811 t.Fatalf("err: %v", err) 2812 } 2813 2814 err = restore.JobSummaryRestore(jobSummary) 2815 if err != nil { 2816 t.Fatalf("err: %v", err) 2817 } 2818 restore.Commit() 2819 2820 ws := memdb.NewWatchSet() 2821 out, err := state.JobSummaryByID(ws, job.Namespace, job.ID) 2822 if err != nil { 2823 t.Fatalf("err: %v", err) 2824 } 2825 2826 if !reflect.DeepEqual(out, jobSummary) { 2827 t.Fatalf("Bad: %#v %#v", out, jobSummary) 2828 } 2829 } 2830 2831 // TestStateStore_CSIVolume checks register, list and deregister for csi_volumes 2832 func TestStateStore_CSIVolume(t *testing.T) { 2833 state := testStateStore(t) 2834 index := uint64(1000) 2835 2836 // Volume IDs 2837 vol0, vol1 := uuid.Generate(), uuid.Generate() 2838 2839 // Create a node running a healthy instance of the plugin 2840 node := mock.Node() 2841 pluginID := "minnie" 2842 alloc := mock.Alloc() 2843 alloc.DesiredStatus = "run" 2844 alloc.ClientStatus = "running" 2845 alloc.NodeID = node.ID 2846 alloc.Job.TaskGroups[0].Volumes = map[string]*structs.VolumeRequest{ 2847 "foo": { 2848 Name: "foo", 2849 Source: vol0, 2850 Type: "csi", 2851 }, 2852 } 2853 2854 node.CSINodePlugins = map[string]*structs.CSIInfo{ 2855 pluginID: { 2856 PluginID: pluginID, 2857 AllocID: alloc.ID, 2858 Healthy: true, 2859 HealthDescription: "healthy", 2860 RequiresControllerPlugin: false, 2861 RequiresTopologies: false, 2862 NodeInfo: &structs.CSINodeInfo{ 2863 ID: node.ID, 2864 MaxVolumes: 64, 2865 RequiresNodeStageVolume: true, 2866 }, 2867 }, 2868 } 2869 2870 index++ 2871 err := state.UpsertNode(index, node) 2872 require.NoError(t, err) 2873 defer state.DeleteNode(9999, []string{pluginID}) 2874 2875 index++ 2876 err = state.UpsertAllocs(index, []*structs.Allocation{alloc}) 2877 require.NoError(t, err) 2878 2879 ns := structs.DefaultNamespace 2880 2881 v0 := structs.NewCSIVolume("foo", index) 2882 v0.ID = vol0 2883 v0.Namespace = ns 2884 v0.PluginID = "minnie" 2885 v0.Schedulable = true 2886 v0.AccessMode = structs.CSIVolumeAccessModeMultiNodeSingleWriter 2887 v0.AttachmentMode = structs.CSIVolumeAttachmentModeFilesystem 2888 2889 index++ 2890 v1 := structs.NewCSIVolume("foo", index) 2891 v1.ID = vol1 2892 v1.Namespace = ns 2893 v1.PluginID = "adam" 2894 v1.Schedulable = true 2895 v1.AccessMode = structs.CSIVolumeAccessModeMultiNodeSingleWriter 2896 v1.AttachmentMode = structs.CSIVolumeAttachmentModeFilesystem 2897 2898 index++ 2899 err = state.CSIVolumeRegister(index, []*structs.CSIVolume{v0, v1}) 2900 require.NoError(t, err) 2901 2902 // volume registration is idempotent, unless identies are changed 2903 index++ 2904 err = state.CSIVolumeRegister(index, []*structs.CSIVolume{v0, v1}) 2905 require.NoError(t, err) 2906 2907 index++ 2908 v2 := v0.Copy() 2909 v2.PluginID = "new-id" 2910 err = state.CSIVolumeRegister(index, []*structs.CSIVolume{v2}) 2911 require.Error(t, err, fmt.Sprintf("volume exists: %s", v0.ID)) 2912 2913 ws := memdb.NewWatchSet() 2914 iter, err := state.CSIVolumesByNamespace(ws, ns) 2915 require.NoError(t, err) 2916 2917 slurp := func(iter memdb.ResultIterator) (vs []*structs.CSIVolume) { 2918 for { 2919 raw := iter.Next() 2920 if raw == nil { 2921 break 2922 } 2923 vol := raw.(*structs.CSIVolume) 2924 vs = append(vs, vol) 2925 } 2926 return vs 2927 } 2928 2929 vs := slurp(iter) 2930 require.Equal(t, 2, len(vs)) 2931 2932 ws = memdb.NewWatchSet() 2933 iter, err = state.CSIVolumesByPluginID(ws, ns, "minnie") 2934 require.NoError(t, err) 2935 vs = slurp(iter) 2936 require.Equal(t, 1, len(vs)) 2937 2938 ws = memdb.NewWatchSet() 2939 iter, err = state.CSIVolumesByNodeID(ws, node.ID) 2940 require.NoError(t, err) 2941 vs = slurp(iter) 2942 require.Equal(t, 1, len(vs)) 2943 2944 // Claims 2945 a0 := &structs.Allocation{ID: uuid.Generate()} 2946 a1 := &structs.Allocation{ID: uuid.Generate()} 2947 r := structs.CSIVolumeClaimRead 2948 w := structs.CSIVolumeClaimWrite 2949 u := structs.CSIVolumeClaimRelease 2950 2951 index++ 2952 err = state.CSIVolumeClaim(index, ns, vol0, a0, r) 2953 require.NoError(t, err) 2954 index++ 2955 err = state.CSIVolumeClaim(index, ns, vol0, a1, w) 2956 require.NoError(t, err) 2957 2958 ws = memdb.NewWatchSet() 2959 iter, err = state.CSIVolumesByPluginID(ws, ns, "minnie") 2960 require.NoError(t, err) 2961 vs = slurp(iter) 2962 require.False(t, vs[0].WriteFreeClaims()) 2963 2964 err = state.CSIVolumeClaim(2, ns, vol0, a0, u) 2965 require.NoError(t, err) 2966 ws = memdb.NewWatchSet() 2967 iter, err = state.CSIVolumesByPluginID(ws, ns, "minnie") 2968 require.NoError(t, err) 2969 vs = slurp(iter) 2970 require.True(t, vs[0].ReadSchedulable()) 2971 2972 // registration is an error when the volume is in use 2973 index++ 2974 err = state.CSIVolumeRegister(index, []*structs.CSIVolume{v0}) 2975 require.Error(t, err, fmt.Sprintf("volume exists: %s", vol0)) 2976 // as is deregistration 2977 index++ 2978 err = state.CSIVolumeDeregister(index, ns, []string{vol0}) 2979 require.Error(t, err, fmt.Sprintf("volume in use: %s", vol0)) 2980 2981 // release claims to unblock deregister 2982 index++ 2983 err = state.CSIVolumeClaim(index, ns, vol0, a0, u) 2984 require.NoError(t, err) 2985 index++ 2986 err = state.CSIVolumeClaim(index, ns, vol0, a1, u) 2987 require.NoError(t, err) 2988 2989 index++ 2990 err = state.CSIVolumeDeregister(index, ns, []string{vol0}) 2991 require.NoError(t, err) 2992 2993 // List, now omitting the deregistered volume 2994 ws = memdb.NewWatchSet() 2995 iter, err = state.CSIVolumesByPluginID(ws, ns, "minnie") 2996 require.NoError(t, err) 2997 vs = slurp(iter) 2998 require.Equal(t, 0, len(vs)) 2999 3000 ws = memdb.NewWatchSet() 3001 iter, err = state.CSIVolumesByNamespace(ws, ns) 3002 require.NoError(t, err) 3003 vs = slurp(iter) 3004 require.Equal(t, 1, len(vs)) 3005 } 3006 3007 // TestStateStore_CSIPluginNodes uses node fingerprinting to create a plugin and update health 3008 func TestStateStore_CSIPluginNodes(t *testing.T) { 3009 index := uint64(999) 3010 state := testStateStore(t) 3011 3012 // Create Nodes fingerprinting the plugins 3013 ns := []*structs.Node{mock.Node(), mock.Node()} 3014 3015 for _, n := range ns { 3016 index++ 3017 err := state.UpsertNode(index, n) 3018 require.NoError(t, err) 3019 } 3020 3021 // Fingerprint a running controller plugin 3022 n0 := ns[0].Copy() 3023 n0.CSIControllerPlugins = map[string]*structs.CSIInfo{ 3024 "foo": { 3025 PluginID: "foo", 3026 Healthy: true, 3027 UpdateTime: time.Now(), 3028 RequiresControllerPlugin: true, 3029 RequiresTopologies: false, 3030 ControllerInfo: &structs.CSIControllerInfo{ 3031 SupportsReadOnlyAttach: true, 3032 SupportsListVolumes: true, 3033 }, 3034 }, 3035 } 3036 3037 index++ 3038 err := state.UpsertNode(index, n0) 3039 require.NoError(t, err) 3040 3041 // Fingerprint two running node plugins 3042 for _, n := range ns[:] { 3043 n = n.Copy() 3044 n.CSINodePlugins = map[string]*structs.CSIInfo{ 3045 "foo": { 3046 PluginID: "foo", 3047 Healthy: true, 3048 UpdateTime: time.Now(), 3049 RequiresControllerPlugin: true, 3050 RequiresTopologies: false, 3051 NodeInfo: &structs.CSINodeInfo{}, 3052 }, 3053 } 3054 3055 index++ 3056 err = state.UpsertNode(index, n) 3057 require.NoError(t, err) 3058 } 3059 3060 ws := memdb.NewWatchSet() 3061 plug, err := state.CSIPluginByID(ws, "foo") 3062 require.NoError(t, err) 3063 3064 require.Equal(t, "foo", plug.ID) 3065 require.Equal(t, 1, plug.ControllersHealthy) 3066 require.Equal(t, 2, plug.NodesHealthy) 3067 3068 // Controller is unhealthy 3069 n0.CSIControllerPlugins = map[string]*structs.CSIInfo{ 3070 "foo": { 3071 PluginID: "foo", 3072 Healthy: false, 3073 UpdateTime: time.Now(), 3074 RequiresControllerPlugin: true, 3075 RequiresTopologies: false, 3076 ControllerInfo: &structs.CSIControllerInfo{ 3077 SupportsReadOnlyAttach: true, 3078 SupportsListVolumes: true, 3079 }, 3080 }, 3081 } 3082 3083 index++ 3084 err = state.UpsertNode(index, n0) 3085 require.NoError(t, err) 3086 3087 plug, err = state.CSIPluginByID(ws, "foo") 3088 require.NoError(t, err) 3089 require.Equal(t, "foo", plug.ID) 3090 require.Equal(t, 0, plug.ControllersHealthy) 3091 require.Equal(t, 2, plug.NodesHealthy) 3092 3093 // Volume using the plugin 3094 index++ 3095 vol := &structs.CSIVolume{ 3096 ID: uuid.Generate(), 3097 Namespace: structs.DefaultNamespace, 3098 PluginID: "foo", 3099 } 3100 err = state.CSIVolumeRegister(index, []*structs.CSIVolume{vol}) 3101 require.NoError(t, err) 3102 3103 vol, err = state.CSIVolumeByID(ws, structs.DefaultNamespace, vol.ID) 3104 require.NoError(t, err) 3105 require.True(t, vol.Schedulable) 3106 3107 // Node plugin is removed 3108 n1 := ns[1].Copy() 3109 n1.CSINodePlugins = map[string]*structs.CSIInfo{} 3110 index++ 3111 err = state.UpsertNode(index, n1) 3112 require.NoError(t, err) 3113 3114 plug, err = state.CSIPluginByID(ws, "foo") 3115 require.NoError(t, err) 3116 require.Equal(t, "foo", plug.ID) 3117 require.Equal(t, 0, plug.ControllersHealthy) 3118 require.Equal(t, 1, plug.NodesHealthy) 3119 3120 // Last plugin is removed 3121 n0 = ns[0].Copy() 3122 n0.CSINodePlugins = map[string]*structs.CSIInfo{} 3123 index++ 3124 err = state.UpsertNode(index, n0) 3125 require.NoError(t, err) 3126 3127 plug, err = state.CSIPluginByID(ws, "foo") 3128 require.NoError(t, err) 3129 require.Nil(t, plug) 3130 3131 // Volume exists and is safe to query, but unschedulable 3132 vol, err = state.CSIVolumeByID(ws, structs.DefaultNamespace, vol.ID) 3133 require.NoError(t, err) 3134 require.False(t, vol.Schedulable) 3135 } 3136 3137 func TestStateStore_CSIPluginJobs(t *testing.T) { 3138 s := testStateStore(t) 3139 deleteNodes := CreateTestCSIPlugin(s, "foo") 3140 defer deleteNodes() 3141 3142 index := uint64(1001) 3143 3144 controllerJob := mock.Job() 3145 controllerJob.TaskGroups[0].Tasks[0].CSIPluginConfig = &structs.TaskCSIPluginConfig{ 3146 ID: "foo", 3147 Type: structs.CSIPluginTypeController, 3148 } 3149 3150 nodeJob := mock.Job() 3151 nodeJob.TaskGroups[0].Tasks[0].CSIPluginConfig = &structs.TaskCSIPluginConfig{ 3152 ID: "foo", 3153 Type: structs.CSIPluginTypeNode, 3154 } 3155 3156 err := s.UpsertJob(index, controllerJob) 3157 require.NoError(t, err) 3158 index++ 3159 3160 err = s.UpsertJob(index, nodeJob) 3161 require.NoError(t, err) 3162 index++ 3163 3164 // Get the plugin, and make better fake allocations for it 3165 ws := memdb.NewWatchSet() 3166 plug, err := s.CSIPluginByID(ws, "foo") 3167 require.NoError(t, err) 3168 index++ 3169 3170 as := []*structs.Allocation{} 3171 for id, info := range plug.Controllers { 3172 as = append(as, &structs.Allocation{ 3173 ID: info.AllocID, 3174 Namespace: controllerJob.Namespace, 3175 JobID: controllerJob.ID, 3176 Job: controllerJob, 3177 TaskGroup: "web", 3178 EvalID: uuid.Generate(), 3179 NodeID: id, 3180 }) 3181 } 3182 for id, info := range plug.Nodes { 3183 as = append(as, &structs.Allocation{ 3184 ID: info.AllocID, 3185 JobID: nodeJob.ID, 3186 Namespace: nodeJob.Namespace, 3187 Job: nodeJob, 3188 TaskGroup: "web", 3189 EvalID: uuid.Generate(), 3190 NodeID: id, 3191 }) 3192 } 3193 3194 err = s.UpsertAllocs(index, as) 3195 require.NoError(t, err) 3196 index++ 3197 3198 // Delete a job 3199 err = s.DeleteJob(index, controllerJob.Namespace, controllerJob.ID) 3200 require.NoError(t, err) 3201 index++ 3202 3203 // plugin still exists 3204 plug, err = s.CSIPluginByID(ws, "foo") 3205 require.NoError(t, err) 3206 require.NotNil(t, plug) 3207 require.Equal(t, 0, len(plug.Controllers)) 3208 3209 // Delete a job 3210 err = s.DeleteJob(index, nodeJob.Namespace, nodeJob.ID) 3211 require.NoError(t, err) 3212 index++ 3213 3214 // plugin was collected 3215 plug, err = s.CSIPluginByID(ws, "foo") 3216 require.NoError(t, err) 3217 require.Nil(t, plug) 3218 } 3219 3220 func TestStateStore_RestoreCSIPlugin(t *testing.T) { 3221 t.Parallel() 3222 require := require.New(t) 3223 3224 state := testStateStore(t) 3225 plugin := mock.CSIPlugin() 3226 3227 restore, err := state.Restore() 3228 require.NoError(err) 3229 3230 err = restore.CSIPluginRestore(plugin) 3231 require.NoError(err) 3232 restore.Commit() 3233 3234 ws := memdb.NewWatchSet() 3235 out, err := state.CSIPluginByID(ws, plugin.ID) 3236 require.NoError(err) 3237 require.EqualValues(out, plugin) 3238 } 3239 3240 func TestStateStore_RestoreCSIVolume(t *testing.T) { 3241 t.Parallel() 3242 require := require.New(t) 3243 3244 state := testStateStore(t) 3245 plugin := mock.CSIPlugin() 3246 volume := mock.CSIVolume(plugin) 3247 3248 restore, err := state.Restore() 3249 require.NoError(err) 3250 3251 err = restore.CSIVolumeRestore(volume) 3252 require.NoError(err) 3253 restore.Commit() 3254 3255 ws := memdb.NewWatchSet() 3256 out, err := state.CSIVolumeByID(ws, "default", volume.ID) 3257 require.NoError(err) 3258 require.EqualValues(out, volume) 3259 } 3260 3261 func TestStateStore_Indexes(t *testing.T) { 3262 t.Parallel() 3263 3264 state := testStateStore(t) 3265 node := mock.Node() 3266 3267 err := state.UpsertNode(1000, node) 3268 if err != nil { 3269 t.Fatalf("err: %v", err) 3270 } 3271 3272 iter, err := state.Indexes() 3273 if err != nil { 3274 t.Fatalf("err: %v", err) 3275 } 3276 3277 var out []*IndexEntry 3278 for { 3279 raw := iter.Next() 3280 if raw == nil { 3281 break 3282 } 3283 out = append(out, raw.(*IndexEntry)) 3284 } 3285 3286 expect := &IndexEntry{"nodes", 1000} 3287 if l := len(out); l < 1 { 3288 t.Fatalf("unexpected number of index entries: %v", pretty.Sprint(out)) 3289 } 3290 3291 for _, index := range out { 3292 if index.Key != expect.Key { 3293 continue 3294 } 3295 if index.Value != expect.Value { 3296 t.Fatalf("bad index; got %d; want %d", index.Value, expect.Value) 3297 } 3298 3299 // We matched 3300 return 3301 } 3302 3303 t.Fatal("did not find expected index entry") 3304 } 3305 3306 func TestStateStore_LatestIndex(t *testing.T) { 3307 t.Parallel() 3308 3309 state := testStateStore(t) 3310 3311 if err := state.UpsertNode(1000, mock.Node()); err != nil { 3312 t.Fatalf("err: %v", err) 3313 } 3314 3315 exp := uint64(2000) 3316 if err := state.UpsertJob(exp, mock.Job()); err != nil { 3317 t.Fatalf("err: %v", err) 3318 } 3319 3320 latest, err := state.LatestIndex() 3321 if err != nil { 3322 t.Fatalf("err: %v", err) 3323 } 3324 3325 if latest != exp { 3326 t.Fatalf("LatestIndex() returned %d; want %d", latest, exp) 3327 } 3328 } 3329 3330 func TestStateStore_RestoreIndex(t *testing.T) { 3331 t.Parallel() 3332 3333 state := testStateStore(t) 3334 3335 restore, err := state.Restore() 3336 if err != nil { 3337 t.Fatalf("err: %v", err) 3338 } 3339 3340 index := &IndexEntry{"jobs", 1000} 3341 err = restore.IndexRestore(index) 3342 if err != nil { 3343 t.Fatalf("err: %v", err) 3344 } 3345 3346 restore.Commit() 3347 3348 out, err := state.Index("jobs") 3349 if err != nil { 3350 t.Fatalf("err: %v", err) 3351 } 3352 3353 if out != 1000 { 3354 t.Fatalf("Bad: %#v %#v", out, 1000) 3355 } 3356 } 3357 3358 func TestStateStore_UpsertEvals_Eval(t *testing.T) { 3359 t.Parallel() 3360 3361 state := testStateStore(t) 3362 eval := mock.Eval() 3363 3364 // Create a watchset so we can test that upsert fires the watch 3365 ws := memdb.NewWatchSet() 3366 if _, err := state.EvalByID(ws, eval.ID); err != nil { 3367 t.Fatalf("bad: %v", err) 3368 } 3369 3370 err := state.UpsertEvals(1000, []*structs.Evaluation{eval}) 3371 if err != nil { 3372 t.Fatalf("err: %v", err) 3373 } 3374 3375 if !watchFired(ws) { 3376 t.Fatalf("bad") 3377 } 3378 3379 ws = memdb.NewWatchSet() 3380 out, err := state.EvalByID(ws, eval.ID) 3381 if err != nil { 3382 t.Fatalf("err: %v", err) 3383 } 3384 3385 if !reflect.DeepEqual(eval, out) { 3386 t.Fatalf("bad: %#v %#v", eval, out) 3387 } 3388 3389 index, err := state.Index("evals") 3390 if err != nil { 3391 t.Fatalf("err: %v", err) 3392 } 3393 if index != 1000 { 3394 t.Fatalf("bad: %d", index) 3395 } 3396 3397 if watchFired(ws) { 3398 t.Fatalf("bad") 3399 } 3400 } 3401 3402 func TestStateStore_UpsertEvals_CancelBlocked(t *testing.T) { 3403 t.Parallel() 3404 3405 state := testStateStore(t) 3406 3407 // Create two blocked evals for the same job 3408 j := "test-job" 3409 b1, b2 := mock.Eval(), mock.Eval() 3410 b1.JobID = j 3411 b1.Status = structs.EvalStatusBlocked 3412 b2.JobID = j 3413 b2.Status = structs.EvalStatusBlocked 3414 3415 err := state.UpsertEvals(999, []*structs.Evaluation{b1, b2}) 3416 if err != nil { 3417 t.Fatalf("err: %v", err) 3418 } 3419 3420 // Create one complete and successful eval for the job 3421 eval := mock.Eval() 3422 eval.JobID = j 3423 eval.Status = structs.EvalStatusComplete 3424 3425 // Create a watchset so we can test that the upsert of the complete eval 3426 // fires the watch 3427 ws := memdb.NewWatchSet() 3428 if _, err := state.EvalByID(ws, b1.ID); err != nil { 3429 t.Fatalf("bad: %v", err) 3430 } 3431 if _, err := state.EvalByID(ws, b2.ID); err != nil { 3432 t.Fatalf("bad: %v", err) 3433 } 3434 3435 if err := state.UpsertEvals(1000, []*structs.Evaluation{eval}); err != nil { 3436 t.Fatalf("err: %v", err) 3437 } 3438 3439 if !watchFired(ws) { 3440 t.Fatalf("bad") 3441 } 3442 3443 ws = memdb.NewWatchSet() 3444 out, err := state.EvalByID(ws, eval.ID) 3445 if err != nil { 3446 t.Fatalf("err: %v", err) 3447 } 3448 3449 if !reflect.DeepEqual(eval, out) { 3450 t.Fatalf("bad: %#v %#v", eval, out) 3451 } 3452 3453 index, err := state.Index("evals") 3454 if err != nil { 3455 t.Fatalf("err: %v", err) 3456 } 3457 if index != 1000 { 3458 t.Fatalf("bad: %d", index) 3459 } 3460 3461 // Get b1/b2 and check they are cancelled 3462 out1, err := state.EvalByID(ws, b1.ID) 3463 if err != nil { 3464 t.Fatalf("err: %v", err) 3465 } 3466 3467 out2, err := state.EvalByID(ws, b2.ID) 3468 if err != nil { 3469 t.Fatalf("err: %v", err) 3470 } 3471 3472 if out1.Status != structs.EvalStatusCancelled || out2.Status != structs.EvalStatusCancelled { 3473 t.Fatalf("bad: %#v %#v", out1, out2) 3474 } 3475 3476 if watchFired(ws) { 3477 t.Fatalf("bad") 3478 } 3479 } 3480 3481 func TestStateStore_Update_UpsertEvals_Eval(t *testing.T) { 3482 t.Parallel() 3483 3484 state := testStateStore(t) 3485 eval := mock.Eval() 3486 3487 err := state.UpsertEvals(1000, []*structs.Evaluation{eval}) 3488 if err != nil { 3489 t.Fatalf("err: %v", err) 3490 } 3491 3492 // Create a watchset so we can test that delete fires the watch 3493 ws := memdb.NewWatchSet() 3494 ws2 := memdb.NewWatchSet() 3495 if _, err := state.EvalByID(ws, eval.ID); err != nil { 3496 t.Fatalf("bad: %v", err) 3497 } 3498 3499 if _, err := state.EvalsByJob(ws2, eval.Namespace, eval.JobID); err != nil { 3500 t.Fatalf("bad: %v", err) 3501 } 3502 3503 eval2 := mock.Eval() 3504 eval2.ID = eval.ID 3505 eval2.JobID = eval.JobID 3506 err = state.UpsertEvals(1001, []*structs.Evaluation{eval2}) 3507 if err != nil { 3508 t.Fatalf("err: %v", err) 3509 } 3510 3511 if !watchFired(ws) { 3512 t.Fatalf("bad") 3513 } 3514 if !watchFired(ws2) { 3515 t.Fatalf("bad") 3516 } 3517 3518 ws = memdb.NewWatchSet() 3519 out, err := state.EvalByID(ws, eval.ID) 3520 if err != nil { 3521 t.Fatalf("err: %v", err) 3522 } 3523 3524 if !reflect.DeepEqual(eval2, out) { 3525 t.Fatalf("bad: %#v %#v", eval2, out) 3526 } 3527 3528 if out.CreateIndex != 1000 { 3529 t.Fatalf("bad: %#v", out) 3530 } 3531 if out.ModifyIndex != 1001 { 3532 t.Fatalf("bad: %#v", out) 3533 } 3534 3535 index, err := state.Index("evals") 3536 if err != nil { 3537 t.Fatalf("err: %v", err) 3538 } 3539 if index != 1001 { 3540 t.Fatalf("bad: %d", index) 3541 } 3542 3543 if watchFired(ws) { 3544 t.Fatalf("bad") 3545 } 3546 } 3547 3548 func TestStateStore_UpsertEvals_Eval_ChildJob(t *testing.T) { 3549 t.Parallel() 3550 3551 state := testStateStore(t) 3552 3553 parent := mock.Job() 3554 if err := state.UpsertJob(998, parent); err != nil { 3555 t.Fatalf("err: %v", err) 3556 } 3557 3558 child := mock.Job() 3559 child.ParentID = parent.ID 3560 3561 if err := state.UpsertJob(999, child); err != nil { 3562 t.Fatalf("err: %v", err) 3563 } 3564 3565 eval := mock.Eval() 3566 eval.Status = structs.EvalStatusComplete 3567 eval.JobID = child.ID 3568 3569 // Create watchsets so we can test that upsert fires the watch 3570 ws := memdb.NewWatchSet() 3571 ws2 := memdb.NewWatchSet() 3572 ws3 := memdb.NewWatchSet() 3573 if _, err := state.JobSummaryByID(ws, parent.Namespace, parent.ID); err != nil { 3574 t.Fatalf("bad: %v", err) 3575 } 3576 if _, err := state.EvalByID(ws2, eval.ID); err != nil { 3577 t.Fatalf("bad: %v", err) 3578 } 3579 if _, err := state.EvalsByJob(ws3, eval.Namespace, eval.JobID); err != nil { 3580 t.Fatalf("bad: %v", err) 3581 } 3582 3583 err := state.UpsertEvals(1000, []*structs.Evaluation{eval}) 3584 if err != nil { 3585 t.Fatalf("err: %v", err) 3586 } 3587 3588 if !watchFired(ws) { 3589 t.Fatalf("bad") 3590 } 3591 if !watchFired(ws2) { 3592 t.Fatalf("bad") 3593 } 3594 if !watchFired(ws3) { 3595 t.Fatalf("bad") 3596 } 3597 3598 ws = memdb.NewWatchSet() 3599 out, err := state.EvalByID(ws, eval.ID) 3600 if err != nil { 3601 t.Fatalf("err: %v", err) 3602 } 3603 3604 if !reflect.DeepEqual(eval, out) { 3605 t.Fatalf("bad: %#v %#v", eval, out) 3606 } 3607 3608 index, err := state.Index("evals") 3609 if err != nil { 3610 t.Fatalf("err: %v", err) 3611 } 3612 if index != 1000 { 3613 t.Fatalf("bad: %d", index) 3614 } 3615 3616 summary, err := state.JobSummaryByID(ws, parent.Namespace, parent.ID) 3617 if err != nil { 3618 t.Fatalf("err: %v", err) 3619 } 3620 if summary == nil { 3621 t.Fatalf("nil summary") 3622 } 3623 if summary.JobID != parent.ID { 3624 t.Fatalf("bad summary id: %v", parent.ID) 3625 } 3626 if summary.Children == nil { 3627 t.Fatalf("nil children summary") 3628 } 3629 if summary.Children.Pending != 0 || summary.Children.Running != 0 || summary.Children.Dead != 1 { 3630 t.Fatalf("bad children summary: %v", summary.Children) 3631 } 3632 3633 if watchFired(ws) { 3634 t.Fatalf("bad") 3635 } 3636 } 3637 3638 func TestStateStore_DeleteEval_Eval(t *testing.T) { 3639 t.Parallel() 3640 3641 state := testStateStore(t) 3642 eval1 := mock.Eval() 3643 eval2 := mock.Eval() 3644 alloc1 := mock.Alloc() 3645 alloc2 := mock.Alloc() 3646 3647 // Create watchsets so we can test that upsert fires the watch 3648 watches := make([]memdb.WatchSet, 12) 3649 for i := 0; i < 12; i++ { 3650 watches[i] = memdb.NewWatchSet() 3651 } 3652 if _, err := state.EvalByID(watches[0], eval1.ID); err != nil { 3653 t.Fatalf("bad: %v", err) 3654 } 3655 if _, err := state.EvalByID(watches[1], eval2.ID); err != nil { 3656 t.Fatalf("bad: %v", err) 3657 } 3658 if _, err := state.EvalsByJob(watches[2], eval1.Namespace, eval1.JobID); err != nil { 3659 t.Fatalf("bad: %v", err) 3660 } 3661 if _, err := state.EvalsByJob(watches[3], eval2.Namespace, eval2.JobID); err != nil { 3662 t.Fatalf("bad: %v", err) 3663 } 3664 if _, err := state.AllocByID(watches[4], alloc1.ID); err != nil { 3665 t.Fatalf("bad: %v", err) 3666 } 3667 if _, err := state.AllocByID(watches[5], alloc2.ID); err != nil { 3668 t.Fatalf("bad: %v", err) 3669 } 3670 if _, err := state.AllocsByEval(watches[6], alloc1.EvalID); err != nil { 3671 t.Fatalf("bad: %v", err) 3672 } 3673 if _, err := state.AllocsByEval(watches[7], alloc2.EvalID); err != nil { 3674 t.Fatalf("bad: %v", err) 3675 } 3676 if _, err := state.AllocsByJob(watches[8], alloc1.Namespace, alloc1.JobID, false); err != nil { 3677 t.Fatalf("bad: %v", err) 3678 } 3679 if _, err := state.AllocsByJob(watches[9], alloc2.Namespace, alloc2.JobID, false); err != nil { 3680 t.Fatalf("bad: %v", err) 3681 } 3682 if _, err := state.AllocsByNode(watches[10], alloc1.NodeID); err != nil { 3683 t.Fatalf("bad: %v", err) 3684 } 3685 if _, err := state.AllocsByNode(watches[11], alloc2.NodeID); err != nil { 3686 t.Fatalf("bad: %v", err) 3687 } 3688 3689 state.UpsertJobSummary(900, mock.JobSummary(eval1.JobID)) 3690 state.UpsertJobSummary(901, mock.JobSummary(eval2.JobID)) 3691 state.UpsertJobSummary(902, mock.JobSummary(alloc1.JobID)) 3692 state.UpsertJobSummary(903, mock.JobSummary(alloc2.JobID)) 3693 err := state.UpsertEvals(1000, []*structs.Evaluation{eval1, eval2}) 3694 if err != nil { 3695 t.Fatalf("err: %v", err) 3696 } 3697 3698 err = state.UpsertAllocs(1001, []*structs.Allocation{alloc1, alloc2}) 3699 if err != nil { 3700 t.Fatalf("err: %v", err) 3701 } 3702 3703 err = state.DeleteEval(1002, []string{eval1.ID, eval2.ID}, []string{alloc1.ID, alloc2.ID}) 3704 if err != nil { 3705 t.Fatalf("err: %v", err) 3706 } 3707 3708 for i, ws := range watches { 3709 if !watchFired(ws) { 3710 t.Fatalf("bad %d", i) 3711 } 3712 } 3713 3714 ws := memdb.NewWatchSet() 3715 out, err := state.EvalByID(ws, eval1.ID) 3716 if err != nil { 3717 t.Fatalf("err: %v", err) 3718 } 3719 3720 if out != nil { 3721 t.Fatalf("bad: %#v %#v", eval1, out) 3722 } 3723 3724 out, err = state.EvalByID(ws, eval2.ID) 3725 if err != nil { 3726 t.Fatalf("err: %v", err) 3727 } 3728 3729 if out != nil { 3730 t.Fatalf("bad: %#v %#v", eval1, out) 3731 } 3732 3733 outA, err := state.AllocByID(ws, alloc1.ID) 3734 if err != nil { 3735 t.Fatalf("err: %v", err) 3736 } 3737 3738 if out != nil { 3739 t.Fatalf("bad: %#v %#v", alloc1, outA) 3740 } 3741 3742 outA, err = state.AllocByID(ws, alloc2.ID) 3743 if err != nil { 3744 t.Fatalf("err: %v", err) 3745 } 3746 3747 if out != nil { 3748 t.Fatalf("bad: %#v %#v", alloc1, outA) 3749 } 3750 3751 index, err := state.Index("evals") 3752 if err != nil { 3753 t.Fatalf("err: %v", err) 3754 } 3755 if index != 1002 { 3756 t.Fatalf("bad: %d", index) 3757 } 3758 3759 index, err = state.Index("allocs") 3760 if err != nil { 3761 t.Fatalf("err: %v", err) 3762 } 3763 if index != 1002 { 3764 t.Fatalf("bad: %d", index) 3765 } 3766 3767 if watchFired(ws) { 3768 t.Fatalf("bad") 3769 } 3770 } 3771 3772 func TestStateStore_DeleteEval_ChildJob(t *testing.T) { 3773 t.Parallel() 3774 3775 state := testStateStore(t) 3776 3777 parent := mock.Job() 3778 if err := state.UpsertJob(998, parent); err != nil { 3779 t.Fatalf("err: %v", err) 3780 } 3781 3782 child := mock.Job() 3783 child.ParentID = parent.ID 3784 3785 if err := state.UpsertJob(999, child); err != nil { 3786 t.Fatalf("err: %v", err) 3787 } 3788 3789 eval1 := mock.Eval() 3790 eval1.JobID = child.ID 3791 alloc1 := mock.Alloc() 3792 alloc1.JobID = child.ID 3793 3794 err := state.UpsertEvals(1000, []*structs.Evaluation{eval1}) 3795 if err != nil { 3796 t.Fatalf("err: %v", err) 3797 } 3798 3799 err = state.UpsertAllocs(1001, []*structs.Allocation{alloc1}) 3800 if err != nil { 3801 t.Fatalf("err: %v", err) 3802 } 3803 3804 // Create watchsets so we can test that delete fires the watch 3805 ws := memdb.NewWatchSet() 3806 if _, err := state.JobSummaryByID(ws, parent.Namespace, parent.ID); err != nil { 3807 t.Fatalf("bad: %v", err) 3808 } 3809 3810 err = state.DeleteEval(1002, []string{eval1.ID}, []string{alloc1.ID}) 3811 if err != nil { 3812 t.Fatalf("err: %v", err) 3813 } 3814 3815 if !watchFired(ws) { 3816 t.Fatalf("bad") 3817 } 3818 3819 ws = memdb.NewWatchSet() 3820 summary, err := state.JobSummaryByID(ws, parent.Namespace, parent.ID) 3821 if err != nil { 3822 t.Fatalf("err: %v", err) 3823 } 3824 if summary == nil { 3825 t.Fatalf("nil summary") 3826 } 3827 if summary.JobID != parent.ID { 3828 t.Fatalf("bad summary id: %v", parent.ID) 3829 } 3830 if summary.Children == nil { 3831 t.Fatalf("nil children summary") 3832 } 3833 if summary.Children.Pending != 0 || summary.Children.Running != 0 || summary.Children.Dead != 1 { 3834 t.Fatalf("bad children summary: %v", summary.Children) 3835 } 3836 3837 if watchFired(ws) { 3838 t.Fatalf("bad") 3839 } 3840 } 3841 3842 func TestStateStore_EvalsByJob(t *testing.T) { 3843 t.Parallel() 3844 3845 state := testStateStore(t) 3846 3847 eval1 := mock.Eval() 3848 eval2 := mock.Eval() 3849 eval2.JobID = eval1.JobID 3850 eval3 := mock.Eval() 3851 evals := []*structs.Evaluation{eval1, eval2} 3852 3853 err := state.UpsertEvals(1000, evals) 3854 if err != nil { 3855 t.Fatalf("err: %v", err) 3856 } 3857 err = state.UpsertEvals(1001, []*structs.Evaluation{eval3}) 3858 if err != nil { 3859 t.Fatalf("err: %v", err) 3860 } 3861 3862 ws := memdb.NewWatchSet() 3863 out, err := state.EvalsByJob(ws, eval1.Namespace, eval1.JobID) 3864 if err != nil { 3865 t.Fatalf("err: %v", err) 3866 } 3867 3868 sort.Sort(EvalIDSort(evals)) 3869 sort.Sort(EvalIDSort(out)) 3870 3871 if !reflect.DeepEqual(evals, out) { 3872 t.Fatalf("bad: %#v %#v", evals, out) 3873 } 3874 3875 if watchFired(ws) { 3876 t.Fatalf("bad") 3877 } 3878 } 3879 3880 func TestStateStore_Evals(t *testing.T) { 3881 t.Parallel() 3882 3883 state := testStateStore(t) 3884 var evals []*structs.Evaluation 3885 3886 for i := 0; i < 10; i++ { 3887 eval := mock.Eval() 3888 evals = append(evals, eval) 3889 3890 err := state.UpsertEvals(1000+uint64(i), []*structs.Evaluation{eval}) 3891 if err != nil { 3892 t.Fatalf("err: %v", err) 3893 } 3894 } 3895 3896 ws := memdb.NewWatchSet() 3897 iter, err := state.Evals(ws) 3898 if err != nil { 3899 t.Fatalf("err: %v", err) 3900 } 3901 3902 var out []*structs.Evaluation 3903 for { 3904 raw := iter.Next() 3905 if raw == nil { 3906 break 3907 } 3908 out = append(out, raw.(*structs.Evaluation)) 3909 } 3910 3911 sort.Sort(EvalIDSort(evals)) 3912 sort.Sort(EvalIDSort(out)) 3913 3914 if !reflect.DeepEqual(evals, out) { 3915 t.Fatalf("bad: %#v %#v", evals, out) 3916 } 3917 3918 if watchFired(ws) { 3919 t.Fatalf("bad") 3920 } 3921 } 3922 3923 func TestStateStore_EvalsByIDPrefix(t *testing.T) { 3924 t.Parallel() 3925 3926 state := testStateStore(t) 3927 var evals []*structs.Evaluation 3928 3929 ids := []string{ 3930 "aaaaaaaa-7bfb-395d-eb95-0685af2176b2", 3931 "aaaaaaab-7bfb-395d-eb95-0685af2176b2", 3932 "aaaaaabb-7bfb-395d-eb95-0685af2176b2", 3933 "aaaaabbb-7bfb-395d-eb95-0685af2176b2", 3934 "aaaabbbb-7bfb-395d-eb95-0685af2176b2", 3935 "aaabbbbb-7bfb-395d-eb95-0685af2176b2", 3936 "aabbbbbb-7bfb-395d-eb95-0685af2176b2", 3937 "abbbbbbb-7bfb-395d-eb95-0685af2176b2", 3938 "bbbbbbbb-7bfb-395d-eb95-0685af2176b2", 3939 } 3940 for i := 0; i < 9; i++ { 3941 eval := mock.Eval() 3942 eval.ID = ids[i] 3943 evals = append(evals, eval) 3944 } 3945 3946 err := state.UpsertEvals(1000, evals) 3947 if err != nil { 3948 t.Fatalf("err: %v", err) 3949 } 3950 3951 ws := memdb.NewWatchSet() 3952 iter, err := state.EvalsByIDPrefix(ws, structs.DefaultNamespace, "aaaa") 3953 if err != nil { 3954 t.Fatalf("err: %v", err) 3955 } 3956 3957 gatherEvals := func(iter memdb.ResultIterator) []*structs.Evaluation { 3958 var evals []*structs.Evaluation 3959 for { 3960 raw := iter.Next() 3961 if raw == nil { 3962 break 3963 } 3964 evals = append(evals, raw.(*structs.Evaluation)) 3965 } 3966 return evals 3967 } 3968 3969 out := gatherEvals(iter) 3970 if len(out) != 5 { 3971 t.Fatalf("bad: expected five evaluations, got: %#v", out) 3972 } 3973 3974 sort.Sort(EvalIDSort(evals)) 3975 3976 for index, eval := range out { 3977 if ids[index] != eval.ID { 3978 t.Fatalf("bad: got unexpected id: %s", eval.ID) 3979 } 3980 } 3981 3982 iter, err = state.EvalsByIDPrefix(ws, structs.DefaultNamespace, "b-a7bfb") 3983 if err != nil { 3984 t.Fatalf("err: %v", err) 3985 } 3986 3987 out = gatherEvals(iter) 3988 if len(out) != 0 { 3989 t.Fatalf("bad: unexpected zero evaluations, got: %#v", out) 3990 } 3991 3992 if watchFired(ws) { 3993 t.Fatalf("bad") 3994 } 3995 } 3996 3997 func TestStateStore_RestoreEval(t *testing.T) { 3998 t.Parallel() 3999 4000 state := testStateStore(t) 4001 eval := mock.Eval() 4002 4003 restore, err := state.Restore() 4004 if err != nil { 4005 t.Fatalf("err: %v", err) 4006 } 4007 4008 err = restore.EvalRestore(eval) 4009 if err != nil { 4010 t.Fatalf("err: %v", err) 4011 } 4012 restore.Commit() 4013 4014 ws := memdb.NewWatchSet() 4015 out, err := state.EvalByID(ws, eval.ID) 4016 if err != nil { 4017 t.Fatalf("err: %v", err) 4018 } 4019 4020 if !reflect.DeepEqual(out, eval) { 4021 t.Fatalf("Bad: %#v %#v", out, eval) 4022 } 4023 } 4024 4025 func TestStateStore_UpdateAllocsFromClient(t *testing.T) { 4026 t.Parallel() 4027 4028 state := testStateStore(t) 4029 parent := mock.Job() 4030 if err := state.UpsertJob(998, parent); err != nil { 4031 t.Fatalf("err: %v", err) 4032 } 4033 4034 child := mock.Job() 4035 child.ParentID = parent.ID 4036 if err := state.UpsertJob(999, child); err != nil { 4037 t.Fatalf("err: %v", err) 4038 } 4039 4040 alloc := mock.Alloc() 4041 alloc.JobID = child.ID 4042 alloc.Job = child 4043 4044 err := state.UpsertAllocs(1000, []*structs.Allocation{alloc}) 4045 if err != nil { 4046 t.Fatalf("err: %v", err) 4047 } 4048 4049 ws := memdb.NewWatchSet() 4050 summary, err := state.JobSummaryByID(ws, parent.Namespace, parent.ID) 4051 if err != nil { 4052 t.Fatalf("err: %v", err) 4053 } 4054 if summary == nil { 4055 t.Fatalf("nil summary") 4056 } 4057 if summary.JobID != parent.ID { 4058 t.Fatalf("bad summary id: %v", parent.ID) 4059 } 4060 if summary.Children == nil { 4061 t.Fatalf("nil children summary") 4062 } 4063 if summary.Children.Pending != 0 || summary.Children.Running != 1 || summary.Children.Dead != 0 { 4064 t.Fatalf("bad children summary: %v", summary.Children) 4065 } 4066 4067 // Create watchsets so we can test that update fires the watch 4068 ws = memdb.NewWatchSet() 4069 if _, err := state.JobSummaryByID(ws, parent.Namespace, parent.ID); err != nil { 4070 t.Fatalf("bad: %v", err) 4071 } 4072 4073 // Create the delta updates 4074 ts := map[string]*structs.TaskState{"web": {State: structs.TaskStateRunning}} 4075 update := &structs.Allocation{ 4076 ID: alloc.ID, 4077 ClientStatus: structs.AllocClientStatusComplete, 4078 TaskStates: ts, 4079 JobID: alloc.JobID, 4080 TaskGroup: alloc.TaskGroup, 4081 } 4082 err = state.UpdateAllocsFromClient(1001, []*structs.Allocation{update}) 4083 if err != nil { 4084 t.Fatalf("err: %v", err) 4085 } 4086 4087 if !watchFired(ws) { 4088 t.Fatalf("bad") 4089 } 4090 4091 ws = memdb.NewWatchSet() 4092 summary, err = state.JobSummaryByID(ws, parent.Namespace, parent.ID) 4093 if err != nil { 4094 t.Fatalf("err: %v", err) 4095 } 4096 if summary == nil { 4097 t.Fatalf("nil summary") 4098 } 4099 if summary.JobID != parent.ID { 4100 t.Fatalf("bad summary id: %v", parent.ID) 4101 } 4102 if summary.Children == nil { 4103 t.Fatalf("nil children summary") 4104 } 4105 if summary.Children.Pending != 0 || summary.Children.Running != 0 || summary.Children.Dead != 1 { 4106 t.Fatalf("bad children summary: %v", summary.Children) 4107 } 4108 4109 if watchFired(ws) { 4110 t.Fatalf("bad") 4111 } 4112 } 4113 4114 func TestStateStore_UpdateAllocsFromClient_ChildJob(t *testing.T) { 4115 t.Parallel() 4116 4117 state := testStateStore(t) 4118 alloc1 := mock.Alloc() 4119 alloc2 := mock.Alloc() 4120 4121 if err := state.UpsertJob(999, alloc1.Job); err != nil { 4122 t.Fatalf("err: %v", err) 4123 } 4124 if err := state.UpsertJob(999, alloc2.Job); err != nil { 4125 t.Fatalf("err: %v", err) 4126 } 4127 4128 err := state.UpsertAllocs(1000, []*structs.Allocation{alloc1, alloc2}) 4129 if err != nil { 4130 t.Fatalf("err: %v", err) 4131 } 4132 4133 // Create watchsets so we can test that update fires the watch 4134 watches := make([]memdb.WatchSet, 8) 4135 for i := 0; i < 8; i++ { 4136 watches[i] = memdb.NewWatchSet() 4137 } 4138 if _, err := state.AllocByID(watches[0], alloc1.ID); err != nil { 4139 t.Fatalf("bad: %v", err) 4140 } 4141 if _, err := state.AllocByID(watches[1], alloc2.ID); err != nil { 4142 t.Fatalf("bad: %v", err) 4143 } 4144 if _, err := state.AllocsByEval(watches[2], alloc1.EvalID); err != nil { 4145 t.Fatalf("bad: %v", err) 4146 } 4147 if _, err := state.AllocsByEval(watches[3], alloc2.EvalID); err != nil { 4148 t.Fatalf("bad: %v", err) 4149 } 4150 if _, err := state.AllocsByJob(watches[4], alloc1.Namespace, alloc1.JobID, false); err != nil { 4151 t.Fatalf("bad: %v", err) 4152 } 4153 if _, err := state.AllocsByJob(watches[5], alloc2.Namespace, alloc2.JobID, false); err != nil { 4154 t.Fatalf("bad: %v", err) 4155 } 4156 if _, err := state.AllocsByNode(watches[6], alloc1.NodeID); err != nil { 4157 t.Fatalf("bad: %v", err) 4158 } 4159 if _, err := state.AllocsByNode(watches[7], alloc2.NodeID); err != nil { 4160 t.Fatalf("bad: %v", err) 4161 } 4162 4163 // Create the delta updates 4164 ts := map[string]*structs.TaskState{"web": {State: structs.TaskStatePending}} 4165 update := &structs.Allocation{ 4166 ID: alloc1.ID, 4167 ClientStatus: structs.AllocClientStatusFailed, 4168 TaskStates: ts, 4169 JobID: alloc1.JobID, 4170 TaskGroup: alloc1.TaskGroup, 4171 } 4172 update2 := &structs.Allocation{ 4173 ID: alloc2.ID, 4174 ClientStatus: structs.AllocClientStatusRunning, 4175 TaskStates: ts, 4176 JobID: alloc2.JobID, 4177 TaskGroup: alloc2.TaskGroup, 4178 } 4179 4180 err = state.UpdateAllocsFromClient(1001, []*structs.Allocation{update, update2}) 4181 if err != nil { 4182 t.Fatalf("err: %v", err) 4183 } 4184 4185 for i, ws := range watches { 4186 if !watchFired(ws) { 4187 t.Fatalf("bad %d", i) 4188 } 4189 } 4190 4191 ws := memdb.NewWatchSet() 4192 out, err := state.AllocByID(ws, alloc1.ID) 4193 if err != nil { 4194 t.Fatalf("err: %v", err) 4195 } 4196 4197 alloc1.CreateIndex = 1000 4198 alloc1.ModifyIndex = 1001 4199 alloc1.TaskStates = ts 4200 alloc1.ClientStatus = structs.AllocClientStatusFailed 4201 if !reflect.DeepEqual(alloc1, out) { 4202 t.Fatalf("bad: %#v %#v", alloc1, out) 4203 } 4204 4205 out, err = state.AllocByID(ws, alloc2.ID) 4206 if err != nil { 4207 t.Fatalf("err: %v", err) 4208 } 4209 4210 alloc2.ModifyIndex = 1000 4211 alloc2.ModifyIndex = 1001 4212 alloc2.ClientStatus = structs.AllocClientStatusRunning 4213 alloc2.TaskStates = ts 4214 if !reflect.DeepEqual(alloc2, out) { 4215 t.Fatalf("bad: %#v %#v", alloc2, out) 4216 } 4217 4218 index, err := state.Index("allocs") 4219 if err != nil { 4220 t.Fatalf("err: %v", err) 4221 } 4222 if index != 1001 { 4223 t.Fatalf("bad: %d", index) 4224 } 4225 4226 // Ensure summaries have been updated 4227 summary, err := state.JobSummaryByID(ws, alloc1.Namespace, alloc1.JobID) 4228 if err != nil { 4229 t.Fatalf("err: %v", err) 4230 } 4231 tgSummary := summary.Summary["web"] 4232 if tgSummary.Failed != 1 { 4233 t.Fatalf("expected failed: %v, actual: %v, summary: %#v", 1, tgSummary.Failed, tgSummary) 4234 } 4235 4236 summary2, err := state.JobSummaryByID(ws, alloc2.Namespace, alloc2.JobID) 4237 if err != nil { 4238 t.Fatalf("err: %v", err) 4239 } 4240 tgSummary2 := summary2.Summary["web"] 4241 if tgSummary2.Running != 1 { 4242 t.Fatalf("expected running: %v, actual: %v", 1, tgSummary2.Running) 4243 } 4244 4245 if watchFired(ws) { 4246 t.Fatalf("bad") 4247 } 4248 } 4249 4250 func TestStateStore_UpdateMultipleAllocsFromClient(t *testing.T) { 4251 t.Parallel() 4252 4253 state := testStateStore(t) 4254 alloc := mock.Alloc() 4255 4256 if err := state.UpsertJob(999, alloc.Job); err != nil { 4257 t.Fatalf("err: %v", err) 4258 } 4259 err := state.UpsertAllocs(1000, []*structs.Allocation{alloc}) 4260 if err != nil { 4261 t.Fatalf("err: %v", err) 4262 } 4263 4264 // Create the delta updates 4265 ts := map[string]*structs.TaskState{"web": {State: structs.TaskStatePending}} 4266 update := &structs.Allocation{ 4267 ID: alloc.ID, 4268 ClientStatus: structs.AllocClientStatusRunning, 4269 TaskStates: ts, 4270 JobID: alloc.JobID, 4271 TaskGroup: alloc.TaskGroup, 4272 } 4273 update2 := &structs.Allocation{ 4274 ID: alloc.ID, 4275 ClientStatus: structs.AllocClientStatusPending, 4276 TaskStates: ts, 4277 JobID: alloc.JobID, 4278 TaskGroup: alloc.TaskGroup, 4279 } 4280 4281 err = state.UpdateAllocsFromClient(1001, []*structs.Allocation{update, update2}) 4282 if err != nil { 4283 t.Fatalf("err: %v", err) 4284 } 4285 4286 ws := memdb.NewWatchSet() 4287 out, err := state.AllocByID(ws, alloc.ID) 4288 if err != nil { 4289 t.Fatalf("err: %v", err) 4290 } 4291 4292 alloc.CreateIndex = 1000 4293 alloc.ModifyIndex = 1001 4294 alloc.TaskStates = ts 4295 alloc.ClientStatus = structs.AllocClientStatusPending 4296 if !reflect.DeepEqual(alloc, out) { 4297 t.Fatalf("bad: %#v , actual:%#v", alloc, out) 4298 } 4299 4300 summary, err := state.JobSummaryByID(ws, alloc.Namespace, alloc.JobID) 4301 expectedSummary := &structs.JobSummary{ 4302 JobID: alloc.JobID, 4303 Namespace: alloc.Namespace, 4304 Summary: map[string]structs.TaskGroupSummary{ 4305 "web": { 4306 Starting: 1, 4307 }, 4308 }, 4309 Children: new(structs.JobChildrenSummary), 4310 CreateIndex: 999, 4311 ModifyIndex: 1001, 4312 } 4313 if err != nil { 4314 t.Fatalf("err: %v", err) 4315 } 4316 if !reflect.DeepEqual(summary, expectedSummary) { 4317 t.Fatalf("expected: %#v, actual: %#v", expectedSummary, summary) 4318 } 4319 } 4320 4321 func TestStateStore_UpdateAllocsFromClient_Deployment(t *testing.T) { 4322 t.Parallel() 4323 require := require.New(t) 4324 4325 state := testStateStore(t) 4326 4327 alloc := mock.Alloc() 4328 now := time.Now() 4329 alloc.CreateTime = now.UnixNano() 4330 pdeadline := 5 * time.Minute 4331 deployment := mock.Deployment() 4332 deployment.TaskGroups[alloc.TaskGroup].ProgressDeadline = pdeadline 4333 alloc.DeploymentID = deployment.ID 4334 4335 require.Nil(state.UpsertJob(999, alloc.Job)) 4336 require.Nil(state.UpsertDeployment(1000, deployment)) 4337 require.Nil(state.UpsertAllocs(1001, []*structs.Allocation{alloc})) 4338 4339 healthy := now.Add(time.Second) 4340 update := &structs.Allocation{ 4341 ID: alloc.ID, 4342 ClientStatus: structs.AllocClientStatusRunning, 4343 JobID: alloc.JobID, 4344 TaskGroup: alloc.TaskGroup, 4345 DeploymentStatus: &structs.AllocDeploymentStatus{ 4346 Healthy: helper.BoolToPtr(true), 4347 Timestamp: healthy, 4348 }, 4349 } 4350 require.Nil(state.UpdateAllocsFromClient(1001, []*structs.Allocation{update})) 4351 4352 // Check that the deployment state was updated because the healthy 4353 // deployment 4354 dout, err := state.DeploymentByID(nil, deployment.ID) 4355 require.Nil(err) 4356 require.NotNil(dout) 4357 require.Len(dout.TaskGroups, 1) 4358 dstate := dout.TaskGroups[alloc.TaskGroup] 4359 require.NotNil(dstate) 4360 require.Equal(1, dstate.PlacedAllocs) 4361 require.True(healthy.Add(pdeadline).Equal(dstate.RequireProgressBy)) 4362 } 4363 4364 // This tests that the deployment state is merged correctly 4365 func TestStateStore_UpdateAllocsFromClient_DeploymentStateMerges(t *testing.T) { 4366 t.Parallel() 4367 require := require.New(t) 4368 4369 state := testStateStore(t) 4370 alloc := mock.Alloc() 4371 now := time.Now() 4372 alloc.CreateTime = now.UnixNano() 4373 pdeadline := 5 * time.Minute 4374 deployment := mock.Deployment() 4375 deployment.TaskGroups[alloc.TaskGroup].ProgressDeadline = pdeadline 4376 alloc.DeploymentID = deployment.ID 4377 alloc.DeploymentStatus = &structs.AllocDeploymentStatus{ 4378 Canary: true, 4379 } 4380 4381 require.Nil(state.UpsertJob(999, alloc.Job)) 4382 require.Nil(state.UpsertDeployment(1000, deployment)) 4383 require.Nil(state.UpsertAllocs(1001, []*structs.Allocation{alloc})) 4384 4385 update := &structs.Allocation{ 4386 ID: alloc.ID, 4387 ClientStatus: structs.AllocClientStatusRunning, 4388 JobID: alloc.JobID, 4389 TaskGroup: alloc.TaskGroup, 4390 DeploymentStatus: &structs.AllocDeploymentStatus{ 4391 Healthy: helper.BoolToPtr(true), 4392 Canary: false, 4393 }, 4394 } 4395 require.Nil(state.UpdateAllocsFromClient(1001, []*structs.Allocation{update})) 4396 4397 // Check that the merging of the deployment status was correct 4398 out, err := state.AllocByID(nil, alloc.ID) 4399 require.Nil(err) 4400 require.NotNil(out) 4401 require.True(out.DeploymentStatus.Canary) 4402 require.NotNil(out.DeploymentStatus.Healthy) 4403 require.True(*out.DeploymentStatus.Healthy) 4404 } 4405 4406 func TestStateStore_UpsertAlloc_Alloc(t *testing.T) { 4407 t.Parallel() 4408 4409 state := testStateStore(t) 4410 alloc := mock.Alloc() 4411 4412 if err := state.UpsertJob(999, alloc.Job); err != nil { 4413 t.Fatalf("err: %v", err) 4414 } 4415 4416 // Create watchsets so we can test that update fires the watch 4417 watches := make([]memdb.WatchSet, 4) 4418 for i := 0; i < 4; i++ { 4419 watches[i] = memdb.NewWatchSet() 4420 } 4421 if _, err := state.AllocByID(watches[0], alloc.ID); err != nil { 4422 t.Fatalf("bad: %v", err) 4423 } 4424 if _, err := state.AllocsByEval(watches[1], alloc.EvalID); err != nil { 4425 t.Fatalf("bad: %v", err) 4426 } 4427 if _, err := state.AllocsByJob(watches[2], alloc.Namespace, alloc.JobID, false); err != nil { 4428 t.Fatalf("bad: %v", err) 4429 } 4430 if _, err := state.AllocsByNode(watches[3], alloc.NodeID); err != nil { 4431 t.Fatalf("bad: %v", err) 4432 } 4433 4434 err := state.UpsertAllocs(1000, []*structs.Allocation{alloc}) 4435 if err != nil { 4436 t.Fatalf("err: %v", err) 4437 } 4438 4439 for i, ws := range watches { 4440 if !watchFired(ws) { 4441 t.Fatalf("bad %d", i) 4442 } 4443 } 4444 4445 ws := memdb.NewWatchSet() 4446 out, err := state.AllocByID(ws, alloc.ID) 4447 if err != nil { 4448 t.Fatalf("err: %v", err) 4449 } 4450 4451 if !reflect.DeepEqual(alloc, out) { 4452 t.Fatalf("bad: %#v %#v", alloc, out) 4453 } 4454 4455 index, err := state.Index("allocs") 4456 if err != nil { 4457 t.Fatalf("err: %v", err) 4458 } 4459 if index != 1000 { 4460 t.Fatalf("bad: %d", index) 4461 } 4462 4463 summary, err := state.JobSummaryByID(ws, alloc.Namespace, alloc.JobID) 4464 if err != nil { 4465 t.Fatalf("err: %v", err) 4466 } 4467 4468 tgSummary, ok := summary.Summary["web"] 4469 if !ok { 4470 t.Fatalf("no summary for task group web") 4471 } 4472 if tgSummary.Starting != 1 { 4473 t.Fatalf("expected queued: %v, actual: %v", 1, tgSummary.Starting) 4474 } 4475 4476 if watchFired(ws) { 4477 t.Fatalf("bad") 4478 } 4479 } 4480 4481 func TestStateStore_UpsertAlloc_Deployment(t *testing.T) { 4482 t.Parallel() 4483 require := require.New(t) 4484 4485 state := testStateStore(t) 4486 alloc := mock.Alloc() 4487 now := time.Now() 4488 alloc.CreateTime = now.UnixNano() 4489 alloc.ModifyTime = now.UnixNano() 4490 pdeadline := 5 * time.Minute 4491 deployment := mock.Deployment() 4492 deployment.TaskGroups[alloc.TaskGroup].ProgressDeadline = pdeadline 4493 alloc.DeploymentID = deployment.ID 4494 4495 require.Nil(state.UpsertJob(999, alloc.Job)) 4496 require.Nil(state.UpsertDeployment(1000, deployment)) 4497 4498 // Create a watch set so we can test that update fires the watch 4499 ws := memdb.NewWatchSet() 4500 require.Nil(state.AllocsByDeployment(ws, alloc.DeploymentID)) 4501 4502 err := state.UpsertAllocs(1001, []*structs.Allocation{alloc}) 4503 require.Nil(err) 4504 4505 if !watchFired(ws) { 4506 t.Fatalf("watch not fired") 4507 } 4508 4509 ws = memdb.NewWatchSet() 4510 allocs, err := state.AllocsByDeployment(ws, alloc.DeploymentID) 4511 require.Nil(err) 4512 require.Len(allocs, 1) 4513 require.EqualValues(alloc, allocs[0]) 4514 4515 index, err := state.Index("allocs") 4516 require.Nil(err) 4517 require.EqualValues(1001, index) 4518 if watchFired(ws) { 4519 t.Fatalf("bad") 4520 } 4521 4522 // Check that the deployment state was updated 4523 dout, err := state.DeploymentByID(nil, deployment.ID) 4524 require.Nil(err) 4525 require.NotNil(dout) 4526 require.Len(dout.TaskGroups, 1) 4527 dstate := dout.TaskGroups[alloc.TaskGroup] 4528 require.NotNil(dstate) 4529 require.Equal(1, dstate.PlacedAllocs) 4530 require.True(now.Add(pdeadline).Equal(dstate.RequireProgressBy)) 4531 } 4532 4533 // Testing to ensure we keep issue 4534 // https://github.com/hashicorp/nomad/issues/2583 fixed 4535 func TestStateStore_UpsertAlloc_No_Job(t *testing.T) { 4536 t.Parallel() 4537 4538 state := testStateStore(t) 4539 alloc := mock.Alloc() 4540 alloc.Job = nil 4541 4542 err := state.UpsertAllocs(999, []*structs.Allocation{alloc}) 4543 if err == nil || !strings.Contains(err.Error(), "without a job") { 4544 t.Fatalf("expect err: %v", err) 4545 } 4546 } 4547 4548 func TestStateStore_UpsertAlloc_ChildJob(t *testing.T) { 4549 t.Parallel() 4550 4551 state := testStateStore(t) 4552 4553 parent := mock.Job() 4554 if err := state.UpsertJob(998, parent); err != nil { 4555 t.Fatalf("err: %v", err) 4556 } 4557 4558 child := mock.Job() 4559 child.ParentID = parent.ID 4560 4561 if err := state.UpsertJob(999, child); err != nil { 4562 t.Fatalf("err: %v", err) 4563 } 4564 4565 alloc := mock.Alloc() 4566 alloc.JobID = child.ID 4567 alloc.Job = child 4568 4569 // Create watchsets so we can test that delete fires the watch 4570 ws := memdb.NewWatchSet() 4571 if _, err := state.JobSummaryByID(ws, parent.Namespace, parent.ID); err != nil { 4572 t.Fatalf("bad: %v", err) 4573 } 4574 4575 err := state.UpsertAllocs(1000, []*structs.Allocation{alloc}) 4576 if err != nil { 4577 t.Fatalf("err: %v", err) 4578 } 4579 4580 if !watchFired(ws) { 4581 t.Fatalf("bad") 4582 } 4583 4584 ws = memdb.NewWatchSet() 4585 summary, err := state.JobSummaryByID(ws, parent.Namespace, parent.ID) 4586 if err != nil { 4587 t.Fatalf("err: %v", err) 4588 } 4589 if summary == nil { 4590 t.Fatalf("nil summary") 4591 } 4592 if summary.JobID != parent.ID { 4593 t.Fatalf("bad summary id: %v", parent.ID) 4594 } 4595 if summary.Children == nil { 4596 t.Fatalf("nil children summary") 4597 } 4598 if summary.Children.Pending != 0 || summary.Children.Running != 1 || summary.Children.Dead != 0 { 4599 t.Fatalf("bad children summary: %v", summary.Children) 4600 } 4601 4602 if watchFired(ws) { 4603 t.Fatalf("bad") 4604 } 4605 } 4606 4607 func TestStateStore_UpdateAlloc_Alloc(t *testing.T) { 4608 t.Parallel() 4609 4610 state := testStateStore(t) 4611 alloc := mock.Alloc() 4612 4613 if err := state.UpsertJob(999, alloc.Job); err != nil { 4614 t.Fatalf("err: %v", err) 4615 } 4616 4617 err := state.UpsertAllocs(1000, []*structs.Allocation{alloc}) 4618 if err != nil { 4619 t.Fatalf("err: %v", err) 4620 } 4621 4622 ws := memdb.NewWatchSet() 4623 summary, err := state.JobSummaryByID(ws, alloc.Namespace, alloc.JobID) 4624 if err != nil { 4625 t.Fatalf("err: %v", err) 4626 } 4627 tgSummary := summary.Summary["web"] 4628 if tgSummary.Starting != 1 { 4629 t.Fatalf("expected starting: %v, actual: %v", 1, tgSummary.Starting) 4630 } 4631 4632 alloc2 := mock.Alloc() 4633 alloc2.ID = alloc.ID 4634 alloc2.NodeID = alloc.NodeID + ".new" 4635 state.UpsertJobSummary(1001, mock.JobSummary(alloc2.JobID)) 4636 4637 // Create watchsets so we can test that update fires the watch 4638 watches := make([]memdb.WatchSet, 4) 4639 for i := 0; i < 4; i++ { 4640 watches[i] = memdb.NewWatchSet() 4641 } 4642 if _, err := state.AllocByID(watches[0], alloc2.ID); err != nil { 4643 t.Fatalf("bad: %v", err) 4644 } 4645 if _, err := state.AllocsByEval(watches[1], alloc2.EvalID); err != nil { 4646 t.Fatalf("bad: %v", err) 4647 } 4648 if _, err := state.AllocsByJob(watches[2], alloc2.Namespace, alloc2.JobID, false); err != nil { 4649 t.Fatalf("bad: %v", err) 4650 } 4651 if _, err := state.AllocsByNode(watches[3], alloc2.NodeID); err != nil { 4652 t.Fatalf("bad: %v", err) 4653 } 4654 4655 err = state.UpsertAllocs(1002, []*structs.Allocation{alloc2}) 4656 if err != nil { 4657 t.Fatalf("err: %v", err) 4658 } 4659 4660 for i, ws := range watches { 4661 if !watchFired(ws) { 4662 t.Fatalf("bad %d", i) 4663 } 4664 } 4665 4666 ws = memdb.NewWatchSet() 4667 out, err := state.AllocByID(ws, alloc.ID) 4668 if err != nil { 4669 t.Fatalf("err: %v", err) 4670 } 4671 4672 if !reflect.DeepEqual(alloc2, out) { 4673 t.Fatalf("bad: %#v %#v", alloc2, out) 4674 } 4675 4676 if out.CreateIndex != 1000 { 4677 t.Fatalf("bad: %#v", out) 4678 } 4679 if out.ModifyIndex != 1002 { 4680 t.Fatalf("bad: %#v", out) 4681 } 4682 4683 index, err := state.Index("allocs") 4684 if err != nil { 4685 t.Fatalf("err: %v", err) 4686 } 4687 if index != 1002 { 4688 t.Fatalf("bad: %d", index) 4689 } 4690 4691 // Ensure that summary hasb't changed 4692 summary, err = state.JobSummaryByID(ws, alloc.Namespace, alloc.JobID) 4693 if err != nil { 4694 t.Fatalf("err: %v", err) 4695 } 4696 tgSummary = summary.Summary["web"] 4697 if tgSummary.Starting != 1 { 4698 t.Fatalf("expected starting: %v, actual: %v", 1, tgSummary.Starting) 4699 } 4700 4701 if watchFired(ws) { 4702 t.Fatalf("bad") 4703 } 4704 } 4705 4706 // This test ensures that the state store will mark the clients status as lost 4707 // when set rather than preferring the existing status. 4708 func TestStateStore_UpdateAlloc_Lost(t *testing.T) { 4709 t.Parallel() 4710 4711 state := testStateStore(t) 4712 alloc := mock.Alloc() 4713 alloc.ClientStatus = "foo" 4714 4715 if err := state.UpsertJob(999, alloc.Job); err != nil { 4716 t.Fatalf("err: %v", err) 4717 } 4718 4719 err := state.UpsertAllocs(1000, []*structs.Allocation{alloc}) 4720 if err != nil { 4721 t.Fatalf("err: %v", err) 4722 } 4723 4724 alloc2 := new(structs.Allocation) 4725 *alloc2 = *alloc 4726 alloc2.ClientStatus = structs.AllocClientStatusLost 4727 if err := state.UpsertAllocs(1001, []*structs.Allocation{alloc2}); err != nil { 4728 t.Fatalf("err: %v", err) 4729 } 4730 4731 ws := memdb.NewWatchSet() 4732 out, err := state.AllocByID(ws, alloc2.ID) 4733 if err != nil { 4734 t.Fatalf("err: %v", err) 4735 } 4736 4737 if out.ClientStatus != structs.AllocClientStatusLost { 4738 t.Fatalf("bad: %#v", out) 4739 } 4740 } 4741 4742 // This test ensures an allocation can be updated when there is no job 4743 // associated with it. This will happen when a job is stopped by an user which 4744 // has non-terminal allocations on clients 4745 func TestStateStore_UpdateAlloc_NoJob(t *testing.T) { 4746 t.Parallel() 4747 4748 state := testStateStore(t) 4749 alloc := mock.Alloc() 4750 4751 // Upsert a job 4752 state.UpsertJobSummary(998, mock.JobSummary(alloc.JobID)) 4753 if err := state.UpsertJob(999, alloc.Job); err != nil { 4754 t.Fatalf("err: %v", err) 4755 } 4756 4757 err := state.UpsertAllocs(1000, []*structs.Allocation{alloc}) 4758 if err != nil { 4759 t.Fatalf("err: %v", err) 4760 } 4761 4762 if err := state.DeleteJob(1001, alloc.Namespace, alloc.JobID); err != nil { 4763 t.Fatalf("err: %v", err) 4764 } 4765 4766 // Update the desired state of the allocation to stop 4767 allocCopy := alloc.Copy() 4768 allocCopy.DesiredStatus = structs.AllocDesiredStatusStop 4769 if err := state.UpsertAllocs(1002, []*structs.Allocation{allocCopy}); err != nil { 4770 t.Fatalf("err: %v", err) 4771 } 4772 4773 // Update the client state of the allocation to complete 4774 allocCopy1 := allocCopy.Copy() 4775 allocCopy1.ClientStatus = structs.AllocClientStatusComplete 4776 if err := state.UpdateAllocsFromClient(1003, []*structs.Allocation{allocCopy1}); err != nil { 4777 t.Fatalf("err: %v", err) 4778 } 4779 4780 ws := memdb.NewWatchSet() 4781 out, _ := state.AllocByID(ws, alloc.ID) 4782 // Update the modify index of the alloc before comparing 4783 allocCopy1.ModifyIndex = 1003 4784 if !reflect.DeepEqual(out, allocCopy1) { 4785 t.Fatalf("expected: %#v \n actual: %#v", allocCopy1, out) 4786 } 4787 } 4788 4789 func TestStateStore_UpdateAllocDesiredTransition(t *testing.T) { 4790 t.Parallel() 4791 require := require.New(t) 4792 4793 state := testStateStore(t) 4794 alloc := mock.Alloc() 4795 4796 require.Nil(state.UpsertJob(999, alloc.Job)) 4797 require.Nil(state.UpsertAllocs(1000, []*structs.Allocation{alloc})) 4798 4799 t1 := &structs.DesiredTransition{ 4800 Migrate: helper.BoolToPtr(true), 4801 } 4802 t2 := &structs.DesiredTransition{ 4803 Migrate: helper.BoolToPtr(false), 4804 } 4805 eval := &structs.Evaluation{ 4806 ID: uuid.Generate(), 4807 Namespace: alloc.Namespace, 4808 Priority: alloc.Job.Priority, 4809 Type: alloc.Job.Type, 4810 TriggeredBy: structs.EvalTriggerNodeDrain, 4811 JobID: alloc.Job.ID, 4812 JobModifyIndex: alloc.Job.ModifyIndex, 4813 Status: structs.EvalStatusPending, 4814 } 4815 evals := []*structs.Evaluation{eval} 4816 4817 m := map[string]*structs.DesiredTransition{alloc.ID: t1} 4818 require.Nil(state.UpdateAllocsDesiredTransitions(1001, m, evals)) 4819 4820 ws := memdb.NewWatchSet() 4821 out, err := state.AllocByID(ws, alloc.ID) 4822 require.Nil(err) 4823 require.NotNil(out.DesiredTransition.Migrate) 4824 require.True(*out.DesiredTransition.Migrate) 4825 require.EqualValues(1000, out.CreateIndex) 4826 require.EqualValues(1001, out.ModifyIndex) 4827 4828 index, err := state.Index("allocs") 4829 require.Nil(err) 4830 require.EqualValues(1001, index) 4831 4832 // Check the eval is created 4833 eout, err := state.EvalByID(nil, eval.ID) 4834 require.Nil(err) 4835 require.NotNil(eout) 4836 4837 m = map[string]*structs.DesiredTransition{alloc.ID: t2} 4838 require.Nil(state.UpdateAllocsDesiredTransitions(1002, m, evals)) 4839 4840 ws = memdb.NewWatchSet() 4841 out, err = state.AllocByID(ws, alloc.ID) 4842 require.Nil(err) 4843 require.NotNil(out.DesiredTransition.Migrate) 4844 require.False(*out.DesiredTransition.Migrate) 4845 require.EqualValues(1000, out.CreateIndex) 4846 require.EqualValues(1002, out.ModifyIndex) 4847 4848 index, err = state.Index("allocs") 4849 require.Nil(err) 4850 require.EqualValues(1002, index) 4851 4852 // Try with a bogus alloc id 4853 m = map[string]*structs.DesiredTransition{uuid.Generate(): t2} 4854 require.Nil(state.UpdateAllocsDesiredTransitions(1003, m, evals)) 4855 } 4856 4857 func TestStateStore_JobSummary(t *testing.T) { 4858 t.Parallel() 4859 4860 state := testStateStore(t) 4861 4862 // Add a job 4863 job := mock.Job() 4864 state.UpsertJob(900, job) 4865 4866 // Get the job back 4867 ws := memdb.NewWatchSet() 4868 outJob, _ := state.JobByID(ws, job.Namespace, job.ID) 4869 if outJob.CreateIndex != 900 { 4870 t.Fatalf("bad create index: %v", outJob.CreateIndex) 4871 } 4872 summary, _ := state.JobSummaryByID(ws, job.Namespace, job.ID) 4873 if summary.CreateIndex != 900 { 4874 t.Fatalf("bad create index: %v", summary.CreateIndex) 4875 } 4876 4877 // Upsert an allocation 4878 alloc := mock.Alloc() 4879 alloc.JobID = job.ID 4880 alloc.Job = job 4881 state.UpsertAllocs(910, []*structs.Allocation{alloc}) 4882 4883 // Update the alloc from client 4884 alloc1 := alloc.Copy() 4885 alloc1.ClientStatus = structs.AllocClientStatusPending 4886 alloc1.DesiredStatus = "" 4887 state.UpdateAllocsFromClient(920, []*structs.Allocation{alloc}) 4888 4889 alloc3 := alloc.Copy() 4890 alloc3.ClientStatus = structs.AllocClientStatusRunning 4891 alloc3.DesiredStatus = "" 4892 state.UpdateAllocsFromClient(930, []*structs.Allocation{alloc3}) 4893 4894 // Upsert the alloc 4895 alloc4 := alloc.Copy() 4896 alloc4.ClientStatus = structs.AllocClientStatusPending 4897 alloc4.DesiredStatus = structs.AllocDesiredStatusRun 4898 state.UpsertAllocs(950, []*structs.Allocation{alloc4}) 4899 4900 // Again upsert the alloc 4901 alloc5 := alloc.Copy() 4902 alloc5.ClientStatus = structs.AllocClientStatusPending 4903 alloc5.DesiredStatus = structs.AllocDesiredStatusRun 4904 state.UpsertAllocs(970, []*structs.Allocation{alloc5}) 4905 4906 if !watchFired(ws) { 4907 t.Fatalf("bad") 4908 } 4909 4910 expectedSummary := structs.JobSummary{ 4911 JobID: job.ID, 4912 Namespace: job.Namespace, 4913 Summary: map[string]structs.TaskGroupSummary{ 4914 "web": { 4915 Running: 1, 4916 }, 4917 }, 4918 Children: new(structs.JobChildrenSummary), 4919 CreateIndex: 900, 4920 ModifyIndex: 930, 4921 } 4922 4923 summary, _ = state.JobSummaryByID(ws, job.Namespace, job.ID) 4924 if !reflect.DeepEqual(&expectedSummary, summary) { 4925 t.Fatalf("expected: %#v, actual: %v", expectedSummary, summary) 4926 } 4927 4928 // De-register the job. 4929 state.DeleteJob(980, job.Namespace, job.ID) 4930 4931 // Shouldn't have any effect on the summary 4932 alloc6 := alloc.Copy() 4933 alloc6.ClientStatus = structs.AllocClientStatusRunning 4934 alloc6.DesiredStatus = "" 4935 state.UpdateAllocsFromClient(990, []*structs.Allocation{alloc6}) 4936 4937 // We shouldn't have any summary at this point 4938 summary, _ = state.JobSummaryByID(ws, job.Namespace, job.ID) 4939 if summary != nil { 4940 t.Fatalf("expected nil, actual: %#v", summary) 4941 } 4942 4943 // Re-register the same job 4944 job1 := mock.Job() 4945 job1.ID = job.ID 4946 state.UpsertJob(1000, job1) 4947 outJob2, _ := state.JobByID(ws, job1.Namespace, job1.ID) 4948 if outJob2.CreateIndex != 1000 { 4949 t.Fatalf("bad create index: %v", outJob2.CreateIndex) 4950 } 4951 summary, _ = state.JobSummaryByID(ws, job1.Namespace, job1.ID) 4952 if summary.CreateIndex != 1000 { 4953 t.Fatalf("bad create index: %v", summary.CreateIndex) 4954 } 4955 4956 // Upsert an allocation 4957 alloc7 := alloc.Copy() 4958 alloc7.JobID = outJob.ID 4959 alloc7.Job = outJob 4960 alloc7.ClientStatus = structs.AllocClientStatusComplete 4961 alloc7.DesiredStatus = structs.AllocDesiredStatusRun 4962 state.UpdateAllocsFromClient(1020, []*structs.Allocation{alloc7}) 4963 4964 expectedSummary = structs.JobSummary{ 4965 JobID: job.ID, 4966 Namespace: job.Namespace, 4967 Summary: map[string]structs.TaskGroupSummary{ 4968 "web": {}, 4969 }, 4970 Children: new(structs.JobChildrenSummary), 4971 CreateIndex: 1000, 4972 ModifyIndex: 1000, 4973 } 4974 4975 summary, _ = state.JobSummaryByID(ws, job1.Namespace, job1.ID) 4976 if !reflect.DeepEqual(&expectedSummary, summary) { 4977 t.Fatalf("expected: %#v, actual: %#v", expectedSummary, summary) 4978 } 4979 } 4980 4981 func TestStateStore_ReconcileJobSummary(t *testing.T) { 4982 t.Parallel() 4983 4984 state := testStateStore(t) 4985 4986 // Create an alloc 4987 alloc := mock.Alloc() 4988 4989 // Add another task group to the job 4990 tg2 := alloc.Job.TaskGroups[0].Copy() 4991 tg2.Name = "db" 4992 alloc.Job.TaskGroups = append(alloc.Job.TaskGroups, tg2) 4993 state.UpsertJob(100, alloc.Job) 4994 4995 // Create one more alloc for the db task group 4996 alloc2 := mock.Alloc() 4997 alloc2.TaskGroup = "db" 4998 alloc2.JobID = alloc.JobID 4999 alloc2.Job = alloc.Job 5000 5001 // Upserts the alloc 5002 state.UpsertAllocs(110, []*structs.Allocation{alloc, alloc2}) 5003 5004 // Change the state of the first alloc to running 5005 alloc3 := alloc.Copy() 5006 alloc3.ClientStatus = structs.AllocClientStatusRunning 5007 state.UpdateAllocsFromClient(120, []*structs.Allocation{alloc3}) 5008 5009 //Add some more allocs to the second tg 5010 alloc4 := mock.Alloc() 5011 alloc4.JobID = alloc.JobID 5012 alloc4.Job = alloc.Job 5013 alloc4.TaskGroup = "db" 5014 alloc5 := alloc4.Copy() 5015 alloc5.ClientStatus = structs.AllocClientStatusRunning 5016 5017 alloc6 := mock.Alloc() 5018 alloc6.JobID = alloc.JobID 5019 alloc6.Job = alloc.Job 5020 alloc6.TaskGroup = "db" 5021 alloc7 := alloc6.Copy() 5022 alloc7.ClientStatus = structs.AllocClientStatusComplete 5023 5024 alloc8 := mock.Alloc() 5025 alloc8.JobID = alloc.JobID 5026 alloc8.Job = alloc.Job 5027 alloc8.TaskGroup = "db" 5028 alloc9 := alloc8.Copy() 5029 alloc9.ClientStatus = structs.AllocClientStatusFailed 5030 5031 alloc10 := mock.Alloc() 5032 alloc10.JobID = alloc.JobID 5033 alloc10.Job = alloc.Job 5034 alloc10.TaskGroup = "db" 5035 alloc11 := alloc10.Copy() 5036 alloc11.ClientStatus = structs.AllocClientStatusLost 5037 5038 state.UpsertAllocs(130, []*structs.Allocation{alloc4, alloc6, alloc8, alloc10}) 5039 5040 state.UpdateAllocsFromClient(150, []*structs.Allocation{alloc5, alloc7, alloc9, alloc11}) 5041 5042 // DeleteJobSummary is a helper method and doesn't modify the indexes table 5043 state.DeleteJobSummary(130, alloc.Namespace, alloc.Job.ID) 5044 5045 state.ReconcileJobSummaries(120) 5046 5047 ws := memdb.NewWatchSet() 5048 summary, _ := state.JobSummaryByID(ws, alloc.Namespace, alloc.Job.ID) 5049 expectedSummary := structs.JobSummary{ 5050 JobID: alloc.Job.ID, 5051 Namespace: alloc.Namespace, 5052 Summary: map[string]structs.TaskGroupSummary{ 5053 "web": { 5054 Running: 1, 5055 }, 5056 "db": { 5057 Starting: 1, 5058 Running: 1, 5059 Failed: 1, 5060 Complete: 1, 5061 Lost: 1, 5062 }, 5063 }, 5064 CreateIndex: 100, 5065 ModifyIndex: 120, 5066 } 5067 if !reflect.DeepEqual(&expectedSummary, summary) { 5068 t.Fatalf("expected: %v, actual: %v", expectedSummary, summary) 5069 } 5070 } 5071 5072 func TestStateStore_ReconcileParentJobSummary(t *testing.T) { 5073 t.Parallel() 5074 require := require.New(t) 5075 5076 state := testStateStore(t) 5077 5078 // Add a node 5079 node := mock.Node() 5080 state.UpsertNode(80, node) 5081 5082 // Make a parameterized job 5083 job1 := mock.BatchJob() 5084 job1.ID = "test" 5085 job1.ParameterizedJob = &structs.ParameterizedJobConfig{ 5086 Payload: "random", 5087 } 5088 job1.TaskGroups[0].Count = 1 5089 state.UpsertJob(100, job1) 5090 5091 // Make a child job 5092 childJob := job1.Copy() 5093 childJob.ID = job1.ID + "dispatch-23423423" 5094 childJob.ParentID = job1.ID 5095 childJob.Dispatched = true 5096 childJob.Status = structs.JobStatusRunning 5097 5098 // Make some allocs for child job 5099 alloc := mock.Alloc() 5100 alloc.NodeID = node.ID 5101 alloc.Job = childJob 5102 alloc.JobID = childJob.ID 5103 alloc.ClientStatus = structs.AllocClientStatusRunning 5104 5105 alloc2 := mock.Alloc() 5106 alloc2.NodeID = node.ID 5107 alloc2.Job = childJob 5108 alloc2.JobID = childJob.ID 5109 alloc2.ClientStatus = structs.AllocClientStatusFailed 5110 5111 require.Nil(state.UpsertJob(110, childJob)) 5112 require.Nil(state.UpsertAllocs(111, []*structs.Allocation{alloc, alloc2})) 5113 5114 // Make the summary incorrect in the state store 5115 summary, err := state.JobSummaryByID(nil, job1.Namespace, job1.ID) 5116 require.Nil(err) 5117 5118 summary.Children = nil 5119 summary.Summary = make(map[string]structs.TaskGroupSummary) 5120 summary.Summary["web"] = structs.TaskGroupSummary{ 5121 Queued: 1, 5122 } 5123 5124 // Delete the child job summary 5125 state.DeleteJobSummary(125, childJob.Namespace, childJob.ID) 5126 5127 state.ReconcileJobSummaries(120) 5128 5129 ws := memdb.NewWatchSet() 5130 5131 // Verify parent summary is corrected 5132 summary, _ = state.JobSummaryByID(ws, alloc.Namespace, job1.ID) 5133 expectedSummary := structs.JobSummary{ 5134 JobID: job1.ID, 5135 Namespace: job1.Namespace, 5136 Summary: make(map[string]structs.TaskGroupSummary), 5137 Children: &structs.JobChildrenSummary{ 5138 Running: 1, 5139 }, 5140 CreateIndex: 100, 5141 ModifyIndex: 120, 5142 } 5143 require.Equal(&expectedSummary, summary) 5144 5145 // Verify child job summary is also correct 5146 childSummary, _ := state.JobSummaryByID(ws, childJob.Namespace, childJob.ID) 5147 expectedChildSummary := structs.JobSummary{ 5148 JobID: childJob.ID, 5149 Namespace: childJob.Namespace, 5150 Summary: map[string]structs.TaskGroupSummary{ 5151 "web": { 5152 Running: 1, 5153 Failed: 1, 5154 }, 5155 }, 5156 CreateIndex: 110, 5157 ModifyIndex: 120, 5158 } 5159 require.Equal(&expectedChildSummary, childSummary) 5160 } 5161 5162 func TestStateStore_UpdateAlloc_JobNotPresent(t *testing.T) { 5163 t.Parallel() 5164 5165 state := testStateStore(t) 5166 5167 alloc := mock.Alloc() 5168 state.UpsertJob(100, alloc.Job) 5169 state.UpsertAllocs(200, []*structs.Allocation{alloc}) 5170 5171 // Delete the job 5172 state.DeleteJob(300, alloc.Namespace, alloc.Job.ID) 5173 5174 // Update the alloc 5175 alloc1 := alloc.Copy() 5176 alloc1.ClientStatus = structs.AllocClientStatusRunning 5177 5178 // Updating allocation should not throw any error 5179 if err := state.UpdateAllocsFromClient(400, []*structs.Allocation{alloc1}); err != nil { 5180 t.Fatalf("expect err: %v", err) 5181 } 5182 5183 // Re-Register the job 5184 state.UpsertJob(500, alloc.Job) 5185 5186 // Update the alloc again 5187 alloc2 := alloc.Copy() 5188 alloc2.ClientStatus = structs.AllocClientStatusComplete 5189 if err := state.UpdateAllocsFromClient(400, []*structs.Allocation{alloc1}); err != nil { 5190 t.Fatalf("expect err: %v", err) 5191 } 5192 5193 // Job Summary of the newly registered job shouldn't account for the 5194 // allocation update for the older job 5195 expectedSummary := structs.JobSummary{ 5196 JobID: alloc1.JobID, 5197 Namespace: alloc1.Namespace, 5198 Summary: map[string]structs.TaskGroupSummary{ 5199 "web": {}, 5200 }, 5201 Children: new(structs.JobChildrenSummary), 5202 CreateIndex: 500, 5203 ModifyIndex: 500, 5204 } 5205 5206 ws := memdb.NewWatchSet() 5207 summary, _ := state.JobSummaryByID(ws, alloc.Namespace, alloc.Job.ID) 5208 if !reflect.DeepEqual(&expectedSummary, summary) { 5209 t.Fatalf("expected: %v, actual: %v", expectedSummary, summary) 5210 } 5211 } 5212 5213 func TestStateStore_EvictAlloc_Alloc(t *testing.T) { 5214 t.Parallel() 5215 5216 state := testStateStore(t) 5217 alloc := mock.Alloc() 5218 5219 state.UpsertJobSummary(999, mock.JobSummary(alloc.JobID)) 5220 err := state.UpsertAllocs(1000, []*structs.Allocation{alloc}) 5221 if err != nil { 5222 t.Fatalf("err: %v", err) 5223 } 5224 5225 alloc2 := new(structs.Allocation) 5226 *alloc2 = *alloc 5227 alloc2.DesiredStatus = structs.AllocDesiredStatusEvict 5228 err = state.UpsertAllocs(1001, []*structs.Allocation{alloc2}) 5229 if err != nil { 5230 t.Fatalf("err: %v", err) 5231 } 5232 5233 ws := memdb.NewWatchSet() 5234 out, err := state.AllocByID(ws, alloc.ID) 5235 if err != nil { 5236 t.Fatalf("err: %v", err) 5237 } 5238 5239 if out.DesiredStatus != structs.AllocDesiredStatusEvict { 5240 t.Fatalf("bad: %#v %#v", alloc, out) 5241 } 5242 5243 index, err := state.Index("allocs") 5244 if err != nil { 5245 t.Fatalf("err: %v", err) 5246 } 5247 if index != 1001 { 5248 t.Fatalf("bad: %d", index) 5249 } 5250 } 5251 5252 func TestStateStore_AllocsByNode(t *testing.T) { 5253 t.Parallel() 5254 5255 state := testStateStore(t) 5256 var allocs []*structs.Allocation 5257 5258 for i := 0; i < 10; i++ { 5259 alloc := mock.Alloc() 5260 alloc.NodeID = "foo" 5261 allocs = append(allocs, alloc) 5262 } 5263 5264 for idx, alloc := range allocs { 5265 state.UpsertJobSummary(uint64(900+idx), mock.JobSummary(alloc.JobID)) 5266 } 5267 5268 err := state.UpsertAllocs(1000, allocs) 5269 if err != nil { 5270 t.Fatalf("err: %v", err) 5271 } 5272 5273 ws := memdb.NewWatchSet() 5274 out, err := state.AllocsByNode(ws, "foo") 5275 if err != nil { 5276 t.Fatalf("err: %v", err) 5277 } 5278 5279 sort.Sort(AllocIDSort(allocs)) 5280 sort.Sort(AllocIDSort(out)) 5281 5282 if !reflect.DeepEqual(allocs, out) { 5283 t.Fatalf("bad: %#v %#v", allocs, out) 5284 } 5285 5286 if watchFired(ws) { 5287 t.Fatalf("bad") 5288 } 5289 } 5290 5291 func TestStateStore_AllocsByNodeTerminal(t *testing.T) { 5292 t.Parallel() 5293 5294 state := testStateStore(t) 5295 var allocs, term, nonterm []*structs.Allocation 5296 5297 for i := 0; i < 10; i++ { 5298 alloc := mock.Alloc() 5299 alloc.NodeID = "foo" 5300 if i%2 == 0 { 5301 alloc.DesiredStatus = structs.AllocDesiredStatusStop 5302 term = append(term, alloc) 5303 } else { 5304 nonterm = append(nonterm, alloc) 5305 } 5306 allocs = append(allocs, alloc) 5307 } 5308 5309 for idx, alloc := range allocs { 5310 state.UpsertJobSummary(uint64(900+idx), mock.JobSummary(alloc.JobID)) 5311 } 5312 5313 err := state.UpsertAllocs(1000, allocs) 5314 if err != nil { 5315 t.Fatalf("err: %v", err) 5316 } 5317 5318 // Verify the terminal allocs 5319 ws := memdb.NewWatchSet() 5320 out, err := state.AllocsByNodeTerminal(ws, "foo", true) 5321 if err != nil { 5322 t.Fatalf("err: %v", err) 5323 } 5324 5325 sort.Sort(AllocIDSort(term)) 5326 sort.Sort(AllocIDSort(out)) 5327 5328 if !reflect.DeepEqual(term, out) { 5329 t.Fatalf("bad: %#v %#v", term, out) 5330 } 5331 5332 // Verify the non-terminal allocs 5333 out, err = state.AllocsByNodeTerminal(ws, "foo", false) 5334 if err != nil { 5335 t.Fatalf("err: %v", err) 5336 } 5337 5338 sort.Sort(AllocIDSort(nonterm)) 5339 sort.Sort(AllocIDSort(out)) 5340 5341 if !reflect.DeepEqual(nonterm, out) { 5342 t.Fatalf("bad: %#v %#v", nonterm, out) 5343 } 5344 5345 if watchFired(ws) { 5346 t.Fatalf("bad") 5347 } 5348 } 5349 5350 func TestStateStore_AllocsByJob(t *testing.T) { 5351 t.Parallel() 5352 5353 state := testStateStore(t) 5354 var allocs []*structs.Allocation 5355 5356 for i := 0; i < 10; i++ { 5357 alloc := mock.Alloc() 5358 alloc.JobID = "foo" 5359 allocs = append(allocs, alloc) 5360 } 5361 5362 for i, alloc := range allocs { 5363 state.UpsertJobSummary(uint64(900+i), mock.JobSummary(alloc.JobID)) 5364 } 5365 5366 err := state.UpsertAllocs(1000, allocs) 5367 if err != nil { 5368 t.Fatalf("err: %v", err) 5369 } 5370 5371 ws := memdb.NewWatchSet() 5372 out, err := state.AllocsByJob(ws, mock.Alloc().Namespace, "foo", false) 5373 if err != nil { 5374 t.Fatalf("err: %v", err) 5375 } 5376 5377 sort.Sort(AllocIDSort(allocs)) 5378 sort.Sort(AllocIDSort(out)) 5379 5380 if !reflect.DeepEqual(allocs, out) { 5381 t.Fatalf("bad: %#v %#v", allocs, out) 5382 } 5383 5384 if watchFired(ws) { 5385 t.Fatalf("bad") 5386 } 5387 } 5388 5389 func TestStateStore_AllocsForRegisteredJob(t *testing.T) { 5390 t.Parallel() 5391 5392 state := testStateStore(t) 5393 var allocs []*structs.Allocation 5394 var allocs1 []*structs.Allocation 5395 5396 job := mock.Job() 5397 job.ID = "foo" 5398 state.UpsertJob(100, job) 5399 for i := 0; i < 3; i++ { 5400 alloc := mock.Alloc() 5401 alloc.Job = job 5402 alloc.JobID = job.ID 5403 allocs = append(allocs, alloc) 5404 } 5405 if err := state.UpsertAllocs(200, allocs); err != nil { 5406 t.Fatalf("err: %v", err) 5407 } 5408 5409 if err := state.DeleteJob(250, job.Namespace, job.ID); err != nil { 5410 t.Fatalf("err: %v", err) 5411 } 5412 5413 job1 := mock.Job() 5414 job1.ID = "foo" 5415 job1.CreateIndex = 50 5416 state.UpsertJob(300, job1) 5417 for i := 0; i < 4; i++ { 5418 alloc := mock.Alloc() 5419 alloc.Job = job1 5420 alloc.JobID = job1.ID 5421 allocs1 = append(allocs1, alloc) 5422 } 5423 5424 if err := state.UpsertAllocs(1000, allocs1); err != nil { 5425 t.Fatalf("err: %v", err) 5426 } 5427 5428 ws := memdb.NewWatchSet() 5429 out, err := state.AllocsByJob(ws, job1.Namespace, job1.ID, true) 5430 if err != nil { 5431 t.Fatalf("err: %v", err) 5432 } 5433 5434 expected := len(allocs) + len(allocs1) 5435 if len(out) != expected { 5436 t.Fatalf("expected: %v, actual: %v", expected, len(out)) 5437 } 5438 5439 out1, err := state.AllocsByJob(ws, job1.Namespace, job1.ID, false) 5440 if err != nil { 5441 t.Fatalf("bad: %v", err) 5442 } 5443 5444 expected = len(allocs1) 5445 if len(out1) != expected { 5446 t.Fatalf("expected: %v, actual: %v", expected, len(out1)) 5447 } 5448 5449 if watchFired(ws) { 5450 t.Fatalf("bad") 5451 } 5452 } 5453 5454 func TestStateStore_AllocsByIDPrefix(t *testing.T) { 5455 t.Parallel() 5456 5457 state := testStateStore(t) 5458 var allocs []*structs.Allocation 5459 5460 ids := []string{ 5461 "aaaaaaaa-7bfb-395d-eb95-0685af2176b2", 5462 "aaaaaaab-7bfb-395d-eb95-0685af2176b2", 5463 "aaaaaabb-7bfb-395d-eb95-0685af2176b2", 5464 "aaaaabbb-7bfb-395d-eb95-0685af2176b2", 5465 "aaaabbbb-7bfb-395d-eb95-0685af2176b2", 5466 "aaabbbbb-7bfb-395d-eb95-0685af2176b2", 5467 "aabbbbbb-7bfb-395d-eb95-0685af2176b2", 5468 "abbbbbbb-7bfb-395d-eb95-0685af2176b2", 5469 "bbbbbbbb-7bfb-395d-eb95-0685af2176b2", 5470 } 5471 for i := 0; i < 9; i++ { 5472 alloc := mock.Alloc() 5473 alloc.ID = ids[i] 5474 allocs = append(allocs, alloc) 5475 } 5476 5477 for i, alloc := range allocs { 5478 state.UpsertJobSummary(uint64(900+i), mock.JobSummary(alloc.JobID)) 5479 } 5480 5481 err := state.UpsertAllocs(1000, allocs) 5482 if err != nil { 5483 t.Fatalf("err: %v", err) 5484 } 5485 5486 ws := memdb.NewWatchSet() 5487 iter, err := state.AllocsByIDPrefix(ws, structs.DefaultNamespace, "aaaa") 5488 if err != nil { 5489 t.Fatalf("err: %v", err) 5490 } 5491 5492 gatherAllocs := func(iter memdb.ResultIterator) []*structs.Allocation { 5493 var allocs []*structs.Allocation 5494 for { 5495 raw := iter.Next() 5496 if raw == nil { 5497 break 5498 } 5499 allocs = append(allocs, raw.(*structs.Allocation)) 5500 } 5501 return allocs 5502 } 5503 5504 out := gatherAllocs(iter) 5505 if len(out) != 5 { 5506 t.Fatalf("bad: expected five allocations, got: %#v", out) 5507 } 5508 5509 sort.Sort(AllocIDSort(allocs)) 5510 5511 for index, alloc := range out { 5512 if ids[index] != alloc.ID { 5513 t.Fatalf("bad: got unexpected id: %s", alloc.ID) 5514 } 5515 } 5516 5517 iter, err = state.AllocsByIDPrefix(ws, structs.DefaultNamespace, "b-a7bfb") 5518 if err != nil { 5519 t.Fatalf("err: %v", err) 5520 } 5521 5522 out = gatherAllocs(iter) 5523 if len(out) != 0 { 5524 t.Fatalf("bad: unexpected zero allocations, got: %#v", out) 5525 } 5526 5527 if watchFired(ws) { 5528 t.Fatalf("bad") 5529 } 5530 } 5531 5532 func TestStateStore_Allocs(t *testing.T) { 5533 t.Parallel() 5534 5535 state := testStateStore(t) 5536 var allocs []*structs.Allocation 5537 5538 for i := 0; i < 10; i++ { 5539 alloc := mock.Alloc() 5540 allocs = append(allocs, alloc) 5541 } 5542 for i, alloc := range allocs { 5543 state.UpsertJobSummary(uint64(900+i), mock.JobSummary(alloc.JobID)) 5544 } 5545 5546 err := state.UpsertAllocs(1000, allocs) 5547 if err != nil { 5548 t.Fatalf("err: %v", err) 5549 } 5550 5551 ws := memdb.NewWatchSet() 5552 iter, err := state.Allocs(ws) 5553 if err != nil { 5554 t.Fatalf("err: %v", err) 5555 } 5556 5557 var out []*structs.Allocation 5558 for { 5559 raw := iter.Next() 5560 if raw == nil { 5561 break 5562 } 5563 out = append(out, raw.(*structs.Allocation)) 5564 } 5565 5566 sort.Sort(AllocIDSort(allocs)) 5567 sort.Sort(AllocIDSort(out)) 5568 5569 if !reflect.DeepEqual(allocs, out) { 5570 t.Fatalf("bad: %#v %#v", allocs, out) 5571 } 5572 5573 if watchFired(ws) { 5574 t.Fatalf("bad") 5575 } 5576 } 5577 5578 func TestStateStore_Allocs_PrevAlloc(t *testing.T) { 5579 t.Parallel() 5580 5581 state := testStateStore(t) 5582 var allocs []*structs.Allocation 5583 5584 require := require.New(t) 5585 for i := 0; i < 5; i++ { 5586 alloc := mock.Alloc() 5587 allocs = append(allocs, alloc) 5588 } 5589 for i, alloc := range allocs { 5590 state.UpsertJobSummary(uint64(900+i), mock.JobSummary(alloc.JobID)) 5591 } 5592 // Set some previous alloc ids 5593 allocs[1].PreviousAllocation = allocs[0].ID 5594 allocs[2].PreviousAllocation = allocs[1].ID 5595 5596 err := state.UpsertAllocs(1000, allocs) 5597 require.Nil(err) 5598 5599 ws := memdb.NewWatchSet() 5600 iter, err := state.Allocs(ws) 5601 require.Nil(err) 5602 5603 var out []*structs.Allocation 5604 for { 5605 raw := iter.Next() 5606 if raw == nil { 5607 break 5608 } 5609 out = append(out, raw.(*structs.Allocation)) 5610 } 5611 5612 // Set expected NextAllocation fields 5613 allocs[0].NextAllocation = allocs[1].ID 5614 allocs[1].NextAllocation = allocs[2].ID 5615 5616 sort.Sort(AllocIDSort(allocs)) 5617 sort.Sort(AllocIDSort(out)) 5618 5619 require.Equal(allocs, out) 5620 require.False(watchFired(ws)) 5621 5622 // Insert another alloc, verify index of previous alloc also got updated 5623 alloc := mock.Alloc() 5624 alloc.PreviousAllocation = allocs[0].ID 5625 err = state.UpsertAllocs(1001, []*structs.Allocation{alloc}) 5626 require.Nil(err) 5627 alloc0, err := state.AllocByID(nil, allocs[0].ID) 5628 require.Nil(err) 5629 require.Equal(alloc0.ModifyIndex, uint64(1001)) 5630 } 5631 5632 func TestStateStore_RestoreAlloc(t *testing.T) { 5633 t.Parallel() 5634 5635 state := testStateStore(t) 5636 alloc := mock.Alloc() 5637 5638 restore, err := state.Restore() 5639 if err != nil { 5640 t.Fatalf("err: %v", err) 5641 } 5642 5643 err = restore.AllocRestore(alloc) 5644 if err != nil { 5645 t.Fatalf("err: %v", err) 5646 } 5647 5648 restore.Commit() 5649 5650 ws := memdb.NewWatchSet() 5651 out, err := state.AllocByID(ws, alloc.ID) 5652 if err != nil { 5653 t.Fatalf("err: %v", err) 5654 } 5655 5656 if !reflect.DeepEqual(out, alloc) { 5657 t.Fatalf("Bad: %#v %#v", out, alloc) 5658 } 5659 5660 if watchFired(ws) { 5661 t.Fatalf("bad") 5662 } 5663 } 5664 5665 func TestStateStore_SetJobStatus_ForceStatus(t *testing.T) { 5666 t.Parallel() 5667 5668 state := testStateStore(t) 5669 txn := state.db.Txn(true) 5670 5671 // Create and insert a mock job. 5672 job := mock.Job() 5673 job.Status = "" 5674 job.ModifyIndex = 0 5675 if err := txn.Insert("jobs", job); err != nil { 5676 t.Fatalf("job insert failed: %v", err) 5677 } 5678 5679 exp := "foobar" 5680 index := uint64(1000) 5681 if err := state.setJobStatus(index, txn, job, false, exp); err != nil { 5682 t.Fatalf("setJobStatus() failed: %v", err) 5683 } 5684 5685 i, err := txn.First("jobs", "id", job.Namespace, job.ID) 5686 if err != nil { 5687 t.Fatalf("job lookup failed: %v", err) 5688 } 5689 updated := i.(*structs.Job) 5690 5691 if updated.Status != exp { 5692 t.Fatalf("setJobStatus() set %v; expected %v", updated.Status, exp) 5693 } 5694 5695 if updated.ModifyIndex != index { 5696 t.Fatalf("setJobStatus() set %d; expected %d", updated.ModifyIndex, index) 5697 } 5698 } 5699 5700 func TestStateStore_SetJobStatus_NoOp(t *testing.T) { 5701 t.Parallel() 5702 5703 state := testStateStore(t) 5704 txn := state.db.Txn(true) 5705 5706 // Create and insert a mock job that should be pending. 5707 job := mock.Job() 5708 job.Status = structs.JobStatusPending 5709 job.ModifyIndex = 10 5710 if err := txn.Insert("jobs", job); err != nil { 5711 t.Fatalf("job insert failed: %v", err) 5712 } 5713 5714 index := uint64(1000) 5715 if err := state.setJobStatus(index, txn, job, false, ""); err != nil { 5716 t.Fatalf("setJobStatus() failed: %v", err) 5717 } 5718 5719 i, err := txn.First("jobs", "id", job.Namespace, job.ID) 5720 if err != nil { 5721 t.Fatalf("job lookup failed: %v", err) 5722 } 5723 updated := i.(*structs.Job) 5724 5725 if updated.ModifyIndex == index { 5726 t.Fatalf("setJobStatus() should have been a no-op") 5727 } 5728 } 5729 5730 func TestStateStore_SetJobStatus(t *testing.T) { 5731 t.Parallel() 5732 5733 state := testStateStore(t) 5734 txn := state.db.Txn(true) 5735 5736 // Create and insert a mock job that should be pending but has an incorrect 5737 // status. 5738 job := mock.Job() 5739 job.Status = "foobar" 5740 job.ModifyIndex = 10 5741 if err := txn.Insert("jobs", job); err != nil { 5742 t.Fatalf("job insert failed: %v", err) 5743 } 5744 5745 index := uint64(1000) 5746 if err := state.setJobStatus(index, txn, job, false, ""); err != nil { 5747 t.Fatalf("setJobStatus() failed: %v", err) 5748 } 5749 5750 i, err := txn.First("jobs", "id", job.Namespace, job.ID) 5751 if err != nil { 5752 t.Fatalf("job lookup failed: %v", err) 5753 } 5754 updated := i.(*structs.Job) 5755 5756 if updated.Status != structs.JobStatusPending { 5757 t.Fatalf("setJobStatus() set %v; expected %v", updated.Status, structs.JobStatusPending) 5758 } 5759 5760 if updated.ModifyIndex != index { 5761 t.Fatalf("setJobStatus() set %d; expected %d", updated.ModifyIndex, index) 5762 } 5763 } 5764 5765 func TestStateStore_GetJobStatus_NoEvalsOrAllocs(t *testing.T) { 5766 t.Parallel() 5767 5768 job := mock.Job() 5769 state := testStateStore(t) 5770 txn := state.db.Txn(false) 5771 status, err := state.getJobStatus(txn, job, false) 5772 if err != nil { 5773 t.Fatalf("getJobStatus() failed: %v", err) 5774 } 5775 5776 if status != structs.JobStatusPending { 5777 t.Fatalf("getJobStatus() returned %v; expected %v", status, structs.JobStatusPending) 5778 } 5779 } 5780 5781 func TestStateStore_GetJobStatus_NoEvalsOrAllocs_Periodic(t *testing.T) { 5782 t.Parallel() 5783 5784 job := mock.PeriodicJob() 5785 state := testStateStore(t) 5786 txn := state.db.Txn(false) 5787 status, err := state.getJobStatus(txn, job, false) 5788 if err != nil { 5789 t.Fatalf("getJobStatus() failed: %v", err) 5790 } 5791 5792 if status != structs.JobStatusRunning { 5793 t.Fatalf("getJobStatus() returned %v; expected %v", status, structs.JobStatusRunning) 5794 } 5795 } 5796 5797 func TestStateStore_GetJobStatus_NoEvalsOrAllocs_EvalDelete(t *testing.T) { 5798 t.Parallel() 5799 5800 job := mock.Job() 5801 state := testStateStore(t) 5802 txn := state.db.Txn(false) 5803 status, err := state.getJobStatus(txn, job, true) 5804 if err != nil { 5805 t.Fatalf("getJobStatus() failed: %v", err) 5806 } 5807 5808 if status != structs.JobStatusDead { 5809 t.Fatalf("getJobStatus() returned %v; expected %v", status, structs.JobStatusDead) 5810 } 5811 } 5812 5813 func TestStateStore_GetJobStatus_DeadEvalsAndAllocs(t *testing.T) { 5814 t.Parallel() 5815 5816 state := testStateStore(t) 5817 job := mock.Job() 5818 5819 // Create a mock alloc that is dead. 5820 alloc := mock.Alloc() 5821 alloc.JobID = job.ID 5822 alloc.DesiredStatus = structs.AllocDesiredStatusStop 5823 state.UpsertJobSummary(999, mock.JobSummary(alloc.JobID)) 5824 if err := state.UpsertAllocs(1000, []*structs.Allocation{alloc}); err != nil { 5825 t.Fatalf("err: %v", err) 5826 } 5827 5828 // Create a mock eval that is complete 5829 eval := mock.Eval() 5830 eval.JobID = job.ID 5831 eval.Status = structs.EvalStatusComplete 5832 if err := state.UpsertEvals(1001, []*structs.Evaluation{eval}); err != nil { 5833 t.Fatalf("err: %v", err) 5834 } 5835 5836 txn := state.db.Txn(false) 5837 status, err := state.getJobStatus(txn, job, false) 5838 if err != nil { 5839 t.Fatalf("getJobStatus() failed: %v", err) 5840 } 5841 5842 if status != structs.JobStatusDead { 5843 t.Fatalf("getJobStatus() returned %v; expected %v", status, structs.JobStatusDead) 5844 } 5845 } 5846 5847 func TestStateStore_GetJobStatus_RunningAlloc(t *testing.T) { 5848 t.Parallel() 5849 5850 state := testStateStore(t) 5851 job := mock.Job() 5852 5853 // Create a mock alloc that is running. 5854 alloc := mock.Alloc() 5855 alloc.JobID = job.ID 5856 alloc.DesiredStatus = structs.AllocDesiredStatusRun 5857 state.UpsertJobSummary(999, mock.JobSummary(alloc.JobID)) 5858 if err := state.UpsertAllocs(1000, []*structs.Allocation{alloc}); err != nil { 5859 t.Fatalf("err: %v", err) 5860 } 5861 5862 txn := state.db.Txn(false) 5863 status, err := state.getJobStatus(txn, job, true) 5864 if err != nil { 5865 t.Fatalf("getJobStatus() failed: %v", err) 5866 } 5867 5868 if status != structs.JobStatusRunning { 5869 t.Fatalf("getJobStatus() returned %v; expected %v", status, structs.JobStatusRunning) 5870 } 5871 } 5872 5873 func TestStateStore_GetJobStatus_PeriodicJob(t *testing.T) { 5874 t.Parallel() 5875 5876 state := testStateStore(t) 5877 job := mock.PeriodicJob() 5878 5879 txn := state.db.Txn(false) 5880 status, err := state.getJobStatus(txn, job, false) 5881 if err != nil { 5882 t.Fatalf("getJobStatus() failed: %v", err) 5883 } 5884 5885 if status != structs.JobStatusRunning { 5886 t.Fatalf("getJobStatus() returned %v; expected %v", status, structs.JobStatusRunning) 5887 } 5888 5889 // Mark it as stopped 5890 job.Stop = true 5891 status, err = state.getJobStatus(txn, job, false) 5892 if err != nil { 5893 t.Fatalf("getJobStatus() failed: %v", err) 5894 } 5895 5896 if status != structs.JobStatusDead { 5897 t.Fatalf("getJobStatus() returned %v; expected %v", status, structs.JobStatusDead) 5898 } 5899 } 5900 5901 func TestStateStore_GetJobStatus_ParameterizedJob(t *testing.T) { 5902 t.Parallel() 5903 5904 state := testStateStore(t) 5905 job := mock.Job() 5906 job.ParameterizedJob = &structs.ParameterizedJobConfig{} 5907 5908 txn := state.db.Txn(false) 5909 status, err := state.getJobStatus(txn, job, false) 5910 if err != nil { 5911 t.Fatalf("getJobStatus() failed: %v", err) 5912 } 5913 5914 if status != structs.JobStatusRunning { 5915 t.Fatalf("getJobStatus() returned %v; expected %v", status, structs.JobStatusRunning) 5916 } 5917 5918 // Mark it as stopped 5919 job.Stop = true 5920 status, err = state.getJobStatus(txn, job, false) 5921 if err != nil { 5922 t.Fatalf("getJobStatus() failed: %v", err) 5923 } 5924 5925 if status != structs.JobStatusDead { 5926 t.Fatalf("getJobStatus() returned %v; expected %v", status, structs.JobStatusDead) 5927 } 5928 } 5929 5930 func TestStateStore_SetJobStatus_PendingEval(t *testing.T) { 5931 t.Parallel() 5932 5933 state := testStateStore(t) 5934 job := mock.Job() 5935 5936 // Create a mock eval that is pending. 5937 eval := mock.Eval() 5938 eval.JobID = job.ID 5939 eval.Status = structs.EvalStatusPending 5940 if err := state.UpsertEvals(1000, []*structs.Evaluation{eval}); err != nil { 5941 t.Fatalf("err: %v", err) 5942 } 5943 5944 txn := state.db.Txn(false) 5945 status, err := state.getJobStatus(txn, job, true) 5946 if err != nil { 5947 t.Fatalf("getJobStatus() failed: %v", err) 5948 } 5949 5950 if status != structs.JobStatusPending { 5951 t.Fatalf("getJobStatus() returned %v; expected %v", status, structs.JobStatusPending) 5952 } 5953 } 5954 5955 // TestStateStore_SetJobStatus_SystemJob asserts that system jobs are still 5956 // considered running until explicitly stopped. 5957 func TestStateStore_SetJobStatus_SystemJob(t *testing.T) { 5958 t.Parallel() 5959 5960 state := testStateStore(t) 5961 job := mock.SystemJob() 5962 5963 // Create a mock eval that is pending. 5964 eval := mock.Eval() 5965 eval.JobID = job.ID 5966 eval.Type = job.Type 5967 eval.Status = structs.EvalStatusComplete 5968 if err := state.UpsertEvals(1000, []*structs.Evaluation{eval}); err != nil { 5969 t.Fatalf("err: %v", err) 5970 } 5971 5972 txn := state.db.Txn(false) 5973 status, err := state.getJobStatus(txn, job, true) 5974 if err != nil { 5975 t.Fatalf("getJobStatus() failed: %v", err) 5976 } 5977 5978 if expected := structs.JobStatusRunning; status != expected { 5979 t.Fatalf("getJobStatus() returned %v; expected %v", status, expected) 5980 } 5981 5982 // Stop the job 5983 job.Stop = true 5984 status, err = state.getJobStatus(txn, job, true) 5985 if err != nil { 5986 t.Fatalf("getJobStatus() failed: %v", err) 5987 } 5988 5989 if expected := structs.JobStatusDead; status != expected { 5990 t.Fatalf("getJobStatus() returned %v; expected %v", status, expected) 5991 } 5992 } 5993 5994 func TestStateJobSummary_UpdateJobCount(t *testing.T) { 5995 t.Parallel() 5996 5997 state := testStateStore(t) 5998 alloc := mock.Alloc() 5999 job := alloc.Job 6000 job.TaskGroups[0].Count = 3 6001 6002 // Create watchsets so we can test that upsert fires the watch 6003 ws := memdb.NewWatchSet() 6004 if _, err := state.JobSummaryByID(ws, job.Namespace, job.ID); err != nil { 6005 t.Fatalf("bad: %v", err) 6006 } 6007 6008 if err := state.UpsertJob(1000, job); err != nil { 6009 t.Fatalf("err: %v", err) 6010 } 6011 6012 if err := state.UpsertAllocs(1001, []*structs.Allocation{alloc}); err != nil { 6013 t.Fatalf("err: %v", err) 6014 } 6015 6016 if !watchFired(ws) { 6017 t.Fatalf("bad") 6018 } 6019 6020 ws = memdb.NewWatchSet() 6021 summary, _ := state.JobSummaryByID(ws, job.Namespace, job.ID) 6022 expectedSummary := structs.JobSummary{ 6023 JobID: job.ID, 6024 Namespace: job.Namespace, 6025 Summary: map[string]structs.TaskGroupSummary{ 6026 "web": { 6027 Starting: 1, 6028 }, 6029 }, 6030 Children: new(structs.JobChildrenSummary), 6031 CreateIndex: 1000, 6032 ModifyIndex: 1001, 6033 } 6034 if !reflect.DeepEqual(summary, &expectedSummary) { 6035 t.Fatalf("expected: %v, actual: %v", expectedSummary, summary) 6036 } 6037 6038 // Create watchsets so we can test that upsert fires the watch 6039 ws2 := memdb.NewWatchSet() 6040 if _, err := state.JobSummaryByID(ws2, job.Namespace, job.ID); err != nil { 6041 t.Fatalf("bad: %v", err) 6042 } 6043 6044 alloc2 := mock.Alloc() 6045 alloc2.Job = job 6046 alloc2.JobID = job.ID 6047 6048 alloc3 := mock.Alloc() 6049 alloc3.Job = job 6050 alloc3.JobID = job.ID 6051 6052 if err := state.UpsertAllocs(1002, []*structs.Allocation{alloc2, alloc3}); err != nil { 6053 t.Fatalf("err: %v", err) 6054 } 6055 6056 if !watchFired(ws2) { 6057 t.Fatalf("bad") 6058 } 6059 6060 outA, _ := state.AllocByID(ws, alloc3.ID) 6061 6062 summary, _ = state.JobSummaryByID(ws, job.Namespace, job.ID) 6063 expectedSummary = structs.JobSummary{ 6064 JobID: job.ID, 6065 Namespace: job.Namespace, 6066 Summary: map[string]structs.TaskGroupSummary{ 6067 "web": { 6068 Starting: 3, 6069 }, 6070 }, 6071 Children: new(structs.JobChildrenSummary), 6072 CreateIndex: job.CreateIndex, 6073 ModifyIndex: outA.ModifyIndex, 6074 } 6075 if !reflect.DeepEqual(summary, &expectedSummary) { 6076 t.Fatalf("expected summary: %v, actual: %v", expectedSummary, summary) 6077 } 6078 6079 // Create watchsets so we can test that upsert fires the watch 6080 ws3 := memdb.NewWatchSet() 6081 if _, err := state.JobSummaryByID(ws3, job.Namespace, job.ID); err != nil { 6082 t.Fatalf("bad: %v", err) 6083 } 6084 6085 alloc4 := mock.Alloc() 6086 alloc4.ID = alloc2.ID 6087 alloc4.Job = alloc2.Job 6088 alloc4.JobID = alloc2.JobID 6089 alloc4.ClientStatus = structs.AllocClientStatusComplete 6090 6091 alloc5 := mock.Alloc() 6092 alloc5.ID = alloc3.ID 6093 alloc5.Job = alloc3.Job 6094 alloc5.JobID = alloc3.JobID 6095 alloc5.ClientStatus = structs.AllocClientStatusComplete 6096 6097 if err := state.UpdateAllocsFromClient(1004, []*structs.Allocation{alloc4, alloc5}); err != nil { 6098 t.Fatalf("err: %v", err) 6099 } 6100 6101 if !watchFired(ws2) { 6102 t.Fatalf("bad") 6103 } 6104 6105 outA, _ = state.AllocByID(ws, alloc5.ID) 6106 summary, _ = state.JobSummaryByID(ws, job.Namespace, job.ID) 6107 expectedSummary = structs.JobSummary{ 6108 JobID: job.ID, 6109 Namespace: job.Namespace, 6110 Summary: map[string]structs.TaskGroupSummary{ 6111 "web": { 6112 Complete: 2, 6113 Starting: 1, 6114 }, 6115 }, 6116 Children: new(structs.JobChildrenSummary), 6117 CreateIndex: job.CreateIndex, 6118 ModifyIndex: outA.ModifyIndex, 6119 } 6120 if !reflect.DeepEqual(summary, &expectedSummary) { 6121 t.Fatalf("expected: %v, actual: %v", expectedSummary, summary) 6122 } 6123 } 6124 6125 func TestJobSummary_UpdateClientStatus(t *testing.T) { 6126 t.Parallel() 6127 6128 state := testStateStore(t) 6129 alloc := mock.Alloc() 6130 job := alloc.Job 6131 job.TaskGroups[0].Count = 3 6132 6133 alloc2 := mock.Alloc() 6134 alloc2.Job = job 6135 alloc2.JobID = job.ID 6136 6137 alloc3 := mock.Alloc() 6138 alloc3.Job = job 6139 alloc3.JobID = job.ID 6140 6141 err := state.UpsertJob(1000, job) 6142 if err != nil { 6143 t.Fatalf("err: %v", err) 6144 } 6145 6146 if err := state.UpsertAllocs(1001, []*structs.Allocation{alloc, alloc2, alloc3}); err != nil { 6147 t.Fatalf("err: %v", err) 6148 } 6149 6150 ws := memdb.NewWatchSet() 6151 summary, _ := state.JobSummaryByID(ws, job.Namespace, job.ID) 6152 if summary.Summary["web"].Starting != 3 { 6153 t.Fatalf("bad job summary: %v", summary) 6154 } 6155 6156 alloc4 := mock.Alloc() 6157 alloc4.ID = alloc2.ID 6158 alloc4.Job = alloc2.Job 6159 alloc4.JobID = alloc2.JobID 6160 alloc4.ClientStatus = structs.AllocClientStatusComplete 6161 6162 alloc5 := mock.Alloc() 6163 alloc5.ID = alloc3.ID 6164 alloc5.Job = alloc3.Job 6165 alloc5.JobID = alloc3.JobID 6166 alloc5.ClientStatus = structs.AllocClientStatusFailed 6167 6168 alloc6 := mock.Alloc() 6169 alloc6.ID = alloc.ID 6170 alloc6.Job = alloc.Job 6171 alloc6.JobID = alloc.JobID 6172 alloc6.ClientStatus = structs.AllocClientStatusRunning 6173 6174 if err := state.UpdateAllocsFromClient(1002, []*structs.Allocation{alloc4, alloc5, alloc6}); err != nil { 6175 t.Fatalf("err: %v", err) 6176 } 6177 6178 if !watchFired(ws) { 6179 t.Fatalf("bad") 6180 } 6181 6182 summary, _ = state.JobSummaryByID(ws, job.Namespace, job.ID) 6183 if summary.Summary["web"].Running != 1 || summary.Summary["web"].Failed != 1 || summary.Summary["web"].Complete != 1 { 6184 t.Fatalf("bad job summary: %v", summary) 6185 } 6186 6187 alloc7 := mock.Alloc() 6188 alloc7.Job = alloc.Job 6189 alloc7.JobID = alloc.JobID 6190 6191 if err := state.UpsertAllocs(1003, []*structs.Allocation{alloc7}); err != nil { 6192 t.Fatalf("err: %v", err) 6193 } 6194 summary, _ = state.JobSummaryByID(ws, job.Namespace, job.ID) 6195 if summary.Summary["web"].Starting != 1 || summary.Summary["web"].Running != 1 || summary.Summary["web"].Failed != 1 || summary.Summary["web"].Complete != 1 { 6196 t.Fatalf("bad job summary: %v", summary) 6197 } 6198 } 6199 6200 // Test that nonexistent deployment can't be updated 6201 func TestStateStore_UpsertDeploymentStatusUpdate_Nonexistent(t *testing.T) { 6202 t.Parallel() 6203 6204 state := testStateStore(t) 6205 6206 // Update the nonexistent deployment 6207 req := &structs.DeploymentStatusUpdateRequest{ 6208 DeploymentUpdate: &structs.DeploymentStatusUpdate{ 6209 DeploymentID: uuid.Generate(), 6210 Status: structs.DeploymentStatusRunning, 6211 }, 6212 } 6213 err := state.UpdateDeploymentStatus(2, req) 6214 if err == nil || !strings.Contains(err.Error(), "does not exist") { 6215 t.Fatalf("expected error updating the status because the deployment doesn't exist") 6216 } 6217 } 6218 6219 // Test that terminal deployment can't be updated 6220 func TestStateStore_UpsertDeploymentStatusUpdate_Terminal(t *testing.T) { 6221 t.Parallel() 6222 6223 state := testStateStore(t) 6224 6225 // Insert a terminal deployment 6226 d := mock.Deployment() 6227 d.Status = structs.DeploymentStatusFailed 6228 6229 if err := state.UpsertDeployment(1, d); err != nil { 6230 t.Fatalf("bad: %v", err) 6231 } 6232 6233 // Update the deployment 6234 req := &structs.DeploymentStatusUpdateRequest{ 6235 DeploymentUpdate: &structs.DeploymentStatusUpdate{ 6236 DeploymentID: d.ID, 6237 Status: structs.DeploymentStatusRunning, 6238 }, 6239 } 6240 err := state.UpdateDeploymentStatus(2, req) 6241 if err == nil || !strings.Contains(err.Error(), "has terminal status") { 6242 t.Fatalf("expected error updating the status because the deployment is terminal") 6243 } 6244 } 6245 6246 // Test that a non terminal deployment is updated and that a job and eval are 6247 // created. 6248 func TestStateStore_UpsertDeploymentStatusUpdate_NonTerminal(t *testing.T) { 6249 t.Parallel() 6250 6251 state := testStateStore(t) 6252 6253 // Insert a deployment 6254 d := mock.Deployment() 6255 if err := state.UpsertDeployment(1, d); err != nil { 6256 t.Fatalf("bad: %v", err) 6257 } 6258 6259 // Create an eval and a job 6260 e := mock.Eval() 6261 j := mock.Job() 6262 6263 // Update the deployment 6264 status, desc := structs.DeploymentStatusFailed, "foo" 6265 req := &structs.DeploymentStatusUpdateRequest{ 6266 DeploymentUpdate: &structs.DeploymentStatusUpdate{ 6267 DeploymentID: d.ID, 6268 Status: status, 6269 StatusDescription: desc, 6270 }, 6271 Job: j, 6272 Eval: e, 6273 } 6274 err := state.UpdateDeploymentStatus(2, req) 6275 if err != nil { 6276 t.Fatalf("bad: %v", err) 6277 } 6278 6279 // Check that the status was updated properly 6280 ws := memdb.NewWatchSet() 6281 dout, err := state.DeploymentByID(ws, d.ID) 6282 if err != nil { 6283 t.Fatalf("bad: %v", err) 6284 } 6285 if dout.Status != status || dout.StatusDescription != desc { 6286 t.Fatalf("bad: %#v", dout) 6287 } 6288 6289 // Check that the evaluation was created 6290 eout, _ := state.EvalByID(ws, e.ID) 6291 if err != nil { 6292 t.Fatalf("bad: %v", err) 6293 } 6294 if eout == nil { 6295 t.Fatalf("bad: %#v", eout) 6296 } 6297 6298 // Check that the job was created 6299 jout, _ := state.JobByID(ws, j.Namespace, j.ID) 6300 if err != nil { 6301 t.Fatalf("bad: %v", err) 6302 } 6303 if jout == nil { 6304 t.Fatalf("bad: %#v", jout) 6305 } 6306 } 6307 6308 // Test that when a deployment is updated to successful the job is updated to 6309 // stable 6310 func TestStateStore_UpsertDeploymentStatusUpdate_Successful(t *testing.T) { 6311 t.Parallel() 6312 6313 state := testStateStore(t) 6314 6315 // Insert a job 6316 job := mock.Job() 6317 if err := state.UpsertJob(1, job); err != nil { 6318 t.Fatalf("bad: %v", err) 6319 } 6320 6321 // Insert a deployment 6322 d := structs.NewDeployment(job) 6323 if err := state.UpsertDeployment(2, d); err != nil { 6324 t.Fatalf("bad: %v", err) 6325 } 6326 6327 // Update the deployment 6328 req := &structs.DeploymentStatusUpdateRequest{ 6329 DeploymentUpdate: &structs.DeploymentStatusUpdate{ 6330 DeploymentID: d.ID, 6331 Status: structs.DeploymentStatusSuccessful, 6332 StatusDescription: structs.DeploymentStatusDescriptionSuccessful, 6333 }, 6334 } 6335 err := state.UpdateDeploymentStatus(3, req) 6336 if err != nil { 6337 t.Fatalf("bad: %v", err) 6338 } 6339 6340 // Check that the status was updated properly 6341 ws := memdb.NewWatchSet() 6342 dout, err := state.DeploymentByID(ws, d.ID) 6343 if err != nil { 6344 t.Fatalf("bad: %v", err) 6345 } 6346 if dout.Status != structs.DeploymentStatusSuccessful || 6347 dout.StatusDescription != structs.DeploymentStatusDescriptionSuccessful { 6348 t.Fatalf("bad: %#v", dout) 6349 } 6350 6351 // Check that the job was created 6352 jout, _ := state.JobByID(ws, job.Namespace, job.ID) 6353 if err != nil { 6354 t.Fatalf("bad: %v", err) 6355 } 6356 if jout == nil { 6357 t.Fatalf("bad: %#v", jout) 6358 } 6359 if !jout.Stable { 6360 t.Fatalf("job not marked stable %#v", jout) 6361 } 6362 if jout.Version != d.JobVersion { 6363 t.Fatalf("job version changed; got %d; want %d", jout.Version, d.JobVersion) 6364 } 6365 } 6366 6367 func TestStateStore_UpdateJobStability(t *testing.T) { 6368 t.Parallel() 6369 6370 state := testStateStore(t) 6371 6372 // Insert a job twice to get two versions 6373 job := mock.Job() 6374 if err := state.UpsertJob(1, job); err != nil { 6375 t.Fatalf("bad: %v", err) 6376 } 6377 6378 if err := state.UpsertJob(2, job); err != nil { 6379 t.Fatalf("bad: %v", err) 6380 } 6381 6382 // Update the stability to true 6383 err := state.UpdateJobStability(3, job.Namespace, job.ID, 0, true) 6384 if err != nil { 6385 t.Fatalf("bad: %v", err) 6386 } 6387 6388 // Check that the job was updated properly 6389 ws := memdb.NewWatchSet() 6390 jout, _ := state.JobByIDAndVersion(ws, job.Namespace, job.ID, 0) 6391 if err != nil { 6392 t.Fatalf("bad: %v", err) 6393 } 6394 if jout == nil { 6395 t.Fatalf("bad: %#v", jout) 6396 } 6397 if !jout.Stable { 6398 t.Fatalf("job not marked stable %#v", jout) 6399 } 6400 6401 // Update the stability to false 6402 err = state.UpdateJobStability(3, job.Namespace, job.ID, 0, false) 6403 if err != nil { 6404 t.Fatalf("bad: %v", err) 6405 } 6406 6407 // Check that the job was updated properly 6408 jout, _ = state.JobByIDAndVersion(ws, job.Namespace, job.ID, 0) 6409 if err != nil { 6410 t.Fatalf("bad: %v", err) 6411 } 6412 if jout == nil { 6413 t.Fatalf("bad: %#v", jout) 6414 } 6415 if jout.Stable { 6416 t.Fatalf("job marked stable %#v", jout) 6417 } 6418 } 6419 6420 // Test that nonexistent deployment can't be promoted 6421 func TestStateStore_UpsertDeploymentPromotion_Nonexistent(t *testing.T) { 6422 t.Parallel() 6423 6424 state := testStateStore(t) 6425 6426 // Promote the nonexistent deployment 6427 req := &structs.ApplyDeploymentPromoteRequest{ 6428 DeploymentPromoteRequest: structs.DeploymentPromoteRequest{ 6429 DeploymentID: uuid.Generate(), 6430 All: true, 6431 }, 6432 } 6433 err := state.UpdateDeploymentPromotion(2, req) 6434 if err == nil || !strings.Contains(err.Error(), "does not exist") { 6435 t.Fatalf("expected error promoting because the deployment doesn't exist") 6436 } 6437 } 6438 6439 // Test that terminal deployment can't be updated 6440 func TestStateStore_UpsertDeploymentPromotion_Terminal(t *testing.T) { 6441 t.Parallel() 6442 6443 state := testStateStore(t) 6444 6445 // Insert a terminal deployment 6446 d := mock.Deployment() 6447 d.Status = structs.DeploymentStatusFailed 6448 6449 if err := state.UpsertDeployment(1, d); err != nil { 6450 t.Fatalf("bad: %v", err) 6451 } 6452 6453 // Promote the deployment 6454 req := &structs.ApplyDeploymentPromoteRequest{ 6455 DeploymentPromoteRequest: structs.DeploymentPromoteRequest{ 6456 DeploymentID: d.ID, 6457 All: true, 6458 }, 6459 } 6460 err := state.UpdateDeploymentPromotion(2, req) 6461 if err == nil || !strings.Contains(err.Error(), "has terminal status") { 6462 t.Fatalf("expected error updating the status because the deployment is terminal: %v", err) 6463 } 6464 } 6465 6466 // Test promoting unhealthy canaries in a deployment. 6467 func TestStateStore_UpsertDeploymentPromotion_Unhealthy(t *testing.T) { 6468 t.Parallel() 6469 6470 state := testStateStore(t) 6471 require := require.New(t) 6472 6473 // Create a job 6474 j := mock.Job() 6475 require.Nil(state.UpsertJob(1, j)) 6476 6477 // Create a deployment 6478 d := mock.Deployment() 6479 d.JobID = j.ID 6480 d.TaskGroups["web"].DesiredCanaries = 2 6481 require.Nil(state.UpsertDeployment(2, d)) 6482 6483 // Create a set of allocations 6484 c1 := mock.Alloc() 6485 c1.JobID = j.ID 6486 c1.DeploymentID = d.ID 6487 d.TaskGroups[c1.TaskGroup].PlacedCanaries = append(d.TaskGroups[c1.TaskGroup].PlacedCanaries, c1.ID) 6488 c2 := mock.Alloc() 6489 c2.JobID = j.ID 6490 c2.DeploymentID = d.ID 6491 d.TaskGroups[c2.TaskGroup].PlacedCanaries = append(d.TaskGroups[c2.TaskGroup].PlacedCanaries, c2.ID) 6492 6493 // Create a healthy but terminal alloc 6494 c3 := mock.Alloc() 6495 c3.JobID = j.ID 6496 c3.DeploymentID = d.ID 6497 c3.DesiredStatus = structs.AllocDesiredStatusStop 6498 c3.DeploymentStatus = &structs.AllocDeploymentStatus{Healthy: helper.BoolToPtr(true)} 6499 d.TaskGroups[c3.TaskGroup].PlacedCanaries = append(d.TaskGroups[c3.TaskGroup].PlacedCanaries, c3.ID) 6500 6501 require.Nil(state.UpsertAllocs(3, []*structs.Allocation{c1, c2, c3})) 6502 6503 // Promote the canaries 6504 req := &structs.ApplyDeploymentPromoteRequest{ 6505 DeploymentPromoteRequest: structs.DeploymentPromoteRequest{ 6506 DeploymentID: d.ID, 6507 All: true, 6508 }, 6509 } 6510 err := state.UpdateDeploymentPromotion(4, req) 6511 require.NotNil(err) 6512 require.Contains(err.Error(), `Task group "web" has 0/2 healthy allocations`) 6513 } 6514 6515 // Test promoting a deployment with no canaries 6516 func TestStateStore_UpsertDeploymentPromotion_NoCanaries(t *testing.T) { 6517 t.Parallel() 6518 6519 state := testStateStore(t) 6520 require := require.New(t) 6521 6522 // Create a job 6523 j := mock.Job() 6524 require.Nil(state.UpsertJob(1, j)) 6525 6526 // Create a deployment 6527 d := mock.Deployment() 6528 d.TaskGroups["web"].DesiredCanaries = 2 6529 d.JobID = j.ID 6530 require.Nil(state.UpsertDeployment(2, d)) 6531 6532 // Promote the canaries 6533 req := &structs.ApplyDeploymentPromoteRequest{ 6534 DeploymentPromoteRequest: structs.DeploymentPromoteRequest{ 6535 DeploymentID: d.ID, 6536 All: true, 6537 }, 6538 } 6539 err := state.UpdateDeploymentPromotion(4, req) 6540 require.NotNil(err) 6541 require.Contains(err.Error(), `Task group "web" has 0/2 healthy allocations`) 6542 } 6543 6544 // Test promoting all canaries in a deployment. 6545 func TestStateStore_UpsertDeploymentPromotion_All(t *testing.T) { 6546 t.Parallel() 6547 6548 state := testStateStore(t) 6549 6550 // Create a job with two task groups 6551 j := mock.Job() 6552 tg1 := j.TaskGroups[0] 6553 tg2 := tg1.Copy() 6554 tg2.Name = "foo" 6555 j.TaskGroups = append(j.TaskGroups, tg2) 6556 if err := state.UpsertJob(1, j); err != nil { 6557 t.Fatalf("bad: %v", err) 6558 } 6559 6560 // Create a deployment 6561 d := mock.Deployment() 6562 d.StatusDescription = structs.DeploymentStatusDescriptionRunningNeedsPromotion 6563 d.JobID = j.ID 6564 d.TaskGroups = map[string]*structs.DeploymentState{ 6565 "web": { 6566 DesiredTotal: 10, 6567 DesiredCanaries: 1, 6568 }, 6569 "foo": { 6570 DesiredTotal: 10, 6571 DesiredCanaries: 1, 6572 }, 6573 } 6574 if err := state.UpsertDeployment(2, d); err != nil { 6575 t.Fatalf("bad: %v", err) 6576 } 6577 6578 // Create a set of allocations 6579 c1 := mock.Alloc() 6580 c1.JobID = j.ID 6581 c1.DeploymentID = d.ID 6582 d.TaskGroups[c1.TaskGroup].PlacedCanaries = append(d.TaskGroups[c1.TaskGroup].PlacedCanaries, c1.ID) 6583 c1.DeploymentStatus = &structs.AllocDeploymentStatus{ 6584 Healthy: helper.BoolToPtr(true), 6585 } 6586 c2 := mock.Alloc() 6587 c2.JobID = j.ID 6588 c2.DeploymentID = d.ID 6589 d.TaskGroups[c2.TaskGroup].PlacedCanaries = append(d.TaskGroups[c2.TaskGroup].PlacedCanaries, c2.ID) 6590 c2.TaskGroup = tg2.Name 6591 c2.DeploymentStatus = &structs.AllocDeploymentStatus{ 6592 Healthy: helper.BoolToPtr(true), 6593 } 6594 6595 if err := state.UpsertAllocs(3, []*structs.Allocation{c1, c2}); err != nil { 6596 t.Fatalf("err: %v", err) 6597 } 6598 6599 // Create an eval 6600 e := mock.Eval() 6601 6602 // Promote the canaries 6603 req := &structs.ApplyDeploymentPromoteRequest{ 6604 DeploymentPromoteRequest: structs.DeploymentPromoteRequest{ 6605 DeploymentID: d.ID, 6606 All: true, 6607 }, 6608 Eval: e, 6609 } 6610 err := state.UpdateDeploymentPromotion(4, req) 6611 if err != nil { 6612 t.Fatalf("bad: %v", err) 6613 } 6614 6615 // Check that the status per task group was updated properly 6616 ws := memdb.NewWatchSet() 6617 dout, err := state.DeploymentByID(ws, d.ID) 6618 if err != nil { 6619 t.Fatalf("bad: %v", err) 6620 } 6621 if dout.StatusDescription != structs.DeploymentStatusDescriptionRunning { 6622 t.Fatalf("status description not updated: got %v; want %v", dout.StatusDescription, structs.DeploymentStatusDescriptionRunning) 6623 } 6624 if len(dout.TaskGroups) != 2 { 6625 t.Fatalf("bad: %#v", dout.TaskGroups) 6626 } 6627 for tg, state := range dout.TaskGroups { 6628 if !state.Promoted { 6629 t.Fatalf("bad: group %q not promoted %#v", tg, state) 6630 } 6631 } 6632 6633 // Check that the evaluation was created 6634 eout, _ := state.EvalByID(ws, e.ID) 6635 if err != nil { 6636 t.Fatalf("bad: %v", err) 6637 } 6638 if eout == nil { 6639 t.Fatalf("bad: %#v", eout) 6640 } 6641 } 6642 6643 // Test promoting a subset of canaries in a deployment. 6644 func TestStateStore_UpsertDeploymentPromotion_Subset(t *testing.T) { 6645 t.Parallel() 6646 require := require.New(t) 6647 6648 state := testStateStore(t) 6649 6650 // Create a job with two task groups 6651 j := mock.Job() 6652 tg1 := j.TaskGroups[0] 6653 tg2 := tg1.Copy() 6654 tg2.Name = "foo" 6655 j.TaskGroups = append(j.TaskGroups, tg2) 6656 require.Nil(state.UpsertJob(1, j)) 6657 6658 // Create a deployment 6659 d := mock.Deployment() 6660 d.JobID = j.ID 6661 d.TaskGroups = map[string]*structs.DeploymentState{ 6662 "web": { 6663 DesiredTotal: 10, 6664 DesiredCanaries: 1, 6665 }, 6666 "foo": { 6667 DesiredTotal: 10, 6668 DesiredCanaries: 1, 6669 }, 6670 } 6671 require.Nil(state.UpsertDeployment(2, d)) 6672 6673 // Create a set of allocations for both groups, including an unhealthy one 6674 c1 := mock.Alloc() 6675 c1.JobID = j.ID 6676 c1.DeploymentID = d.ID 6677 d.TaskGroups[c1.TaskGroup].PlacedCanaries = append(d.TaskGroups[c1.TaskGroup].PlacedCanaries, c1.ID) 6678 c1.DeploymentStatus = &structs.AllocDeploymentStatus{ 6679 Healthy: helper.BoolToPtr(true), 6680 Canary: true, 6681 } 6682 6683 // Should still be a canary 6684 c2 := mock.Alloc() 6685 c2.JobID = j.ID 6686 c2.DeploymentID = d.ID 6687 d.TaskGroups[c2.TaskGroup].PlacedCanaries = append(d.TaskGroups[c2.TaskGroup].PlacedCanaries, c2.ID) 6688 c2.TaskGroup = tg2.Name 6689 c2.DeploymentStatus = &structs.AllocDeploymentStatus{ 6690 Healthy: helper.BoolToPtr(true), 6691 Canary: true, 6692 } 6693 6694 c3 := mock.Alloc() 6695 c3.JobID = j.ID 6696 c3.DeploymentID = d.ID 6697 d.TaskGroups[c3.TaskGroup].PlacedCanaries = append(d.TaskGroups[c3.TaskGroup].PlacedCanaries, c3.ID) 6698 c3.DeploymentStatus = &structs.AllocDeploymentStatus{ 6699 Healthy: helper.BoolToPtr(false), 6700 Canary: true, 6701 } 6702 6703 require.Nil(state.UpsertAllocs(3, []*structs.Allocation{c1, c2, c3})) 6704 6705 // Create an eval 6706 e := mock.Eval() 6707 6708 // Promote the canaries 6709 req := &structs.ApplyDeploymentPromoteRequest{ 6710 DeploymentPromoteRequest: structs.DeploymentPromoteRequest{ 6711 DeploymentID: d.ID, 6712 Groups: []string{"web"}, 6713 }, 6714 Eval: e, 6715 } 6716 require.Nil(state.UpdateDeploymentPromotion(4, req)) 6717 6718 // Check that the status per task group was updated properly 6719 ws := memdb.NewWatchSet() 6720 dout, err := state.DeploymentByID(ws, d.ID) 6721 require.Nil(err) 6722 require.Len(dout.TaskGroups, 2) 6723 require.Contains(dout.TaskGroups, "web") 6724 require.True(dout.TaskGroups["web"].Promoted) 6725 6726 // Check that the evaluation was created 6727 eout, err := state.EvalByID(ws, e.ID) 6728 require.Nil(err) 6729 require.NotNil(eout) 6730 6731 // Check the canary field was set properly 6732 aout1, err1 := state.AllocByID(ws, c1.ID) 6733 aout2, err2 := state.AllocByID(ws, c2.ID) 6734 aout3, err3 := state.AllocByID(ws, c3.ID) 6735 require.Nil(err1) 6736 require.Nil(err2) 6737 require.Nil(err3) 6738 require.NotNil(aout1) 6739 require.NotNil(aout2) 6740 require.NotNil(aout3) 6741 require.False(aout1.DeploymentStatus.Canary) 6742 require.True(aout2.DeploymentStatus.Canary) 6743 require.True(aout3.DeploymentStatus.Canary) 6744 } 6745 6746 // Test that allocation health can't be set against a nonexistent deployment 6747 func TestStateStore_UpsertDeploymentAllocHealth_Nonexistent(t *testing.T) { 6748 t.Parallel() 6749 6750 state := testStateStore(t) 6751 6752 // Set health against the nonexistent deployment 6753 req := &structs.ApplyDeploymentAllocHealthRequest{ 6754 DeploymentAllocHealthRequest: structs.DeploymentAllocHealthRequest{ 6755 DeploymentID: uuid.Generate(), 6756 HealthyAllocationIDs: []string{uuid.Generate()}, 6757 }, 6758 } 6759 err := state.UpdateDeploymentAllocHealth(2, req) 6760 if err == nil || !strings.Contains(err.Error(), "does not exist") { 6761 t.Fatalf("expected error because the deployment doesn't exist: %v", err) 6762 } 6763 } 6764 6765 // Test that allocation health can't be set against a terminal deployment 6766 func TestStateStore_UpsertDeploymentAllocHealth_Terminal(t *testing.T) { 6767 t.Parallel() 6768 6769 state := testStateStore(t) 6770 6771 // Insert a terminal deployment 6772 d := mock.Deployment() 6773 d.Status = structs.DeploymentStatusFailed 6774 6775 if err := state.UpsertDeployment(1, d); err != nil { 6776 t.Fatalf("bad: %v", err) 6777 } 6778 6779 // Set health against the terminal deployment 6780 req := &structs.ApplyDeploymentAllocHealthRequest{ 6781 DeploymentAllocHealthRequest: structs.DeploymentAllocHealthRequest{ 6782 DeploymentID: d.ID, 6783 HealthyAllocationIDs: []string{uuid.Generate()}, 6784 }, 6785 } 6786 err := state.UpdateDeploymentAllocHealth(2, req) 6787 if err == nil || !strings.Contains(err.Error(), "has terminal status") { 6788 t.Fatalf("expected error because the deployment is terminal: %v", err) 6789 } 6790 } 6791 6792 // Test that allocation health can't be set against a nonexistent alloc 6793 func TestStateStore_UpsertDeploymentAllocHealth_BadAlloc_Nonexistent(t *testing.T) { 6794 t.Parallel() 6795 6796 state := testStateStore(t) 6797 6798 // Insert a deployment 6799 d := mock.Deployment() 6800 if err := state.UpsertDeployment(1, d); err != nil { 6801 t.Fatalf("bad: %v", err) 6802 } 6803 6804 // Set health against the terminal deployment 6805 req := &structs.ApplyDeploymentAllocHealthRequest{ 6806 DeploymentAllocHealthRequest: structs.DeploymentAllocHealthRequest{ 6807 DeploymentID: d.ID, 6808 HealthyAllocationIDs: []string{uuid.Generate()}, 6809 }, 6810 } 6811 err := state.UpdateDeploymentAllocHealth(2, req) 6812 if err == nil || !strings.Contains(err.Error(), "unknown alloc") { 6813 t.Fatalf("expected error because the alloc doesn't exist: %v", err) 6814 } 6815 } 6816 6817 // Test that a deployments PlacedCanaries is properly updated 6818 func TestStateStore_UpsertDeploymentAlloc_Canaries(t *testing.T) { 6819 t.Parallel() 6820 6821 state := testStateStore(t) 6822 6823 // Create a deployment 6824 d1 := mock.Deployment() 6825 require.NoError(t, state.UpsertDeployment(2, d1)) 6826 6827 // Create a Job 6828 job := mock.Job() 6829 require.NoError(t, state.UpsertJob(3, job)) 6830 6831 // Create alloc with canary status 6832 a := mock.Alloc() 6833 a.JobID = job.ID 6834 a.DeploymentID = d1.ID 6835 a.DeploymentStatus = &structs.AllocDeploymentStatus{ 6836 Healthy: helper.BoolToPtr(false), 6837 Canary: true, 6838 } 6839 require.NoError(t, state.UpsertAllocs(4, []*structs.Allocation{a})) 6840 6841 // Pull the deployment from state 6842 ws := memdb.NewWatchSet() 6843 deploy, err := state.DeploymentByID(ws, d1.ID) 6844 require.NoError(t, err) 6845 6846 // Ensure that PlacedCanaries is accurate 6847 require.Equal(t, 1, len(deploy.TaskGroups[job.TaskGroups[0].Name].PlacedCanaries)) 6848 6849 // Create alloc without canary status 6850 b := mock.Alloc() 6851 b.JobID = job.ID 6852 b.DeploymentID = d1.ID 6853 b.DeploymentStatus = &structs.AllocDeploymentStatus{ 6854 Healthy: helper.BoolToPtr(false), 6855 Canary: false, 6856 } 6857 require.NoError(t, state.UpsertAllocs(4, []*structs.Allocation{b})) 6858 6859 // Pull the deployment from state 6860 ws = memdb.NewWatchSet() 6861 deploy, err = state.DeploymentByID(ws, d1.ID) 6862 require.NoError(t, err) 6863 6864 // Ensure that PlacedCanaries is accurate 6865 require.Equal(t, 1, len(deploy.TaskGroups[job.TaskGroups[0].Name].PlacedCanaries)) 6866 6867 // Create a second deployment 6868 d2 := mock.Deployment() 6869 require.NoError(t, state.UpsertDeployment(5, d2)) 6870 6871 c := mock.Alloc() 6872 c.JobID = job.ID 6873 c.DeploymentID = d2.ID 6874 c.DeploymentStatus = &structs.AllocDeploymentStatus{ 6875 Healthy: helper.BoolToPtr(false), 6876 Canary: true, 6877 } 6878 require.NoError(t, state.UpsertAllocs(6, []*structs.Allocation{c})) 6879 6880 ws = memdb.NewWatchSet() 6881 deploy2, err := state.DeploymentByID(ws, d2.ID) 6882 require.NoError(t, err) 6883 6884 // Ensure that PlacedCanaries is accurate 6885 require.Equal(t, 1, len(deploy2.TaskGroups[job.TaskGroups[0].Name].PlacedCanaries)) 6886 } 6887 6888 func TestStateStore_UpsertDeploymentAlloc_NoCanaries(t *testing.T) { 6889 t.Parallel() 6890 6891 state := testStateStore(t) 6892 6893 // Create a deployment 6894 d1 := mock.Deployment() 6895 require.NoError(t, state.UpsertDeployment(2, d1)) 6896 6897 // Create a Job 6898 job := mock.Job() 6899 require.NoError(t, state.UpsertJob(3, job)) 6900 6901 // Create alloc with canary status 6902 a := mock.Alloc() 6903 a.JobID = job.ID 6904 a.DeploymentID = d1.ID 6905 a.DeploymentStatus = &structs.AllocDeploymentStatus{ 6906 Healthy: helper.BoolToPtr(true), 6907 Canary: false, 6908 } 6909 require.NoError(t, state.UpsertAllocs(4, []*structs.Allocation{a})) 6910 6911 // Pull the deployment from state 6912 ws := memdb.NewWatchSet() 6913 deploy, err := state.DeploymentByID(ws, d1.ID) 6914 require.NoError(t, err) 6915 6916 // Ensure that PlacedCanaries is accurate 6917 require.Equal(t, 0, len(deploy.TaskGroups[job.TaskGroups[0].Name].PlacedCanaries)) 6918 } 6919 6920 // Test that allocation health can't be set for an alloc with mismatched 6921 // deployment ids 6922 func TestStateStore_UpsertDeploymentAllocHealth_BadAlloc_MismatchDeployment(t *testing.T) { 6923 t.Parallel() 6924 6925 state := testStateStore(t) 6926 6927 // Insert two deployment 6928 d1 := mock.Deployment() 6929 d2 := mock.Deployment() 6930 if err := state.UpsertDeployment(1, d1); err != nil { 6931 t.Fatalf("bad: %v", err) 6932 } 6933 if err := state.UpsertDeployment(2, d2); err != nil { 6934 t.Fatalf("bad: %v", err) 6935 } 6936 6937 // Insert an alloc for a random deployment 6938 a := mock.Alloc() 6939 a.DeploymentID = d1.ID 6940 if err := state.UpsertAllocs(3, []*structs.Allocation{a}); err != nil { 6941 t.Fatalf("bad: %v", err) 6942 } 6943 6944 // Set health against the terminal deployment 6945 req := &structs.ApplyDeploymentAllocHealthRequest{ 6946 DeploymentAllocHealthRequest: structs.DeploymentAllocHealthRequest{ 6947 DeploymentID: d2.ID, 6948 HealthyAllocationIDs: []string{a.ID}, 6949 }, 6950 } 6951 err := state.UpdateDeploymentAllocHealth(4, req) 6952 if err == nil || !strings.Contains(err.Error(), "not part of deployment") { 6953 t.Fatalf("expected error because the alloc isn't part of the deployment: %v", err) 6954 } 6955 } 6956 6957 // Test that allocation health is properly set 6958 func TestStateStore_UpsertDeploymentAllocHealth(t *testing.T) { 6959 t.Parallel() 6960 6961 state := testStateStore(t) 6962 6963 // Insert a deployment 6964 d := mock.Deployment() 6965 d.TaskGroups["web"].ProgressDeadline = 5 * time.Minute 6966 if err := state.UpsertDeployment(1, d); err != nil { 6967 t.Fatalf("bad: %v", err) 6968 } 6969 6970 // Insert two allocations 6971 a1 := mock.Alloc() 6972 a1.DeploymentID = d.ID 6973 a2 := mock.Alloc() 6974 a2.DeploymentID = d.ID 6975 if err := state.UpsertAllocs(2, []*structs.Allocation{a1, a2}); err != nil { 6976 t.Fatalf("bad: %v", err) 6977 } 6978 6979 // Create a job to roll back to 6980 j := mock.Job() 6981 6982 // Create an eval that should be upserted 6983 e := mock.Eval() 6984 6985 // Create a status update for the deployment 6986 status, desc := structs.DeploymentStatusFailed, "foo" 6987 u := &structs.DeploymentStatusUpdate{ 6988 DeploymentID: d.ID, 6989 Status: status, 6990 StatusDescription: desc, 6991 } 6992 6993 // Capture the time for the update 6994 ts := time.Now() 6995 6996 // Set health against the deployment 6997 req := &structs.ApplyDeploymentAllocHealthRequest{ 6998 DeploymentAllocHealthRequest: structs.DeploymentAllocHealthRequest{ 6999 DeploymentID: d.ID, 7000 HealthyAllocationIDs: []string{a1.ID}, 7001 UnhealthyAllocationIDs: []string{a2.ID}, 7002 }, 7003 Job: j, 7004 Eval: e, 7005 DeploymentUpdate: u, 7006 Timestamp: ts, 7007 } 7008 err := state.UpdateDeploymentAllocHealth(3, req) 7009 if err != nil { 7010 t.Fatalf("bad: %v", err) 7011 } 7012 7013 // Check that the status was updated properly 7014 ws := memdb.NewWatchSet() 7015 dout, err := state.DeploymentByID(ws, d.ID) 7016 if err != nil { 7017 t.Fatalf("bad: %v", err) 7018 } 7019 if dout.Status != status || dout.StatusDescription != desc { 7020 t.Fatalf("bad: %#v", dout) 7021 } 7022 7023 // Check that the evaluation was created 7024 eout, _ := state.EvalByID(ws, e.ID) 7025 if err != nil { 7026 t.Fatalf("bad: %v", err) 7027 } 7028 if eout == nil { 7029 t.Fatalf("bad: %#v", eout) 7030 } 7031 7032 // Check that the job was created 7033 jout, _ := state.JobByID(ws, j.Namespace, j.ID) 7034 if err != nil { 7035 t.Fatalf("bad: %v", err) 7036 } 7037 if jout == nil { 7038 t.Fatalf("bad: %#v", jout) 7039 } 7040 7041 // Check the status of the allocs 7042 out1, err := state.AllocByID(ws, a1.ID) 7043 if err != nil { 7044 t.Fatalf("err: %v", err) 7045 } 7046 out2, err := state.AllocByID(ws, a2.ID) 7047 if err != nil { 7048 t.Fatalf("err: %v", err) 7049 } 7050 7051 if !out1.DeploymentStatus.IsHealthy() { 7052 t.Fatalf("bad: alloc %q not healthy", out1.ID) 7053 } 7054 if !out2.DeploymentStatus.IsUnhealthy() { 7055 t.Fatalf("bad: alloc %q not unhealthy", out2.ID) 7056 } 7057 7058 if !out1.DeploymentStatus.Timestamp.Equal(ts) { 7059 t.Fatalf("bad: alloc %q had timestamp %v; want %v", out1.ID, out1.DeploymentStatus.Timestamp, ts) 7060 } 7061 if !out2.DeploymentStatus.Timestamp.Equal(ts) { 7062 t.Fatalf("bad: alloc %q had timestamp %v; want %v", out2.ID, out2.DeploymentStatus.Timestamp, ts) 7063 } 7064 } 7065 7066 func TestStateStore_UpsertVaultAccessors(t *testing.T) { 7067 t.Parallel() 7068 7069 state := testStateStore(t) 7070 a := mock.VaultAccessor() 7071 a2 := mock.VaultAccessor() 7072 7073 ws := memdb.NewWatchSet() 7074 if _, err := state.VaultAccessor(ws, a.Accessor); err != nil { 7075 t.Fatalf("err: %v", err) 7076 } 7077 7078 if _, err := state.VaultAccessor(ws, a2.Accessor); err != nil { 7079 t.Fatalf("err: %v", err) 7080 } 7081 7082 err := state.UpsertVaultAccessor(1000, []*structs.VaultAccessor{a, a2}) 7083 if err != nil { 7084 t.Fatalf("err: %v", err) 7085 } 7086 7087 if !watchFired(ws) { 7088 t.Fatalf("bad") 7089 } 7090 7091 ws = memdb.NewWatchSet() 7092 out, err := state.VaultAccessor(ws, a.Accessor) 7093 if err != nil { 7094 t.Fatalf("err: %v", err) 7095 } 7096 7097 if !reflect.DeepEqual(a, out) { 7098 t.Fatalf("bad: %#v %#v", a, out) 7099 } 7100 7101 out, err = state.VaultAccessor(ws, a2.Accessor) 7102 if err != nil { 7103 t.Fatalf("err: %v", err) 7104 } 7105 7106 if !reflect.DeepEqual(a2, out) { 7107 t.Fatalf("bad: %#v %#v", a2, out) 7108 } 7109 7110 iter, err := state.VaultAccessors(ws) 7111 if err != nil { 7112 t.Fatalf("err: %v", err) 7113 } 7114 7115 count := 0 7116 for { 7117 raw := iter.Next() 7118 if raw == nil { 7119 break 7120 } 7121 7122 count++ 7123 accessor := raw.(*structs.VaultAccessor) 7124 7125 if !reflect.DeepEqual(accessor, a) && !reflect.DeepEqual(accessor, a2) { 7126 t.Fatalf("bad: %#v", accessor) 7127 } 7128 } 7129 7130 if count != 2 { 7131 t.Fatalf("bad: %d", count) 7132 } 7133 7134 index, err := state.Index("vault_accessors") 7135 if err != nil { 7136 t.Fatalf("err: %v", err) 7137 } 7138 if index != 1000 { 7139 t.Fatalf("bad: %d", index) 7140 } 7141 7142 if watchFired(ws) { 7143 t.Fatalf("bad") 7144 } 7145 } 7146 7147 func TestStateStore_DeleteVaultAccessors(t *testing.T) { 7148 t.Parallel() 7149 7150 state := testStateStore(t) 7151 a1 := mock.VaultAccessor() 7152 a2 := mock.VaultAccessor() 7153 accessors := []*structs.VaultAccessor{a1, a2} 7154 7155 err := state.UpsertVaultAccessor(1000, accessors) 7156 if err != nil { 7157 t.Fatalf("err: %v", err) 7158 } 7159 7160 ws := memdb.NewWatchSet() 7161 if _, err := state.VaultAccessor(ws, a1.Accessor); err != nil { 7162 t.Fatalf("err: %v", err) 7163 } 7164 7165 err = state.DeleteVaultAccessors(1001, accessors) 7166 if err != nil { 7167 t.Fatalf("err: %v", err) 7168 } 7169 7170 if !watchFired(ws) { 7171 t.Fatalf("bad") 7172 } 7173 7174 ws = memdb.NewWatchSet() 7175 out, err := state.VaultAccessor(ws, a1.Accessor) 7176 if err != nil { 7177 t.Fatalf("err: %v", err) 7178 } 7179 if out != nil { 7180 t.Fatalf("bad: %#v %#v", a1, out) 7181 } 7182 out, err = state.VaultAccessor(ws, a2.Accessor) 7183 if err != nil { 7184 t.Fatalf("err: %v", err) 7185 } 7186 if out != nil { 7187 t.Fatalf("bad: %#v %#v", a2, out) 7188 } 7189 7190 index, err := state.Index("vault_accessors") 7191 if err != nil { 7192 t.Fatalf("err: %v", err) 7193 } 7194 if index != 1001 { 7195 t.Fatalf("bad: %d", index) 7196 } 7197 7198 if watchFired(ws) { 7199 t.Fatalf("bad") 7200 } 7201 } 7202 7203 func TestStateStore_VaultAccessorsByAlloc(t *testing.T) { 7204 t.Parallel() 7205 7206 state := testStateStore(t) 7207 alloc := mock.Alloc() 7208 var accessors []*structs.VaultAccessor 7209 var expected []*structs.VaultAccessor 7210 7211 for i := 0; i < 5; i++ { 7212 accessor := mock.VaultAccessor() 7213 accessor.AllocID = alloc.ID 7214 expected = append(expected, accessor) 7215 accessors = append(accessors, accessor) 7216 } 7217 7218 for i := 0; i < 10; i++ { 7219 accessor := mock.VaultAccessor() 7220 accessors = append(accessors, accessor) 7221 } 7222 7223 err := state.UpsertVaultAccessor(1000, accessors) 7224 if err != nil { 7225 t.Fatalf("err: %v", err) 7226 } 7227 7228 ws := memdb.NewWatchSet() 7229 out, err := state.VaultAccessorsByAlloc(ws, alloc.ID) 7230 if err != nil { 7231 t.Fatalf("err: %v", err) 7232 } 7233 7234 if len(expected) != len(out) { 7235 t.Fatalf("bad: %#v %#v", len(expected), len(out)) 7236 } 7237 7238 index, err := state.Index("vault_accessors") 7239 if err != nil { 7240 t.Fatalf("err: %v", err) 7241 } 7242 if index != 1000 { 7243 t.Fatalf("bad: %d", index) 7244 } 7245 7246 if watchFired(ws) { 7247 t.Fatalf("bad") 7248 } 7249 } 7250 7251 func TestStateStore_VaultAccessorsByNode(t *testing.T) { 7252 t.Parallel() 7253 7254 state := testStateStore(t) 7255 node := mock.Node() 7256 var accessors []*structs.VaultAccessor 7257 var expected []*structs.VaultAccessor 7258 7259 for i := 0; i < 5; i++ { 7260 accessor := mock.VaultAccessor() 7261 accessor.NodeID = node.ID 7262 expected = append(expected, accessor) 7263 accessors = append(accessors, accessor) 7264 } 7265 7266 for i := 0; i < 10; i++ { 7267 accessor := mock.VaultAccessor() 7268 accessors = append(accessors, accessor) 7269 } 7270 7271 err := state.UpsertVaultAccessor(1000, accessors) 7272 if err != nil { 7273 t.Fatalf("err: %v", err) 7274 } 7275 7276 ws := memdb.NewWatchSet() 7277 out, err := state.VaultAccessorsByNode(ws, node.ID) 7278 if err != nil { 7279 t.Fatalf("err: %v", err) 7280 } 7281 7282 if len(expected) != len(out) { 7283 t.Fatalf("bad: %#v %#v", len(expected), len(out)) 7284 } 7285 7286 index, err := state.Index("vault_accessors") 7287 if err != nil { 7288 t.Fatalf("err: %v", err) 7289 } 7290 if index != 1000 { 7291 t.Fatalf("bad: %d", index) 7292 } 7293 7294 if watchFired(ws) { 7295 t.Fatalf("bad") 7296 } 7297 } 7298 7299 func TestStateStore_RestoreVaultAccessor(t *testing.T) { 7300 t.Parallel() 7301 7302 state := testStateStore(t) 7303 a := mock.VaultAccessor() 7304 7305 restore, err := state.Restore() 7306 if err != nil { 7307 t.Fatalf("err: %v", err) 7308 } 7309 7310 err = restore.VaultAccessorRestore(a) 7311 if err != nil { 7312 t.Fatalf("err: %v", err) 7313 } 7314 restore.Commit() 7315 7316 ws := memdb.NewWatchSet() 7317 out, err := state.VaultAccessor(ws, a.Accessor) 7318 if err != nil { 7319 t.Fatalf("err: %v", err) 7320 } 7321 7322 if !reflect.DeepEqual(out, a) { 7323 t.Fatalf("Bad: %#v %#v", out, a) 7324 } 7325 7326 if watchFired(ws) { 7327 t.Fatalf("bad") 7328 } 7329 } 7330 7331 func TestStateStore_UpsertSITokenAccessors(t *testing.T) { 7332 t.Parallel() 7333 r := require.New(t) 7334 7335 state := testStateStore(t) 7336 a1 := mock.SITokenAccessor() 7337 a2 := mock.SITokenAccessor() 7338 7339 ws := memdb.NewWatchSet() 7340 var err error 7341 7342 _, err = state.SITokenAccessor(ws, a1.AccessorID) 7343 r.NoError(err) 7344 7345 _, err = state.SITokenAccessor(ws, a2.AccessorID) 7346 r.NoError(err) 7347 7348 err = state.UpsertSITokenAccessors(1000, []*structs.SITokenAccessor{a1, a2}) 7349 r.NoError(err) 7350 7351 wsFired := watchFired(ws) 7352 r.True(wsFired) 7353 7354 noInsertWS := memdb.NewWatchSet() 7355 result1, err := state.SITokenAccessor(noInsertWS, a1.AccessorID) 7356 r.NoError(err) 7357 r.Equal(a1, result1) 7358 7359 result2, err := state.SITokenAccessor(noInsertWS, a2.AccessorID) 7360 r.NoError(err) 7361 r.Equal(a2, result2) 7362 7363 iter, err := state.SITokenAccessors(noInsertWS) 7364 r.NoError(err) 7365 7366 count := 0 7367 for raw := iter.Next(); raw != nil; raw = iter.Next() { 7368 count++ 7369 accessor := raw.(*structs.SITokenAccessor) 7370 // iterator is sorted by dynamic UUID 7371 matches := reflect.DeepEqual(a1, accessor) || reflect.DeepEqual(a2, accessor) 7372 r.True(matches) 7373 } 7374 r.Equal(2, count) 7375 7376 index, err := state.Index(siTokenAccessorTable) 7377 r.NoError(err) 7378 r.Equal(uint64(1000), index) 7379 7380 noInsertWSFired := watchFired(noInsertWS) 7381 r.False(noInsertWSFired) 7382 } 7383 7384 func TestStateStore_DeleteSITokenAccessors(t *testing.T) { 7385 t.Parallel() 7386 r := require.New(t) 7387 7388 state := testStateStore(t) 7389 a1 := mock.SITokenAccessor() 7390 a2 := mock.SITokenAccessor() 7391 accessors := []*structs.SITokenAccessor{a1, a2} 7392 var err error 7393 7394 err = state.UpsertSITokenAccessors(1000, accessors) 7395 r.NoError(err) 7396 7397 ws := memdb.NewWatchSet() 7398 _, err = state.SITokenAccessor(ws, a1.AccessorID) 7399 r.NoError(err) 7400 7401 err = state.DeleteSITokenAccessors(1001, accessors) 7402 r.NoError(err) 7403 7404 wsFired := watchFired(ws) 7405 r.True(wsFired) 7406 7407 wsPostDelete := memdb.NewWatchSet() 7408 7409 result1, err := state.SITokenAccessor(wsPostDelete, a1.AccessorID) 7410 r.NoError(err) 7411 r.Nil(result1) // was deleted 7412 7413 result2, err := state.SITokenAccessor(wsPostDelete, a2.AccessorID) 7414 r.NoError(err) 7415 r.Nil(result2) // was deleted 7416 7417 index, err := state.Index(siTokenAccessorTable) 7418 r.NoError(err) 7419 r.Equal(uint64(1001), index) 7420 7421 wsPostDeleteFired := watchFired(wsPostDelete) 7422 r.False(wsPostDeleteFired) 7423 } 7424 7425 func TestStateStore_SITokenAccessorsByAlloc(t *testing.T) { 7426 t.Parallel() 7427 r := require.New(t) 7428 7429 state := testStateStore(t) 7430 alloc := mock.Alloc() 7431 var accessors []*structs.SITokenAccessor 7432 var expected []*structs.SITokenAccessor 7433 7434 for i := 0; i < 5; i++ { 7435 accessor := mock.SITokenAccessor() 7436 accessor.AllocID = alloc.ID 7437 expected = append(expected, accessor) 7438 accessors = append(accessors, accessor) 7439 } 7440 7441 for i := 0; i < 10; i++ { 7442 accessor := mock.SITokenAccessor() 7443 accessor.AllocID = uuid.Generate() // does not belong to alloc 7444 accessors = append(accessors, accessor) 7445 } 7446 7447 err := state.UpsertSITokenAccessors(1000, accessors) 7448 r.NoError(err) 7449 7450 ws := memdb.NewWatchSet() 7451 result, err := state.SITokenAccessorsByAlloc(ws, alloc.ID) 7452 r.NoError(err) 7453 r.ElementsMatch(expected, result) 7454 7455 index, err := state.Index(siTokenAccessorTable) 7456 r.NoError(err) 7457 r.Equal(uint64(1000), index) 7458 7459 wsFired := watchFired(ws) 7460 r.False(wsFired) 7461 } 7462 7463 func TestStateStore_SITokenAccessorsByNode(t *testing.T) { 7464 t.Parallel() 7465 r := require.New(t) 7466 7467 state := testStateStore(t) 7468 node := mock.Node() 7469 var accessors []*structs.SITokenAccessor 7470 var expected []*structs.SITokenAccessor 7471 var err error 7472 7473 for i := 0; i < 5; i++ { 7474 accessor := mock.SITokenAccessor() 7475 accessor.NodeID = node.ID 7476 expected = append(expected, accessor) 7477 accessors = append(accessors, accessor) 7478 } 7479 7480 for i := 0; i < 10; i++ { 7481 accessor := mock.SITokenAccessor() 7482 accessor.NodeID = uuid.Generate() // does not belong to node 7483 accessors = append(accessors, accessor) 7484 } 7485 7486 err = state.UpsertSITokenAccessors(1000, accessors) 7487 r.NoError(err) 7488 7489 ws := memdb.NewWatchSet() 7490 result, err := state.SITokenAccessorsByNode(ws, node.ID) 7491 r.NoError(err) 7492 r.ElementsMatch(expected, result) 7493 7494 index, err := state.Index(siTokenAccessorTable) 7495 r.NoError(err) 7496 r.Equal(uint64(1000), index) 7497 7498 wsFired := watchFired(ws) 7499 r.False(wsFired) 7500 } 7501 7502 func TestStateStore_RestoreSITokenAccessor(t *testing.T) { 7503 t.Parallel() 7504 r := require.New(t) 7505 7506 state := testStateStore(t) 7507 a1 := mock.SITokenAccessor() 7508 7509 restore, err := state.Restore() 7510 r.NoError(err) 7511 7512 err = restore.SITokenAccessorRestore(a1) 7513 r.NoError(err) 7514 7515 restore.Commit() 7516 7517 ws := memdb.NewWatchSet() 7518 result, err := state.SITokenAccessor(ws, a1.AccessorID) 7519 r.NoError(err) 7520 r.Equal(a1, result) 7521 7522 wsFired := watchFired(ws) 7523 r.False(wsFired) 7524 } 7525 7526 func TestStateStore_UpsertACLPolicy(t *testing.T) { 7527 t.Parallel() 7528 7529 state := testStateStore(t) 7530 policy := mock.ACLPolicy() 7531 policy2 := mock.ACLPolicy() 7532 7533 ws := memdb.NewWatchSet() 7534 if _, err := state.ACLPolicyByName(ws, policy.Name); err != nil { 7535 t.Fatalf("err: %v", err) 7536 } 7537 if _, err := state.ACLPolicyByName(ws, policy2.Name); err != nil { 7538 t.Fatalf("err: %v", err) 7539 } 7540 7541 if err := state.UpsertACLPolicies(1000, 7542 []*structs.ACLPolicy{policy, policy2}); err != nil { 7543 t.Fatalf("err: %v", err) 7544 } 7545 if !watchFired(ws) { 7546 t.Fatalf("bad") 7547 } 7548 7549 ws = memdb.NewWatchSet() 7550 out, err := state.ACLPolicyByName(ws, policy.Name) 7551 assert.Equal(t, nil, err) 7552 assert.Equal(t, policy, out) 7553 7554 out, err = state.ACLPolicyByName(ws, policy2.Name) 7555 assert.Equal(t, nil, err) 7556 assert.Equal(t, policy2, out) 7557 7558 iter, err := state.ACLPolicies(ws) 7559 if err != nil { 7560 t.Fatalf("err: %v", err) 7561 } 7562 7563 // Ensure we see both policies 7564 count := 0 7565 for { 7566 raw := iter.Next() 7567 if raw == nil { 7568 break 7569 } 7570 count++ 7571 } 7572 if count != 2 { 7573 t.Fatalf("bad: %d", count) 7574 } 7575 7576 index, err := state.Index("acl_policy") 7577 if err != nil { 7578 t.Fatalf("err: %v", err) 7579 } 7580 if index != 1000 { 7581 t.Fatalf("bad: %d", index) 7582 } 7583 7584 if watchFired(ws) { 7585 t.Fatalf("bad") 7586 } 7587 } 7588 7589 func TestStateStore_DeleteACLPolicy(t *testing.T) { 7590 t.Parallel() 7591 7592 state := testStateStore(t) 7593 policy := mock.ACLPolicy() 7594 policy2 := mock.ACLPolicy() 7595 7596 // Create the policy 7597 if err := state.UpsertACLPolicies(1000, 7598 []*structs.ACLPolicy{policy, policy2}); err != nil { 7599 t.Fatalf("err: %v", err) 7600 } 7601 7602 // Create a watcher 7603 ws := memdb.NewWatchSet() 7604 if _, err := state.ACLPolicyByName(ws, policy.Name); err != nil { 7605 t.Fatalf("err: %v", err) 7606 } 7607 7608 // Delete the policy 7609 if err := state.DeleteACLPolicies(1001, 7610 []string{policy.Name, policy2.Name}); err != nil { 7611 t.Fatalf("err: %v", err) 7612 } 7613 7614 // Ensure watching triggered 7615 if !watchFired(ws) { 7616 t.Fatalf("bad") 7617 } 7618 7619 // Ensure we don't get the object back 7620 ws = memdb.NewWatchSet() 7621 out, err := state.ACLPolicyByName(ws, policy.Name) 7622 assert.Equal(t, nil, err) 7623 if out != nil { 7624 t.Fatalf("bad: %#v", out) 7625 } 7626 7627 iter, err := state.ACLPolicies(ws) 7628 if err != nil { 7629 t.Fatalf("err: %v", err) 7630 } 7631 7632 // Ensure we see neither policy 7633 count := 0 7634 for { 7635 raw := iter.Next() 7636 if raw == nil { 7637 break 7638 } 7639 count++ 7640 } 7641 if count != 0 { 7642 t.Fatalf("bad: %d", count) 7643 } 7644 7645 index, err := state.Index("acl_policy") 7646 if err != nil { 7647 t.Fatalf("err: %v", err) 7648 } 7649 if index != 1001 { 7650 t.Fatalf("bad: %d", index) 7651 } 7652 7653 if watchFired(ws) { 7654 t.Fatalf("bad") 7655 } 7656 } 7657 7658 func TestStateStore_ACLPolicyByNamePrefix(t *testing.T) { 7659 t.Parallel() 7660 7661 state := testStateStore(t) 7662 names := []string{ 7663 "foo", 7664 "bar", 7665 "foobar", 7666 "foozip", 7667 "zip", 7668 } 7669 7670 // Create the policies 7671 var baseIndex uint64 = 1000 7672 for _, name := range names { 7673 p := mock.ACLPolicy() 7674 p.Name = name 7675 if err := state.UpsertACLPolicies(baseIndex, []*structs.ACLPolicy{p}); err != nil { 7676 t.Fatalf("err: %v", err) 7677 } 7678 baseIndex++ 7679 } 7680 7681 // Scan by prefix 7682 iter, err := state.ACLPolicyByNamePrefix(nil, "foo") 7683 if err != nil { 7684 t.Fatalf("err: %v", err) 7685 } 7686 7687 // Ensure we see both policies 7688 count := 0 7689 out := []string{} 7690 for { 7691 raw := iter.Next() 7692 if raw == nil { 7693 break 7694 } 7695 count++ 7696 out = append(out, raw.(*structs.ACLPolicy).Name) 7697 } 7698 if count != 3 { 7699 t.Fatalf("bad: %d %v", count, out) 7700 } 7701 sort.Strings(out) 7702 7703 expect := []string{"foo", "foobar", "foozip"} 7704 assert.Equal(t, expect, out) 7705 } 7706 7707 func TestStateStore_BootstrapACLTokens(t *testing.T) { 7708 t.Parallel() 7709 7710 state := testStateStore(t) 7711 tk1 := mock.ACLToken() 7712 tk2 := mock.ACLToken() 7713 7714 ok, resetIdx, err := state.CanBootstrapACLToken() 7715 assert.Nil(t, err) 7716 assert.Equal(t, true, ok) 7717 assert.EqualValues(t, 0, resetIdx) 7718 7719 if err := state.BootstrapACLTokens(1000, 0, tk1); err != nil { 7720 t.Fatalf("err: %v", err) 7721 } 7722 7723 out, err := state.ACLTokenByAccessorID(nil, tk1.AccessorID) 7724 assert.Equal(t, nil, err) 7725 assert.Equal(t, tk1, out) 7726 7727 ok, resetIdx, err = state.CanBootstrapACLToken() 7728 assert.Nil(t, err) 7729 assert.Equal(t, false, ok) 7730 assert.EqualValues(t, 1000, resetIdx) 7731 7732 if err := state.BootstrapACLTokens(1001, 0, tk2); err == nil { 7733 t.Fatalf("expected error") 7734 } 7735 7736 iter, err := state.ACLTokens(nil) 7737 if err != nil { 7738 t.Fatalf("err: %v", err) 7739 } 7740 7741 // Ensure we see both policies 7742 count := 0 7743 for { 7744 raw := iter.Next() 7745 if raw == nil { 7746 break 7747 } 7748 count++ 7749 } 7750 if count != 1 { 7751 t.Fatalf("bad: %d", count) 7752 } 7753 7754 index, err := state.Index("acl_token") 7755 if err != nil { 7756 t.Fatalf("err: %v", err) 7757 } 7758 if index != 1000 { 7759 t.Fatalf("bad: %d", index) 7760 } 7761 index, err = state.Index("acl_token_bootstrap") 7762 if err != nil { 7763 t.Fatalf("err: %v", err) 7764 } 7765 if index != 1000 { 7766 t.Fatalf("bad: %d", index) 7767 } 7768 7769 // Should allow bootstrap with reset index 7770 if err := state.BootstrapACLTokens(1001, 1000, tk2); err != nil { 7771 t.Fatalf("err %v", err) 7772 } 7773 7774 // Check we've modified the index 7775 index, err = state.Index("acl_token") 7776 if err != nil { 7777 t.Fatalf("err: %v", err) 7778 } 7779 if index != 1001 { 7780 t.Fatalf("bad: %d", index) 7781 } 7782 index, err = state.Index("acl_token_bootstrap") 7783 if err != nil { 7784 t.Fatalf("err: %v", err) 7785 } 7786 if index != 1001 { 7787 t.Fatalf("bad: %d", index) 7788 } 7789 } 7790 7791 func TestStateStore_UpsertACLTokens(t *testing.T) { 7792 t.Parallel() 7793 7794 state := testStateStore(t) 7795 tk1 := mock.ACLToken() 7796 tk2 := mock.ACLToken() 7797 7798 ws := memdb.NewWatchSet() 7799 if _, err := state.ACLTokenByAccessorID(ws, tk1.AccessorID); err != nil { 7800 t.Fatalf("err: %v", err) 7801 } 7802 if _, err := state.ACLTokenByAccessorID(ws, tk2.AccessorID); err != nil { 7803 t.Fatalf("err: %v", err) 7804 } 7805 7806 if err := state.UpsertACLTokens(1000, 7807 []*structs.ACLToken{tk1, tk2}); err != nil { 7808 t.Fatalf("err: %v", err) 7809 } 7810 if !watchFired(ws) { 7811 t.Fatalf("bad") 7812 } 7813 7814 ws = memdb.NewWatchSet() 7815 out, err := state.ACLTokenByAccessorID(ws, tk1.AccessorID) 7816 assert.Equal(t, nil, err) 7817 assert.Equal(t, tk1, out) 7818 7819 out, err = state.ACLTokenByAccessorID(ws, tk2.AccessorID) 7820 assert.Equal(t, nil, err) 7821 assert.Equal(t, tk2, out) 7822 7823 out, err = state.ACLTokenBySecretID(ws, tk1.SecretID) 7824 assert.Equal(t, nil, err) 7825 assert.Equal(t, tk1, out) 7826 7827 out, err = state.ACLTokenBySecretID(ws, tk2.SecretID) 7828 assert.Equal(t, nil, err) 7829 assert.Equal(t, tk2, out) 7830 7831 iter, err := state.ACLTokens(ws) 7832 if err != nil { 7833 t.Fatalf("err: %v", err) 7834 } 7835 7836 // Ensure we see both policies 7837 count := 0 7838 for { 7839 raw := iter.Next() 7840 if raw == nil { 7841 break 7842 } 7843 count++ 7844 } 7845 if count != 2 { 7846 t.Fatalf("bad: %d", count) 7847 } 7848 7849 index, err := state.Index("acl_token") 7850 if err != nil { 7851 t.Fatalf("err: %v", err) 7852 } 7853 if index != 1000 { 7854 t.Fatalf("bad: %d", index) 7855 } 7856 7857 if watchFired(ws) { 7858 t.Fatalf("bad") 7859 } 7860 } 7861 7862 func TestStateStore_DeleteACLTokens(t *testing.T) { 7863 t.Parallel() 7864 7865 state := testStateStore(t) 7866 tk1 := mock.ACLToken() 7867 tk2 := mock.ACLToken() 7868 7869 // Create the tokens 7870 if err := state.UpsertACLTokens(1000, 7871 []*structs.ACLToken{tk1, tk2}); err != nil { 7872 t.Fatalf("err: %v", err) 7873 } 7874 7875 // Create a watcher 7876 ws := memdb.NewWatchSet() 7877 if _, err := state.ACLTokenByAccessorID(ws, tk1.AccessorID); err != nil { 7878 t.Fatalf("err: %v", err) 7879 } 7880 7881 // Delete the token 7882 if err := state.DeleteACLTokens(1001, 7883 []string{tk1.AccessorID, tk2.AccessorID}); err != nil { 7884 t.Fatalf("err: %v", err) 7885 } 7886 7887 // Ensure watching triggered 7888 if !watchFired(ws) { 7889 t.Fatalf("bad") 7890 } 7891 7892 // Ensure we don't get the object back 7893 ws = memdb.NewWatchSet() 7894 out, err := state.ACLTokenByAccessorID(ws, tk1.AccessorID) 7895 assert.Equal(t, nil, err) 7896 if out != nil { 7897 t.Fatalf("bad: %#v", out) 7898 } 7899 7900 iter, err := state.ACLTokens(ws) 7901 if err != nil { 7902 t.Fatalf("err: %v", err) 7903 } 7904 7905 // Ensure we see both policies 7906 count := 0 7907 for { 7908 raw := iter.Next() 7909 if raw == nil { 7910 break 7911 } 7912 count++ 7913 } 7914 if count != 0 { 7915 t.Fatalf("bad: %d", count) 7916 } 7917 7918 index, err := state.Index("acl_token") 7919 if err != nil { 7920 t.Fatalf("err: %v", err) 7921 } 7922 if index != 1001 { 7923 t.Fatalf("bad: %d", index) 7924 } 7925 7926 if watchFired(ws) { 7927 t.Fatalf("bad") 7928 } 7929 } 7930 7931 func TestStateStore_ACLTokenByAccessorIDPrefix(t *testing.T) { 7932 t.Parallel() 7933 7934 state := testStateStore(t) 7935 prefixes := []string{ 7936 "aaaa", 7937 "aabb", 7938 "bbbb", 7939 "bbcc", 7940 "ffff", 7941 } 7942 7943 // Create the tokens 7944 var baseIndex uint64 = 1000 7945 for _, prefix := range prefixes { 7946 tk := mock.ACLToken() 7947 tk.AccessorID = prefix + tk.AccessorID[4:] 7948 if err := state.UpsertACLTokens(baseIndex, []*structs.ACLToken{tk}); err != nil { 7949 t.Fatalf("err: %v", err) 7950 } 7951 baseIndex++ 7952 } 7953 7954 // Scan by prefix 7955 iter, err := state.ACLTokenByAccessorIDPrefix(nil, "aa") 7956 if err != nil { 7957 t.Fatalf("err: %v", err) 7958 } 7959 7960 // Ensure we see both tokens 7961 count := 0 7962 out := []string{} 7963 for { 7964 raw := iter.Next() 7965 if raw == nil { 7966 break 7967 } 7968 count++ 7969 out = append(out, raw.(*structs.ACLToken).AccessorID[:4]) 7970 } 7971 if count != 2 { 7972 t.Fatalf("bad: %d %v", count, out) 7973 } 7974 sort.Strings(out) 7975 7976 expect := []string{"aaaa", "aabb"} 7977 assert.Equal(t, expect, out) 7978 } 7979 7980 func TestStateStore_RestoreACLPolicy(t *testing.T) { 7981 t.Parallel() 7982 7983 state := testStateStore(t) 7984 policy := mock.ACLPolicy() 7985 7986 restore, err := state.Restore() 7987 if err != nil { 7988 t.Fatalf("err: %v", err) 7989 } 7990 7991 err = restore.ACLPolicyRestore(policy) 7992 if err != nil { 7993 t.Fatalf("err: %v", err) 7994 } 7995 restore.Commit() 7996 7997 ws := memdb.NewWatchSet() 7998 out, err := state.ACLPolicyByName(ws, policy.Name) 7999 if err != nil { 8000 t.Fatalf("err: %v", err) 8001 } 8002 assert.Equal(t, policy, out) 8003 } 8004 8005 func TestStateStore_ACLTokensByGlobal(t *testing.T) { 8006 t.Parallel() 8007 8008 state := testStateStore(t) 8009 tk1 := mock.ACLToken() 8010 tk2 := mock.ACLToken() 8011 tk3 := mock.ACLToken() 8012 tk4 := mock.ACLToken() 8013 tk3.Global = true 8014 8015 if err := state.UpsertACLTokens(1000, 8016 []*structs.ACLToken{tk1, tk2, tk3, tk4}); err != nil { 8017 t.Fatalf("err: %v", err) 8018 } 8019 8020 iter, err := state.ACLTokensByGlobal(nil, true) 8021 if err != nil { 8022 t.Fatalf("err: %v", err) 8023 } 8024 8025 // Ensure we see the one global policies 8026 count := 0 8027 for { 8028 raw := iter.Next() 8029 if raw == nil { 8030 break 8031 } 8032 count++ 8033 } 8034 if count != 1 { 8035 t.Fatalf("bad: %d", count) 8036 } 8037 } 8038 8039 func TestStateStore_RestoreACLToken(t *testing.T) { 8040 t.Parallel() 8041 8042 state := testStateStore(t) 8043 token := mock.ACLToken() 8044 8045 restore, err := state.Restore() 8046 if err != nil { 8047 t.Fatalf("err: %v", err) 8048 } 8049 8050 err = restore.ACLTokenRestore(token) 8051 if err != nil { 8052 t.Fatalf("err: %v", err) 8053 } 8054 restore.Commit() 8055 8056 ws := memdb.NewWatchSet() 8057 out, err := state.ACLTokenByAccessorID(ws, token.AccessorID) 8058 if err != nil { 8059 t.Fatalf("err: %v", err) 8060 } 8061 assert.Equal(t, token, out) 8062 } 8063 8064 func TestStateStore_SchedulerConfig(t *testing.T) { 8065 t.Parallel() 8066 8067 state := testStateStore(t) 8068 schedConfig := &structs.SchedulerConfiguration{ 8069 PreemptionConfig: structs.PreemptionConfig{ 8070 SystemSchedulerEnabled: false, 8071 }, 8072 CreateIndex: 100, 8073 ModifyIndex: 200, 8074 } 8075 8076 require := require.New(t) 8077 restore, err := state.Restore() 8078 require.Nil(err) 8079 8080 err = restore.SchedulerConfigRestore(schedConfig) 8081 require.Nil(err) 8082 8083 restore.Commit() 8084 8085 modIndex, out, err := state.SchedulerConfig() 8086 require.Nil(err) 8087 require.Equal(schedConfig.ModifyIndex, modIndex) 8088 8089 require.Equal(schedConfig, out) 8090 } 8091 8092 func TestStateStore_ClusterMetadata(t *testing.T) { 8093 require := require.New(t) 8094 8095 state := testStateStore(t) 8096 clusterID := "12345678-1234-1234-1234-1234567890" 8097 now := time.Now().UnixNano() 8098 meta := &structs.ClusterMetadata{ClusterID: clusterID, CreateTime: now} 8099 8100 err := state.ClusterSetMetadata(100, meta) 8101 require.NoError(err) 8102 8103 result, err := state.ClusterMetadata() 8104 require.NoError(err) 8105 require.Equal(clusterID, result.ClusterID) 8106 require.Equal(now, result.CreateTime) 8107 } 8108 8109 func TestStateStore_ClusterMetadataRestore(t *testing.T) { 8110 require := require.New(t) 8111 8112 state := testStateStore(t) 8113 clusterID := "12345678-1234-1234-1234-1234567890" 8114 now := time.Now().UnixNano() 8115 meta := &structs.ClusterMetadata{ClusterID: clusterID, CreateTime: now} 8116 8117 restore, err := state.Restore() 8118 require.NoError(err) 8119 8120 err = restore.ClusterMetadataRestore(meta) 8121 require.NoError(err) 8122 8123 restore.Commit() 8124 8125 out, err := state.ClusterMetadata() 8126 require.NoError(err) 8127 require.Equal(clusterID, out.ClusterID) 8128 require.Equal(now, out.CreateTime) 8129 } 8130 8131 func TestStateStore_RestoreScalingPolicy(t *testing.T) { 8132 t.Parallel() 8133 require := require.New(t) 8134 8135 state := testStateStore(t) 8136 scalingPolicy := mock.ScalingPolicy() 8137 8138 restore, err := state.Restore() 8139 require.NoError(err) 8140 8141 err = restore.ScalingPolicyRestore(scalingPolicy) 8142 require.NoError(err) 8143 restore.Commit() 8144 8145 ws := memdb.NewWatchSet() 8146 out, err := state.ScalingPolicyByID(ws, scalingPolicy.ID) 8147 require.NoError(err) 8148 require.EqualValues(out, scalingPolicy) 8149 } 8150 8151 func TestStateStore_UpsertScalingPolicy(t *testing.T) { 8152 t.Parallel() 8153 require := require.New(t) 8154 8155 state := testStateStore(t) 8156 policy := mock.ScalingPolicy() 8157 policy2 := mock.ScalingPolicy() 8158 8159 wsAll := memdb.NewWatchSet() 8160 all, err := state.ScalingPolicies(wsAll) 8161 require.NoError(err) 8162 require.Nil(all.Next()) 8163 8164 ws := memdb.NewWatchSet() 8165 out, err := state.ScalingPolicyByTarget(ws, policy.Target) 8166 require.NoError(err) 8167 require.Nil(out) 8168 8169 out, err = state.ScalingPolicyByTarget(ws, policy2.Target) 8170 require.NoError(err) 8171 require.Nil(out) 8172 8173 err = state.UpsertScalingPolicies(1000, []*structs.ScalingPolicy{policy, policy2}) 8174 require.NoError(err) 8175 require.True(watchFired(ws)) 8176 require.True(watchFired(wsAll)) 8177 8178 ws = memdb.NewWatchSet() 8179 out, err = state.ScalingPolicyByTarget(ws, policy.Target) 8180 require.NoError(err) 8181 require.Equal(policy, out) 8182 8183 out, err = state.ScalingPolicyByTarget(ws, policy2.Target) 8184 require.NoError(err) 8185 require.Equal(policy2, out) 8186 8187 iter, err := state.ScalingPolicies(ws) 8188 require.NoError(err) 8189 8190 // Ensure we see both policies 8191 count := 0 8192 for { 8193 raw := iter.Next() 8194 if raw == nil { 8195 break 8196 } 8197 count++ 8198 } 8199 require.Equal(2, count) 8200 8201 index, err := state.Index("scaling_policy") 8202 require.NoError(err) 8203 require.True(1000 == index) 8204 require.False(watchFired(ws)) 8205 } 8206 8207 func TestStateStore_UpsertScalingPolicy_Namespace(t *testing.T) { 8208 t.Parallel() 8209 require := require.New(t) 8210 8211 otherNamespace := "not-default-namespace" 8212 state := testStateStore(t) 8213 policy := mock.ScalingPolicy() 8214 policy2 := mock.ScalingPolicy() 8215 policy2.Target[structs.ScalingTargetNamespace] = otherNamespace 8216 8217 ws1 := memdb.NewWatchSet() 8218 iter, err := state.ScalingPoliciesByNamespace(ws1, structs.DefaultNamespace) 8219 require.NoError(err) 8220 require.Nil(iter.Next()) 8221 8222 ws2 := memdb.NewWatchSet() 8223 iter, err = state.ScalingPoliciesByNamespace(ws2, otherNamespace) 8224 require.NoError(err) 8225 require.Nil(iter.Next()) 8226 8227 err = state.UpsertScalingPolicies(1000, []*structs.ScalingPolicy{policy, policy2}) 8228 require.NoError(err) 8229 require.True(watchFired(ws1)) 8230 require.True(watchFired(ws2)) 8231 8232 iter, err = state.ScalingPoliciesByNamespace(nil, structs.DefaultNamespace) 8233 require.NoError(err) 8234 policiesInDefaultNamespace := []string{} 8235 for { 8236 raw := iter.Next() 8237 if raw == nil { 8238 break 8239 } 8240 policiesInDefaultNamespace = append(policiesInDefaultNamespace, raw.(*structs.ScalingPolicy).ID) 8241 } 8242 require.ElementsMatch([]string{policy.ID}, policiesInDefaultNamespace) 8243 8244 iter, err = state.ScalingPoliciesByNamespace(nil, otherNamespace) 8245 require.NoError(err) 8246 policiesInOtherNamespace := []string{} 8247 for { 8248 raw := iter.Next() 8249 if raw == nil { 8250 break 8251 } 8252 policiesInOtherNamespace = append(policiesInOtherNamespace, raw.(*structs.ScalingPolicy).ID) 8253 } 8254 require.ElementsMatch([]string{policy2.ID}, policiesInOtherNamespace) 8255 } 8256 8257 func TestStateStore_UpsertJob_UpsertScalingPolicies(t *testing.T) { 8258 t.Parallel() 8259 8260 require := require.New(t) 8261 8262 state := testStateStore(t) 8263 job, policy := mock.JobWithScalingPolicy() 8264 8265 // Create a watchset so we can test that upsert fires the watch 8266 ws := memdb.NewWatchSet() 8267 out, err := state.ScalingPolicyByTarget(ws, policy.Target) 8268 require.NoError(err) 8269 require.Nil(out) 8270 8271 var newIndex uint64 = 1000 8272 err = state.UpsertJob(newIndex, job) 8273 require.NoError(err) 8274 require.True(watchFired(ws), "watch did not fire") 8275 8276 ws = memdb.NewWatchSet() 8277 out, err = state.ScalingPolicyByTarget(ws, policy.Target) 8278 require.NoError(err) 8279 require.NotNil(out) 8280 require.Equal(newIndex, out.CreateIndex) 8281 require.Equal(newIndex, out.ModifyIndex) 8282 8283 index, err := state.Index("scaling_policy") 8284 require.Equal(newIndex, index) 8285 } 8286 8287 // Scaling Policy IDs are generated randomly during Job.Register 8288 // Subsequent updates of the job should preserve the ID for the scaling policy 8289 // associated with a given target. 8290 func TestStateStore_UpsertJob_PreserveScalingPolicyIDsAndIndex(t *testing.T) { 8291 t.Parallel() 8292 8293 require := require.New(t) 8294 8295 state := testStateStore(t) 8296 job, policy := mock.JobWithScalingPolicy() 8297 8298 var newIndex uint64 = 1000 8299 err := state.UpsertJob(newIndex, job) 8300 require.NoError(err) 8301 8302 ws := memdb.NewWatchSet() 8303 p1, err := state.ScalingPolicyByTarget(ws, policy.Target) 8304 require.NoError(err) 8305 require.NotNil(p1) 8306 require.Equal(newIndex, p1.CreateIndex) 8307 require.Equal(newIndex, p1.ModifyIndex) 8308 8309 index, err := state.Index("scaling_policy") 8310 require.Equal(newIndex, index) 8311 require.NotEmpty(p1.ID) 8312 8313 // update the job 8314 job.Meta["new-meta"] = "new-value" 8315 newIndex += 100 8316 err = state.UpsertJob(newIndex, job) 8317 require.NoError(err) 8318 require.False(watchFired(ws), "watch should not have fired") 8319 8320 p2, err := state.ScalingPolicyByTarget(nil, policy.Target) 8321 require.NoError(err) 8322 require.NotNil(p2) 8323 require.Equal(p1.ID, p2.ID, "ID should not have changed") 8324 require.Equal(p1.CreateIndex, p2.CreateIndex) 8325 require.Equal(p1.ModifyIndex, p2.ModifyIndex) 8326 8327 index, err = state.Index("scaling_policy") 8328 require.Equal(index, p1.CreateIndex, "table index should not have changed") 8329 } 8330 8331 // Updating the scaling policy for a job should update the index table and fire the watch. 8332 // This test is the converse of TestStateStore_UpsertJob_PreserveScalingPolicyIDsAndIndex 8333 func TestStateStore_UpsertJob_UpdateScalingPolicy(t *testing.T) { 8334 t.Parallel() 8335 8336 require := require.New(t) 8337 8338 state := testStateStore(t) 8339 job, policy := mock.JobWithScalingPolicy() 8340 8341 var oldIndex uint64 = 1000 8342 require.NoError(state.UpsertJob(oldIndex, job)) 8343 8344 ws := memdb.NewWatchSet() 8345 p1, err := state.ScalingPolicyByTarget(ws, policy.Target) 8346 require.NoError(err) 8347 require.NotNil(p1) 8348 require.Equal(oldIndex, p1.CreateIndex) 8349 require.Equal(oldIndex, p1.ModifyIndex) 8350 prevId := p1.ID 8351 8352 index, err := state.Index("scaling_policy") 8353 require.Equal(oldIndex, index) 8354 require.NotEmpty(p1.ID) 8355 8356 // update the job with the updated scaling policy; make sure to use a different object 8357 newPolicy := structs.CopyScalingPolicy(p1) 8358 newPolicy.Policy["new-field"] = "new-value" 8359 job.TaskGroups[0].Scaling = newPolicy 8360 require.NoError(state.UpsertJob(oldIndex+100, job)) 8361 require.True(watchFired(ws), "watch should have fired") 8362 8363 p2, err := state.ScalingPolicyByTarget(nil, policy.Target) 8364 require.NoError(err) 8365 require.NotNil(p2) 8366 require.Equal(p2.Policy["new-field"], "new-value") 8367 require.Equal(prevId, p2.ID, "ID should not have changed") 8368 require.Equal(oldIndex, p2.CreateIndex) 8369 require.Greater(p2.ModifyIndex, oldIndex, "ModifyIndex should have advanced") 8370 8371 index, err = state.Index("scaling_policy") 8372 require.Greater(index, oldIndex, "table index should have advanced") 8373 } 8374 8375 func TestStateStore_DeleteScalingPolicies(t *testing.T) { 8376 t.Parallel() 8377 8378 require := require.New(t) 8379 8380 state := testStateStore(t) 8381 policy := mock.ScalingPolicy() 8382 policy2 := mock.ScalingPolicy() 8383 8384 // Create the policy 8385 err := state.UpsertScalingPolicies(1000, []*structs.ScalingPolicy{policy, policy2}) 8386 require.NoError(err) 8387 8388 // Create a watcher 8389 ws := memdb.NewWatchSet() 8390 _, err = state.ScalingPolicyByTarget(ws, policy.Target) 8391 require.NoError(err) 8392 8393 // Delete the policy 8394 err = state.DeleteScalingPolicies(1001, []string{policy.ID, policy2.ID}) 8395 require.NoError(err) 8396 8397 // Ensure watching triggered 8398 require.True(watchFired(ws)) 8399 8400 // Ensure we don't get the objects back 8401 ws = memdb.NewWatchSet() 8402 out, err := state.ScalingPolicyByTarget(ws, policy.Target) 8403 require.NoError(err) 8404 require.Nil(out) 8405 8406 ws = memdb.NewWatchSet() 8407 out, err = state.ScalingPolicyByTarget(ws, policy2.Target) 8408 require.NoError(err) 8409 require.Nil(out) 8410 8411 // Ensure we see both policies 8412 iter, err := state.ScalingPoliciesByNamespace(ws, policy.Target[structs.ScalingTargetNamespace]) 8413 require.NoError(err) 8414 count := 0 8415 for { 8416 raw := iter.Next() 8417 if raw == nil { 8418 break 8419 } 8420 count++ 8421 } 8422 require.Equal(0, count) 8423 8424 index, err := state.Index("scaling_policy") 8425 require.NoError(err) 8426 require.True(1001 == index) 8427 require.False(watchFired(ws)) 8428 } 8429 8430 func TestStateStore_DeleteJob_ChildScalingPolicies(t *testing.T) { 8431 t.Parallel() 8432 8433 require := require.New(t) 8434 8435 state := testStateStore(t) 8436 8437 job := mock.Job() 8438 8439 err := state.UpsertJob(1000, job) 8440 require.NoError(err) 8441 8442 policy := mock.ScalingPolicy() 8443 policy.Target[structs.ScalingTargetJob] = job.ID 8444 err = state.UpsertScalingPolicies(1001, []*structs.ScalingPolicy{policy}) 8445 require.NoError(err) 8446 8447 // Delete the job 8448 err = state.DeleteJob(1002, job.Namespace, job.ID) 8449 require.NoError(err) 8450 8451 // Ensure the scaling policy was deleted 8452 ws := memdb.NewWatchSet() 8453 out, err := state.ScalingPolicyByTarget(ws, policy.Target) 8454 require.NoError(err) 8455 require.Nil(out) 8456 index, err := state.Index("scaling_policy") 8457 require.True(index > 1001) 8458 } 8459 8460 // This test ensures that deleting a job that doesn't have any scaling policies 8461 // will not cause the scaling_policy table index to increase, on either job 8462 // registration or deletion. 8463 func TestStateStore_DeleteJob_ScalingPolicyIndexNoop(t *testing.T) { 8464 t.Parallel() 8465 8466 require := require.New(t) 8467 8468 state := testStateStore(t) 8469 8470 job := mock.Job() 8471 8472 prevIndex, err := state.Index("scaling_policy") 8473 require.NoError(err) 8474 8475 err = state.UpsertJob(1000, job) 8476 require.NoError(err) 8477 8478 newIndex, err := state.Index("scaling_policy") 8479 require.NoError(err) 8480 require.Equal(prevIndex, newIndex) 8481 8482 // Delete the job 8483 err = state.DeleteJob(1002, job.Namespace, job.ID) 8484 require.NoError(err) 8485 8486 newIndex, err = state.Index("scaling_policy") 8487 require.NoError(err) 8488 require.Equal(prevIndex, newIndex) 8489 } 8490 8491 func TestStateStore_ScalingPoliciesByJob(t *testing.T) { 8492 t.Parallel() 8493 8494 require := require.New(t) 8495 8496 state := testStateStore(t) 8497 policyA := mock.ScalingPolicy() 8498 policyB1 := mock.ScalingPolicy() 8499 policyB2 := mock.ScalingPolicy() 8500 policyB1.Target[structs.ScalingTargetJob] = policyB2.Target[structs.ScalingTargetJob] 8501 8502 // Create the policies 8503 var baseIndex uint64 = 1000 8504 err := state.UpsertScalingPolicies(baseIndex, []*structs.ScalingPolicy{policyA, policyB1, policyB2}) 8505 require.NoError(err) 8506 8507 iter, err := state.ScalingPoliciesByJob(nil, 8508 policyA.Target[structs.ScalingTargetNamespace], 8509 policyA.Target[structs.ScalingTargetJob]) 8510 require.NoError(err) 8511 8512 // Ensure we see expected policies 8513 count := 0 8514 found := []string{} 8515 for { 8516 raw := iter.Next() 8517 if raw == nil { 8518 break 8519 } 8520 count++ 8521 found = append(found, raw.(*structs.ScalingPolicy).Target[structs.ScalingTargetGroup]) 8522 } 8523 require.Equal(1, count) 8524 sort.Strings(found) 8525 expect := []string{policyA.Target[structs.ScalingTargetGroup]} 8526 sort.Strings(expect) 8527 require.Equal(expect, found) 8528 8529 iter, err = state.ScalingPoliciesByJob(nil, 8530 policyB1.Target[structs.ScalingTargetNamespace], 8531 policyB1.Target[structs.ScalingTargetJob]) 8532 require.NoError(err) 8533 8534 // Ensure we see expected policies 8535 count = 0 8536 found = []string{} 8537 for { 8538 raw := iter.Next() 8539 if raw == nil { 8540 break 8541 } 8542 count++ 8543 found = append(found, raw.(*structs.ScalingPolicy).Target[structs.ScalingTargetGroup]) 8544 } 8545 require.Equal(2, count) 8546 sort.Strings(found) 8547 expect = []string{ 8548 policyB1.Target[structs.ScalingTargetGroup], 8549 policyB2.Target[structs.ScalingTargetGroup], 8550 } 8551 sort.Strings(expect) 8552 require.Equal(expect, found) 8553 } 8554 8555 func TestStateStore_UpsertScalingEvent(t *testing.T) { 8556 t.Parallel() 8557 require := require.New(t) 8558 8559 state := testStateStore(t) 8560 job := mock.Job() 8561 groupName := job.TaskGroups[0].Name 8562 8563 newEvent := structs.NewScalingEvent("message 1").SetMeta(map[string]interface{}{ 8564 "a": 1, 8565 }) 8566 8567 wsAll := memdb.NewWatchSet() 8568 all, err := state.ScalingEvents(wsAll) 8569 require.NoError(err) 8570 require.Nil(all.Next()) 8571 8572 ws := memdb.NewWatchSet() 8573 out, _, err := state.ScalingEventsByJob(ws, job.Namespace, job.ID) 8574 require.NoError(err) 8575 require.Nil(out) 8576 8577 err = state.UpsertScalingEvent(1000, &structs.ScalingEventRequest{ 8578 Namespace: job.Namespace, 8579 JobID: job.ID, 8580 TaskGroup: groupName, 8581 ScalingEvent: newEvent, 8582 }) 8583 require.NoError(err) 8584 require.True(watchFired(ws)) 8585 require.True(watchFired(wsAll)) 8586 8587 ws = memdb.NewWatchSet() 8588 out, eventsIndex, err := state.ScalingEventsByJob(ws, job.Namespace, job.ID) 8589 require.NoError(err) 8590 require.Equal(map[string][]*structs.ScalingEvent{ 8591 groupName: {newEvent}, 8592 }, out) 8593 require.EqualValues(eventsIndex, 1000) 8594 8595 iter, err := state.ScalingEvents(ws) 8596 require.NoError(err) 8597 8598 count := 0 8599 jobsReturned := []string{} 8600 var jobEvents *structs.JobScalingEvents 8601 for { 8602 raw := iter.Next() 8603 if raw == nil { 8604 break 8605 } 8606 jobEvents = raw.(*structs.JobScalingEvents) 8607 jobsReturned = append(jobsReturned, jobEvents.JobID) 8608 count++ 8609 } 8610 require.Equal(1, count) 8611 require.EqualValues(jobEvents.ModifyIndex, 1000) 8612 require.EqualValues(jobEvents.ScalingEvents[groupName][0].CreateIndex, 1000) 8613 8614 index, err := state.Index("scaling_event") 8615 require.NoError(err) 8616 require.ElementsMatch([]string{job.ID}, jobsReturned) 8617 require.Equal(map[string][]*structs.ScalingEvent{ 8618 groupName: {newEvent}, 8619 }, jobEvents.ScalingEvents) 8620 require.EqualValues(1000, index) 8621 require.False(watchFired(ws)) 8622 } 8623 8624 func TestStateStore_UpsertScalingEvent_LimitAndOrder(t *testing.T) { 8625 t.Parallel() 8626 require := require.New(t) 8627 8628 state := testStateStore(t) 8629 namespace := uuid.Generate() 8630 jobID := uuid.Generate() 8631 group1 := uuid.Generate() 8632 group2 := uuid.Generate() 8633 8634 index := uint64(1000) 8635 for i := 1; i <= structs.JobTrackedScalingEvents+10; i++ { 8636 newEvent := structs.NewScalingEvent("").SetMeta(map[string]interface{}{ 8637 "i": i, 8638 "group": group1, 8639 }) 8640 err := state.UpsertScalingEvent(index, &structs.ScalingEventRequest{ 8641 Namespace: namespace, 8642 JobID: jobID, 8643 TaskGroup: group1, 8644 ScalingEvent: newEvent, 8645 }) 8646 index++ 8647 require.NoError(err) 8648 8649 newEvent = structs.NewScalingEvent("").SetMeta(map[string]interface{}{ 8650 "i": i, 8651 "group": group2, 8652 }) 8653 err = state.UpsertScalingEvent(index, &structs.ScalingEventRequest{ 8654 Namespace: namespace, 8655 JobID: jobID, 8656 TaskGroup: group2, 8657 ScalingEvent: newEvent, 8658 }) 8659 index++ 8660 require.NoError(err) 8661 } 8662 8663 out, _, err := state.ScalingEventsByJob(nil, namespace, jobID) 8664 require.NoError(err) 8665 require.Len(out, 2) 8666 8667 expectedEvents := []int{} 8668 for i := structs.JobTrackedScalingEvents; i > 0; i-- { 8669 expectedEvents = append(expectedEvents, i+10) 8670 } 8671 8672 // checking order and content 8673 require.Len(out[group1], structs.JobTrackedScalingEvents) 8674 actualEvents := []int{} 8675 for _, event := range out[group1] { 8676 require.Equal(group1, event.Meta["group"]) 8677 actualEvents = append(actualEvents, event.Meta["i"].(int)) 8678 } 8679 require.Equal(expectedEvents, actualEvents) 8680 8681 // checking order and content 8682 require.Len(out[group2], structs.JobTrackedScalingEvents) 8683 actualEvents = []int{} 8684 for _, event := range out[group2] { 8685 require.Equal(group2, event.Meta["group"]) 8686 actualEvents = append(actualEvents, event.Meta["i"].(int)) 8687 } 8688 require.Equal(expectedEvents, actualEvents) 8689 } 8690 8691 func TestStateStore_RestoreScalingEvents(t *testing.T) { 8692 t.Parallel() 8693 require := require.New(t) 8694 8695 state := testStateStore(t) 8696 jobScalingEvents := &structs.JobScalingEvents{ 8697 Namespace: uuid.Generate(), 8698 JobID: uuid.Generate(), 8699 ScalingEvents: map[string][]*structs.ScalingEvent{ 8700 uuid.Generate(): { 8701 structs.NewScalingEvent(uuid.Generate()), 8702 }, 8703 }, 8704 } 8705 8706 restore, err := state.Restore() 8707 require.NoError(err) 8708 8709 err = restore.ScalingEventsRestore(jobScalingEvents) 8710 require.NoError(err) 8711 restore.Commit() 8712 8713 ws := memdb.NewWatchSet() 8714 out, _, err := state.ScalingEventsByJob(ws, jobScalingEvents.Namespace, 8715 jobScalingEvents.JobID) 8716 require.NoError(err) 8717 require.NotNil(out) 8718 require.EqualValues(jobScalingEvents.ScalingEvents, out) 8719 } 8720 8721 func TestStateStore_Abandon(t *testing.T) { 8722 t.Parallel() 8723 8724 s := testStateStore(t) 8725 abandonCh := s.AbandonCh() 8726 s.Abandon() 8727 select { 8728 case <-abandonCh: 8729 default: 8730 t.Fatalf("bad") 8731 } 8732 } 8733 8734 // Verifies that an error is returned when an allocation doesn't exist in the state store. 8735 func TestStateSnapshot_DenormalizeAllocationDiffSlice_AllocDoesNotExist(t *testing.T) { 8736 t.Parallel() 8737 8738 state := testStateStore(t) 8739 alloc := mock.Alloc() 8740 require := require.New(t) 8741 8742 // Insert job 8743 err := state.UpsertJob(999, alloc.Job) 8744 require.NoError(err) 8745 8746 allocDiffs := []*structs.AllocationDiff{ 8747 { 8748 ID: alloc.ID, 8749 }, 8750 } 8751 8752 snap, err := state.Snapshot() 8753 require.NoError(err) 8754 8755 denormalizedAllocs, err := snap.DenormalizeAllocationDiffSlice(allocDiffs) 8756 8757 require.EqualError(err, fmt.Sprintf("alloc %v doesn't exist", alloc.ID)) 8758 require.Nil(denormalizedAllocs) 8759 } 8760 8761 // TestStateStore_SnapshotMinIndex_OK asserts StateStore.SnapshotMinIndex blocks 8762 // until the StateStore's latest index is >= the requested index. 8763 func TestStateStore_SnapshotMinIndex_OK(t *testing.T) { 8764 t.Parallel() 8765 8766 s := testStateStore(t) 8767 index, err := s.LatestIndex() 8768 require.NoError(t, err) 8769 8770 node := mock.Node() 8771 require.NoError(t, s.UpsertNode(index+1, node)) 8772 8773 // Assert SnapshotMinIndex returns immediately if index < latest index 8774 ctx, cancel := context.WithTimeout(context.Background(), 0) 8775 snap, err := s.SnapshotMinIndex(ctx, index) 8776 cancel() 8777 require.NoError(t, err) 8778 8779 snapIndex, err := snap.LatestIndex() 8780 require.NoError(t, err) 8781 if snapIndex <= index { 8782 require.Fail(t, "snapshot index should be greater than index") 8783 } 8784 8785 // Assert SnapshotMinIndex returns immediately if index == latest index 8786 ctx, cancel = context.WithTimeout(context.Background(), 0) 8787 snap, err = s.SnapshotMinIndex(ctx, index+1) 8788 cancel() 8789 require.NoError(t, err) 8790 8791 snapIndex, err = snap.LatestIndex() 8792 require.NoError(t, err) 8793 require.Equal(t, snapIndex, index+1) 8794 8795 // Assert SnapshotMinIndex blocks if index > latest index 8796 errCh := make(chan error, 1) 8797 ctx, cancel = context.WithTimeout(context.Background(), 10*time.Second) 8798 defer cancel() 8799 go func() { 8800 defer close(errCh) 8801 waitIndex := index + 2 8802 snap, err := s.SnapshotMinIndex(ctx, waitIndex) 8803 if err != nil { 8804 errCh <- err 8805 return 8806 } 8807 8808 snapIndex, err := snap.LatestIndex() 8809 if err != nil { 8810 errCh <- err 8811 return 8812 } 8813 8814 if snapIndex < waitIndex { 8815 errCh <- fmt.Errorf("snapshot index < wait index: %d < %d", snapIndex, waitIndex) 8816 return 8817 } 8818 }() 8819 8820 select { 8821 case err := <-errCh: 8822 require.NoError(t, err) 8823 case <-time.After(500 * time.Millisecond): 8824 // Let it block for a bit before unblocking by upserting 8825 } 8826 8827 node.Name = "hal" 8828 require.NoError(t, s.UpsertNode(index+2, node)) 8829 8830 select { 8831 case err := <-errCh: 8832 require.NoError(t, err) 8833 case <-time.After(5 * time.Second): 8834 require.Fail(t, "timed out waiting for SnapshotMinIndex to unblock") 8835 } 8836 } 8837 8838 // TestStateStore_SnapshotMinIndex_Timeout asserts StateStore.SnapshotMinIndex 8839 // returns an error if the desired index is not reached within the deadline. 8840 func TestStateStore_SnapshotMinIndex_Timeout(t *testing.T) { 8841 t.Parallel() 8842 8843 s := testStateStore(t) 8844 index, err := s.LatestIndex() 8845 require.NoError(t, err) 8846 8847 // Assert SnapshotMinIndex blocks if index > latest index 8848 ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond) 8849 defer cancel() 8850 snap, err := s.SnapshotMinIndex(ctx, index+1) 8851 require.EqualError(t, err, context.DeadlineExceeded.Error()) 8852 require.Nil(t, snap) 8853 } 8854 8855 // watchFired is a helper for unit tests that returns if the given watch set 8856 // fired (it doesn't care which watch actually fired). This uses a fixed 8857 // timeout since we already expect the event happened before calling this and 8858 // just need to distinguish a fire from a timeout. We do need a little time to 8859 // allow the watch to set up any goroutines, though. 8860 func watchFired(ws memdb.WatchSet) bool { 8861 timedOut := ws.Watch(time.After(50 * time.Millisecond)) 8862 return !timedOut 8863 } 8864 8865 // NodeIDSort is used to sort nodes by ID 8866 type NodeIDSort []*structs.Node 8867 8868 func (n NodeIDSort) Len() int { 8869 return len(n) 8870 } 8871 8872 func (n NodeIDSort) Less(i, j int) bool { 8873 return n[i].ID < n[j].ID 8874 } 8875 8876 func (n NodeIDSort) Swap(i, j int) { 8877 n[i], n[j] = n[j], n[i] 8878 } 8879 8880 // JobIDis used to sort jobs by id 8881 type JobIDSort []*structs.Job 8882 8883 func (n JobIDSort) Len() int { 8884 return len(n) 8885 } 8886 8887 func (n JobIDSort) Less(i, j int) bool { 8888 return n[i].ID < n[j].ID 8889 } 8890 8891 func (n JobIDSort) Swap(i, j int) { 8892 n[i], n[j] = n[j], n[i] 8893 } 8894 8895 // EvalIDis used to sort evals by id 8896 type EvalIDSort []*structs.Evaluation 8897 8898 func (n EvalIDSort) Len() int { 8899 return len(n) 8900 } 8901 8902 func (n EvalIDSort) Less(i, j int) bool { 8903 return n[i].ID < n[j].ID 8904 } 8905 8906 func (n EvalIDSort) Swap(i, j int) { 8907 n[i], n[j] = n[j], n[i] 8908 } 8909 8910 // AllocIDsort used to sort allocations by id 8911 type AllocIDSort []*structs.Allocation 8912 8913 func (n AllocIDSort) Len() int { 8914 return len(n) 8915 } 8916 8917 func (n AllocIDSort) Less(i, j int) bool { 8918 return n[i].ID < n[j].ID 8919 } 8920 8921 func (n AllocIDSort) Swap(i, j int) { 8922 n[i], n[j] = n[j], n[i] 8923 }