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