github.com/bigcommerce/nomad@v0.9.3-bc/nomad/blocked_evals_test.go (about) 1 package nomad 2 3 import ( 4 "fmt" 5 "reflect" 6 "testing" 7 "time" 8 9 "github.com/hashicorp/nomad/helper/testlog" 10 "github.com/hashicorp/nomad/nomad/mock" 11 "github.com/hashicorp/nomad/nomad/structs" 12 "github.com/hashicorp/nomad/testutil" 13 ) 14 15 func testBlockedEvals(t *testing.T) (*BlockedEvals, *EvalBroker) { 16 broker := testBroker(t, 0) 17 broker.SetEnabled(true) 18 blocked := NewBlockedEvals(broker, testlog.HCLogger(t)) 19 blocked.SetEnabled(true) 20 return blocked, broker 21 } 22 23 func TestBlockedEvals_Block_Disabled(t *testing.T) { 24 t.Parallel() 25 blocked, _ := testBlockedEvals(t) 26 blocked.SetEnabled(false) 27 28 // Create an escaped eval and add it to the blocked tracker. 29 e := mock.Eval() 30 e.Status = structs.EvalStatusBlocked 31 e.EscapedComputedClass = true 32 blocked.Block(e) 33 34 // Verify block did nothing 35 bStats := blocked.Stats() 36 if bStats.TotalBlocked != 0 || bStats.TotalEscaped != 0 { 37 t.Fatalf("bad: %#v", bStats) 38 } 39 } 40 41 func TestBlockedEvals_Block_SameJob(t *testing.T) { 42 t.Parallel() 43 blocked, _ := testBlockedEvals(t) 44 45 // Create two blocked evals and add them to the blocked tracker. 46 e := mock.Eval() 47 e2 := mock.Eval() 48 e2.JobID = e.JobID 49 blocked.Block(e) 50 blocked.Block(e2) 51 52 // Verify block did track both 53 bStats := blocked.Stats() 54 if bStats.TotalBlocked != 1 || bStats.TotalEscaped != 0 { 55 t.Fatalf("bad: %#v", bStats) 56 } 57 } 58 59 func TestBlockedEvals_Block_Quota(t *testing.T) { 60 t.Parallel() 61 blocked, _ := testBlockedEvals(t) 62 63 // Create a blocked evals on quota 64 e := mock.Eval() 65 e.QuotaLimitReached = "foo" 66 blocked.Block(e) 67 68 // Verify block did track both 69 bs := blocked.Stats() 70 if bs.TotalBlocked != 1 || bs.TotalEscaped != 0 || bs.TotalQuotaLimit != 1 { 71 t.Fatalf("bad: %#v", bs) 72 } 73 } 74 75 func TestBlockedEvals_Block_PriorUnblocks(t *testing.T) { 76 t.Parallel() 77 blocked, _ := testBlockedEvals(t) 78 79 // Do unblocks prior to blocking 80 blocked.Unblock("v1:123", 1000) 81 blocked.Unblock("v1:123", 1001) 82 83 // Create two blocked evals and add them to the blocked tracker. 84 e := mock.Eval() 85 e.Status = structs.EvalStatusBlocked 86 e.ClassEligibility = map[string]bool{"v1:123": false, "v1:456": false} 87 e.SnapshotIndex = 999 88 blocked.Block(e) 89 90 // Verify block did track both 91 bStats := blocked.Stats() 92 if bStats.TotalBlocked != 1 || bStats.TotalEscaped != 0 { 93 t.Fatalf("bad: %#v", bStats) 94 } 95 } 96 97 func TestBlockedEvals_GetDuplicates(t *testing.T) { 98 t.Parallel() 99 blocked, _ := testBlockedEvals(t) 100 101 // Create duplicate blocked evals and add them to the blocked tracker. 102 e := mock.Eval() 103 e.CreateIndex = 100 104 e2 := mock.Eval() 105 e2.JobID = e.JobID 106 e2.CreateIndex = 101 107 e3 := mock.Eval() 108 e3.JobID = e.JobID 109 e3.CreateIndex = 102 110 e4 := mock.Eval() 111 e4.JobID = e.JobID 112 e4.CreateIndex = 100 113 blocked.Block(e) 114 blocked.Block(e2) 115 116 // Verify stats such that we are only tracking one 117 bStats := blocked.Stats() 118 if bStats.TotalBlocked != 1 || bStats.TotalEscaped != 0 { 119 t.Fatalf("bad: %#v", bStats) 120 } 121 122 // Get the duplicates. 123 out := blocked.GetDuplicates(0) 124 if len(out) != 1 || !reflect.DeepEqual(out[0], e) { 125 t.Fatalf("bad: %#v %#v", out, e) 126 } 127 128 // Call block again after a small sleep. 129 go func() { 130 time.Sleep(500 * time.Millisecond) 131 blocked.Block(e3) 132 }() 133 134 // Get the duplicates. 135 out = blocked.GetDuplicates(1 * time.Second) 136 if len(out) != 1 || !reflect.DeepEqual(out[0], e2) { 137 t.Fatalf("bad: %#v %#v", out, e2) 138 } 139 140 // Verify stats such that we are only tracking one 141 bStats = blocked.Stats() 142 if bStats.TotalBlocked != 1 || bStats.TotalEscaped != 0 { 143 t.Fatalf("bad: %#v", bStats) 144 } 145 146 // Add an older evaluation and assert it gets cancelled 147 blocked.Block(e4) 148 out = blocked.GetDuplicates(0) 149 if len(out) != 1 || !reflect.DeepEqual(out[0], e4) { 150 t.Fatalf("bad: %#v %#v", out, e4) 151 } 152 153 // Verify stats such that we are only tracking one 154 bStats = blocked.Stats() 155 if bStats.TotalBlocked != 1 || bStats.TotalEscaped != 0 { 156 t.Fatalf("bad: %#v", bStats) 157 } 158 } 159 160 func TestBlockedEvals_UnblockEscaped(t *testing.T) { 161 t.Parallel() 162 blocked, broker := testBlockedEvals(t) 163 164 // Create an escaped eval and add it to the blocked tracker. 165 e := mock.Eval() 166 e.Status = structs.EvalStatusBlocked 167 e.EscapedComputedClass = true 168 blocked.Block(e) 169 170 // Verify block caused the eval to be tracked 171 bStats := blocked.Stats() 172 if bStats.TotalBlocked != 1 || bStats.TotalEscaped != 1 { 173 t.Fatalf("bad: %#v", bStats) 174 } 175 176 blocked.Unblock("v1:123", 1000) 177 178 testutil.WaitForResult(func() (bool, error) { 179 // Verify Unblock caused an enqueue 180 brokerStats := broker.Stats() 181 if brokerStats.TotalReady != 1 { 182 return false, fmt.Errorf("bad: %#v", brokerStats) 183 } 184 185 // Verify Unblock updates the stats 186 bStats := blocked.Stats() 187 if bStats.TotalBlocked != 0 || bStats.TotalEscaped != 0 { 188 return false, fmt.Errorf("bad: %#v", bStats) 189 } 190 return true, nil 191 }, func(err error) { 192 t.Fatalf("err: %s", err) 193 }) 194 } 195 196 func TestBlockedEvals_UnblockEligible(t *testing.T) { 197 t.Parallel() 198 blocked, broker := testBlockedEvals(t) 199 200 // Create a blocked eval that is eligible on a specific node class and add 201 // it to the blocked tracker. 202 e := mock.Eval() 203 e.Status = structs.EvalStatusBlocked 204 e.ClassEligibility = map[string]bool{"v1:123": true} 205 blocked.Block(e) 206 207 // Verify block caused the eval to be tracked 208 blockedStats := blocked.Stats() 209 if blockedStats.TotalBlocked != 1 { 210 t.Fatalf("bad: %#v", blockedStats) 211 } 212 213 blocked.Unblock("v1:123", 1000) 214 215 testutil.WaitForResult(func() (bool, error) { 216 // Verify Unblock caused an enqueue 217 brokerStats := broker.Stats() 218 if brokerStats.TotalReady != 1 { 219 return false, fmt.Errorf("bad: %#v", brokerStats) 220 } 221 222 // Verify Unblock updates the stats 223 bStats := blocked.Stats() 224 if bStats.TotalBlocked != 0 || bStats.TotalEscaped != 0 { 225 return false, fmt.Errorf("bad: %#v", bStats) 226 } 227 return true, nil 228 }, func(err error) { 229 t.Fatalf("err: %s", err) 230 }) 231 } 232 233 func TestBlockedEvals_UnblockIneligible(t *testing.T) { 234 t.Parallel() 235 blocked, broker := testBlockedEvals(t) 236 237 // Create a blocked eval that is ineligible on a specific node class and add 238 // it to the blocked tracker. 239 e := mock.Eval() 240 e.Status = structs.EvalStatusBlocked 241 e.ClassEligibility = map[string]bool{"v1:123": false} 242 blocked.Block(e) 243 244 // Verify block caused the eval to be tracked 245 blockedStats := blocked.Stats() 246 if blockedStats.TotalBlocked != 1 && blockedStats.TotalEscaped != 0 { 247 t.Fatalf("bad: %#v", blockedStats) 248 } 249 250 // Should do nothing 251 blocked.Unblock("v1:123", 1000) 252 253 testutil.WaitForResult(func() (bool, error) { 254 // Verify Unblock didn't cause an enqueue 255 brokerStats := broker.Stats() 256 if brokerStats.TotalReady != 0 { 257 return false, fmt.Errorf("bad: %#v", brokerStats) 258 } 259 260 bStats := blocked.Stats() 261 if bStats.TotalBlocked != 1 || bStats.TotalEscaped != 0 { 262 return false, fmt.Errorf("bad: %#v", bStats) 263 } 264 return true, nil 265 }, func(err error) { 266 t.Fatalf("err: %s", err) 267 }) 268 } 269 270 func TestBlockedEvals_UnblockUnknown(t *testing.T) { 271 t.Parallel() 272 blocked, broker := testBlockedEvals(t) 273 274 // Create a blocked eval that is ineligible on a specific node class and add 275 // it to the blocked tracker. 276 e := mock.Eval() 277 e.Status = structs.EvalStatusBlocked 278 e.ClassEligibility = map[string]bool{"v1:123": true, "v1:456": false} 279 blocked.Block(e) 280 281 // Verify block caused the eval to be tracked 282 blockedStats := blocked.Stats() 283 if blockedStats.TotalBlocked != 1 && blockedStats.TotalEscaped != 0 { 284 t.Fatalf("bad: %#v", blockedStats) 285 } 286 287 // Should unblock because the eval hasn't seen this node class. 288 blocked.Unblock("v1:789", 1000) 289 290 testutil.WaitForResult(func() (bool, error) { 291 // Verify Unblock causes an enqueue 292 brokerStats := broker.Stats() 293 if brokerStats.TotalReady != 1 { 294 return false, fmt.Errorf("bad: %#v", brokerStats) 295 } 296 297 // Verify Unblock updates the stats 298 bStats := blocked.Stats() 299 if bStats.TotalBlocked != 0 || bStats.TotalEscaped != 0 { 300 return false, fmt.Errorf("bad: %#v", bStats) 301 } 302 return true, nil 303 }, func(err error) { 304 t.Fatalf("err: %s", err) 305 }) 306 } 307 308 func TestBlockedEvals_UnblockEligible_Quota(t *testing.T) { 309 t.Parallel() 310 blocked, broker := testBlockedEvals(t) 311 312 // Create a blocked eval that is eligible for a particular quota 313 e := mock.Eval() 314 e.Status = structs.EvalStatusBlocked 315 e.QuotaLimitReached = "foo" 316 blocked.Block(e) 317 318 // Verify block caused the eval to be tracked 319 bs := blocked.Stats() 320 if bs.TotalBlocked != 1 || bs.TotalQuotaLimit != 1 { 321 t.Fatalf("bad: %#v", bs) 322 } 323 324 blocked.UnblockQuota("foo", 1000) 325 326 testutil.WaitForResult(func() (bool, error) { 327 // Verify Unblock caused an enqueue 328 brokerStats := broker.Stats() 329 if brokerStats.TotalReady != 1 { 330 return false, fmt.Errorf("bad: %#v", brokerStats) 331 } 332 333 // Verify Unblock updates the stats 334 bs := blocked.Stats() 335 if bs.TotalBlocked != 0 || bs.TotalEscaped != 0 || bs.TotalQuotaLimit != 0 { 336 return false, fmt.Errorf("bad: %#v", bs) 337 } 338 return true, nil 339 }, func(err error) { 340 t.Fatalf("err: %s", err) 341 }) 342 } 343 344 func TestBlockedEvals_UnblockIneligible_Quota(t *testing.T) { 345 t.Parallel() 346 blocked, broker := testBlockedEvals(t) 347 348 // Create a blocked eval that is eligible on a specific quota 349 e := mock.Eval() 350 e.Status = structs.EvalStatusBlocked 351 e.QuotaLimitReached = "foo" 352 blocked.Block(e) 353 354 // Verify block caused the eval to be tracked 355 bs := blocked.Stats() 356 if bs.TotalBlocked != 1 || bs.TotalQuotaLimit != 1 { 357 t.Fatalf("bad: %#v", bs) 358 } 359 360 // Should do nothing 361 blocked.UnblockQuota("bar", 1000) 362 363 testutil.WaitForResult(func() (bool, error) { 364 // Verify Unblock didn't cause an enqueue 365 brokerStats := broker.Stats() 366 if brokerStats.TotalReady != 0 { 367 return false, fmt.Errorf("bad: %#v", brokerStats) 368 } 369 370 bs := blocked.Stats() 371 if bs.TotalBlocked != 1 || bs.TotalEscaped != 0 || bs.TotalQuotaLimit != 1 { 372 return false, fmt.Errorf("bad: %#v", bs) 373 } 374 return true, nil 375 }, func(err error) { 376 t.Fatalf("err: %s", err) 377 }) 378 } 379 380 func TestBlockedEvals_Reblock(t *testing.T) { 381 t.Parallel() 382 blocked, broker := testBlockedEvals(t) 383 384 // Create an evaluation, Enqueue/Dequeue it to get a token 385 e := mock.Eval() 386 e.SnapshotIndex = 500 387 e.Status = structs.EvalStatusBlocked 388 e.ClassEligibility = map[string]bool{"v1:123": true, "v1:456": false} 389 broker.Enqueue(e) 390 391 _, token, err := broker.Dequeue([]string{e.Type}, time.Second) 392 if err != nil { 393 t.Fatalf("err: %v", err) 394 } 395 396 // Reblock the evaluation 397 blocked.Reblock(e, token) 398 399 // Verify block caused the eval to be tracked 400 blockedStats := blocked.Stats() 401 if blockedStats.TotalBlocked != 1 && blockedStats.TotalEscaped != 0 { 402 t.Fatalf("bad: %#v", blockedStats) 403 } 404 405 // Should unblock because the eval 406 blocked.Unblock("v1:123", 1000) 407 408 brokerStats := broker.Stats() 409 if brokerStats.TotalReady != 0 && brokerStats.TotalUnacked != 1 { 410 t.Fatalf("bad: %#v", brokerStats) 411 } 412 413 // Ack the evaluation which should cause the reblocked eval to transition 414 // to ready 415 if err := broker.Ack(e.ID, token); err != nil { 416 t.Fatalf("err: %v", err) 417 } 418 419 testutil.WaitForResult(func() (bool, error) { 420 // Verify Unblock causes an enqueue 421 brokerStats := broker.Stats() 422 if brokerStats.TotalReady != 1 { 423 return false, fmt.Errorf("bad: %#v", brokerStats) 424 } 425 426 // Verify Unblock updates the stats 427 bStats := blocked.Stats() 428 if bStats.TotalBlocked != 0 || bStats.TotalEscaped != 0 { 429 return false, fmt.Errorf("bad: %#v", bStats) 430 } 431 return true, nil 432 }, func(err error) { 433 t.Fatalf("err: %s", err) 434 }) 435 } 436 437 // Test the block case in which the eval should be immediately unblocked since 438 // it is escaped and old 439 func TestBlockedEvals_Block_ImmediateUnblock_Escaped(t *testing.T) { 440 t.Parallel() 441 blocked, broker := testBlockedEvals(t) 442 443 // Do an unblock prior to blocking 444 blocked.Unblock("v1:123", 1000) 445 446 // Create a blocked eval that is eligible on a specific node class and add 447 // it to the blocked tracker. 448 e := mock.Eval() 449 e.Status = structs.EvalStatusBlocked 450 e.EscapedComputedClass = true 451 e.SnapshotIndex = 900 452 blocked.Block(e) 453 454 // Verify block caused the eval to be immediately unblocked 455 blockedStats := blocked.Stats() 456 if blockedStats.TotalBlocked != 0 && blockedStats.TotalEscaped != 0 { 457 t.Fatalf("bad: %#v", blockedStats) 458 } 459 460 testutil.WaitForResult(func() (bool, error) { 461 // Verify Unblock caused an enqueue 462 brokerStats := broker.Stats() 463 if brokerStats.TotalReady != 1 { 464 return false, fmt.Errorf("bad: %#v", brokerStats) 465 } 466 467 return true, nil 468 }, func(err error) { 469 t.Fatalf("err: %s", err) 470 }) 471 } 472 473 // Test the block case in which the eval should be immediately unblocked since 474 // there is an unblock on an unseen class that occurred while it was in the 475 // scheduler 476 func TestBlockedEvals_Block_ImmediateUnblock_UnseenClass_After(t *testing.T) { 477 t.Parallel() 478 blocked, broker := testBlockedEvals(t) 479 480 // Do an unblock prior to blocking 481 blocked.Unblock("v1:123", 1000) 482 483 // Create a blocked eval that is eligible on a specific node class and add 484 // it to the blocked tracker. 485 e := mock.Eval() 486 e.Status = structs.EvalStatusBlocked 487 e.EscapedComputedClass = false 488 e.SnapshotIndex = 900 489 blocked.Block(e) 490 491 // Verify block caused the eval to be immediately unblocked 492 blockedStats := blocked.Stats() 493 if blockedStats.TotalBlocked != 0 && blockedStats.TotalEscaped != 0 { 494 t.Fatalf("bad: %#v", blockedStats) 495 } 496 497 testutil.WaitForResult(func() (bool, error) { 498 // Verify Unblock caused an enqueue 499 brokerStats := broker.Stats() 500 if brokerStats.TotalReady != 1 { 501 return false, fmt.Errorf("bad: %#v", brokerStats) 502 } 503 504 return true, nil 505 }, func(err error) { 506 t.Fatalf("err: %s", err) 507 }) 508 } 509 510 // Test the block case in which the eval should not immediately unblock since 511 // there is an unblock on an unseen class that occurred before it was in the 512 // scheduler 513 func TestBlockedEvals_Block_ImmediateUnblock_UnseenClass_Before(t *testing.T) { 514 t.Parallel() 515 blocked, _ := testBlockedEvals(t) 516 517 // Do an unblock prior to blocking 518 blocked.Unblock("v1:123", 500) 519 520 // Create a blocked eval that is eligible on a specific node class and add 521 // it to the blocked tracker. 522 e := mock.Eval() 523 e.Status = structs.EvalStatusBlocked 524 e.EscapedComputedClass = false 525 e.SnapshotIndex = 900 526 blocked.Block(e) 527 528 // Verify block caused the eval to be immediately unblocked 529 blockedStats := blocked.Stats() 530 if blockedStats.TotalBlocked != 1 && blockedStats.TotalEscaped != 0 { 531 t.Fatalf("bad: %#v", blockedStats) 532 } 533 } 534 535 // Test the block case in which the eval should be immediately unblocked since 536 // it a class it is eligible for has been unblocked 537 func TestBlockedEvals_Block_ImmediateUnblock_SeenClass(t *testing.T) { 538 t.Parallel() 539 blocked, broker := testBlockedEvals(t) 540 541 // Do an unblock prior to blocking 542 blocked.Unblock("v1:123", 1000) 543 544 // Create a blocked eval that is eligible on a specific node class and add 545 // it to the blocked tracker. 546 e := mock.Eval() 547 e.Status = structs.EvalStatusBlocked 548 e.ClassEligibility = map[string]bool{"v1:123": true, "v1:456": false} 549 e.SnapshotIndex = 900 550 blocked.Block(e) 551 552 // Verify block caused the eval to be immediately unblocked 553 blockedStats := blocked.Stats() 554 if blockedStats.TotalBlocked != 0 && blockedStats.TotalEscaped != 0 { 555 t.Fatalf("bad: %#v", blockedStats) 556 } 557 558 testutil.WaitForResult(func() (bool, error) { 559 // Verify Unblock caused an enqueue 560 brokerStats := broker.Stats() 561 if brokerStats.TotalReady != 1 { 562 return false, fmt.Errorf("bad: %#v", brokerStats) 563 } 564 565 return true, nil 566 }, func(err error) { 567 t.Fatalf("err: %s", err) 568 }) 569 } 570 571 // Test the block case in which the eval should be immediately unblocked since 572 // it a quota has changed that it is using 573 func TestBlockedEvals_Block_ImmediateUnblock_Quota(t *testing.T) { 574 t.Parallel() 575 blocked, broker := testBlockedEvals(t) 576 577 // Do an unblock prior to blocking 578 blocked.UnblockQuota("my-quota", 1000) 579 580 // Create a blocked eval that is eligible on a specific node class and add 581 // it to the blocked tracker. 582 e := mock.Eval() 583 e.Status = structs.EvalStatusBlocked 584 e.QuotaLimitReached = "my-quota" 585 e.SnapshotIndex = 900 586 blocked.Block(e) 587 588 // Verify block caused the eval to be immediately unblocked 589 bs := blocked.Stats() 590 if bs.TotalBlocked != 0 && bs.TotalEscaped != 0 && bs.TotalQuotaLimit != 0 { 591 t.Fatalf("bad: %#v", bs) 592 } 593 594 testutil.WaitForResult(func() (bool, error) { 595 // Verify Unblock caused an enqueue 596 brokerStats := broker.Stats() 597 if brokerStats.TotalReady != 1 { 598 return false, fmt.Errorf("bad: %#v", brokerStats) 599 } 600 601 return true, nil 602 }, func(err error) { 603 t.Fatalf("err: %s", err) 604 }) 605 } 606 607 func TestBlockedEvals_UnblockFailed(t *testing.T) { 608 t.Parallel() 609 blocked, broker := testBlockedEvals(t) 610 611 // Create blocked evals that are due to failures 612 e := mock.Eval() 613 e.Status = structs.EvalStatusBlocked 614 e.TriggeredBy = structs.EvalTriggerMaxPlans 615 e.EscapedComputedClass = true 616 blocked.Block(e) 617 618 e2 := mock.Eval() 619 e2.Status = structs.EvalStatusBlocked 620 e2.TriggeredBy = structs.EvalTriggerMaxPlans 621 e2.ClassEligibility = map[string]bool{"v1:123": true, "v1:456": false} 622 blocked.Block(e2) 623 624 e3 := mock.Eval() 625 e3.Status = structs.EvalStatusBlocked 626 e3.TriggeredBy = structs.EvalTriggerMaxPlans 627 e3.QuotaLimitReached = "foo" 628 blocked.Block(e3) 629 630 // Trigger an unblock fail 631 blocked.UnblockFailed() 632 633 // Verify UnblockFailed caused the eval to be immediately unblocked 634 bs := blocked.Stats() 635 if bs.TotalBlocked != 0 || bs.TotalEscaped != 0 || bs.TotalQuotaLimit != 0 { 636 t.Fatalf("bad: %#v", bs) 637 } 638 639 testutil.WaitForResult(func() (bool, error) { 640 // Verify Unblock caused an enqueue 641 brokerStats := broker.Stats() 642 if brokerStats.TotalReady != 3 { 643 return false, fmt.Errorf("bad: %#v", brokerStats) 644 } 645 return true, nil 646 }, func(err error) { 647 t.Fatalf("err: %s", err) 648 }) 649 650 // Reblock an eval for the same job and check that it gets tracked. 651 blocked.Block(e) 652 bs = blocked.Stats() 653 if bs.TotalBlocked != 1 || bs.TotalEscaped != 1 { 654 t.Fatalf("bad: %#v", bs) 655 } 656 } 657 658 func TestBlockedEvals_Untrack(t *testing.T) { 659 t.Parallel() 660 blocked, _ := testBlockedEvals(t) 661 662 // Create two blocked evals and add them to the blocked tracker. 663 e := mock.Eval() 664 e.Status = structs.EvalStatusBlocked 665 e.ClassEligibility = map[string]bool{"v1:123": false, "v1:456": false} 666 e.SnapshotIndex = 1000 667 blocked.Block(e) 668 669 // Verify block did track 670 bStats := blocked.Stats() 671 if bStats.TotalBlocked != 1 || bStats.TotalEscaped != 0 { 672 t.Fatalf("bad: %#v", bStats) 673 } 674 675 // Untrack and verify 676 blocked.Untrack(e.JobID, e.Namespace) 677 bStats = blocked.Stats() 678 if bStats.TotalBlocked != 0 || bStats.TotalEscaped != 0 { 679 t.Fatalf("bad: %#v", bStats) 680 } 681 } 682 683 func TestBlockedEvals_Untrack_Quota(t *testing.T) { 684 t.Parallel() 685 blocked, _ := testBlockedEvals(t) 686 687 // Create a blocked evals and add it to the blocked tracker. 688 e := mock.Eval() 689 e.Status = structs.EvalStatusBlocked 690 e.QuotaLimitReached = "foo" 691 e.SnapshotIndex = 1000 692 blocked.Block(e) 693 694 // Verify block did track 695 bs := blocked.Stats() 696 if bs.TotalBlocked != 1 || bs.TotalEscaped != 0 || bs.TotalQuotaLimit != 1 { 697 t.Fatalf("bad: %#v", bs) 698 } 699 700 // Untrack and verify 701 blocked.Untrack(e.JobID, e.Namespace) 702 bs = blocked.Stats() 703 if bs.TotalBlocked != 0 || bs.TotalEscaped != 0 || bs.TotalQuotaLimit != 0 { 704 t.Fatalf("bad: %#v", bs) 705 } 706 }