vitess.io/vitess@v0.16.2/go/vt/vtadmin/cluster/cluster_internal_test.go (about) 1 /* 2 Copyright 2021 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package cluster 18 19 import ( 20 "context" 21 "database/sql" 22 "fmt" 23 "sort" 24 "testing" 25 "time" 26 27 "github.com/stretchr/testify/assert" 28 "github.com/stretchr/testify/require" 29 "k8s.io/apimachinery/pkg/util/sets" 30 31 "vitess.io/vitess/go/pools" 32 "vitess.io/vitess/go/test/utils" 33 "vitess.io/vitess/go/vt/topo" 34 "vitess.io/vitess/go/vt/topo/topoproto" 35 "vitess.io/vitess/go/vt/vitessdriver" 36 "vitess.io/vitess/go/vt/vtadmin/cluster/resolver" 37 "vitess.io/vitess/go/vt/vtadmin/vtctldclient/fakevtctldclient" 38 "vitess.io/vitess/go/vt/vtadmin/vtsql" 39 "vitess.io/vitess/go/vt/vtadmin/vtsql/fakevtsql" 40 "vitess.io/vitess/go/vt/vtctl/vtctldclient" 41 42 logutilpb "vitess.io/vitess/go/vt/proto/logutil" 43 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 44 vtadminpb "vitess.io/vitess/go/vt/proto/vtadmin" 45 vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" 46 ) 47 48 type vtctldProxy struct { 49 vtctldclient.VtctldClient 50 dialErr error 51 } 52 53 func (fake *vtctldProxy) Dial(ctx context.Context) error { return fake.dialErr } 54 55 func TestDeleteTablets(t *testing.T) { 56 t.Parallel() 57 58 testClusterProto := &vtadminpb.Cluster{ 59 Id: "test", 60 Name: "test", 61 } 62 63 tests := []struct { 64 name string 65 cluster *Cluster 66 timeout time.Duration 67 setup func(t testing.TB, c *Cluster) 68 req *vtctldatapb.DeleteTabletsRequest 69 expected *vtctldatapb.DeleteTabletsResponse 70 shouldErr bool 71 }{ 72 { 73 name: "ok", 74 cluster: &Cluster{ 75 Vtctld: &fakevtctldclient.VtctldClient{ 76 DeleteTabletsResults: map[string]error{ 77 "zone1-0000000100,zone1-0000000101": nil, 78 }, 79 }, 80 topoRWPool: pools.NewRPCPool(1, time.Millisecond*100, nil), 81 }, 82 req: &vtctldatapb.DeleteTabletsRequest{ 83 TabletAliases: []*topodatapb.TabletAlias{ 84 { 85 Cell: "zone1", 86 Uid: 100, 87 }, 88 { 89 Cell: "zone1", 90 Uid: 101, 91 }, 92 }, 93 }, 94 expected: &vtctldatapb.DeleteTabletsResponse{}, 95 }, 96 { 97 name: "error", 98 cluster: &Cluster{ 99 Vtctld: &fakevtctldclient.VtctldClient{}, 100 topoRWPool: pools.NewRPCPool(1, time.Millisecond*100, nil), 101 }, 102 req: &vtctldatapb.DeleteTabletsRequest{ 103 TabletAliases: []*topodatapb.TabletAlias{ 104 { 105 Cell: "zone1", 106 Uid: 100, 107 }, 108 { 109 Cell: "zone1", 110 Uid: 101, 111 }, 112 }, 113 }, 114 shouldErr: true, 115 }, 116 { 117 name: "RPC pool full", 118 cluster: &Cluster{ 119 Vtctld: &fakevtctldclient.VtctldClient{}, 120 topoRWPool: pools.NewRPCPool(1, time.Millisecond*10, nil), 121 }, 122 timeout: time.Millisecond * 50, 123 setup: func(t testing.TB, c *Cluster) { 124 err := c.topoRWPool.Acquire(context.Background()) 125 require.NoError(t, err, "failed to lock RPC pool") 126 t.Cleanup(c.topoRWPool.Release) 127 }, 128 req: &vtctldatapb.DeleteTabletsRequest{ 129 TabletAliases: []*topodatapb.TabletAlias{ 130 { 131 Cell: "zone1", 132 Uid: 100, 133 }, 134 { 135 Cell: "zone1", 136 Uid: 101, 137 }, 138 }, 139 }, 140 shouldErr: true, 141 }, 142 } 143 144 for _, tt := range tests { 145 tt := tt 146 147 t.Run(tt.name, func(t *testing.T) { 148 t.Parallel() 149 150 tt.cluster.ID = testClusterProto.Id 151 tt.cluster.Name = testClusterProto.Name 152 153 if tt.setup != nil { 154 tt.setup(t, tt.cluster) 155 } 156 157 var ( 158 ctx context.Context 159 cancel context.CancelFunc 160 ) 161 162 switch tt.timeout { 163 case 0: 164 ctx, cancel = context.WithCancel(context.Background()) 165 default: 166 ctx, cancel = context.WithTimeout(context.Background(), tt.timeout) 167 } 168 defer cancel() 169 170 resp, err := tt.cluster.DeleteTablets(ctx, tt.req) 171 if tt.shouldErr { 172 assert.Error(t, err, "expected error, got %+v", resp) 173 return 174 } 175 176 require.NoError(t, err) 177 utils.MustMatch(t, tt.expected, resp) 178 }) 179 } 180 } 181 182 func TestEmergencyFailoverShard(t *testing.T) { 183 t.Parallel() 184 185 testClusterProto := &vtadminpb.Cluster{ 186 Id: "test", 187 Name: "test", 188 } 189 190 tests := []struct { 191 name string 192 cluster *Cluster 193 setup func(t testing.TB, c *Cluster) 194 timeout time.Duration 195 req *vtctldatapb.EmergencyReparentShardRequest 196 expected *vtadminpb.EmergencyFailoverShardResponse 197 shouldErr bool 198 }{ 199 { 200 name: "ok", 201 cluster: &Cluster{ 202 ID: "test", 203 Name: "test", 204 Vtctld: &fakevtctldclient.VtctldClient{ 205 EmergencyReparentShardResults: map[string]struct { 206 Response *vtctldatapb.EmergencyReparentShardResponse 207 Error error 208 }{ 209 "ks1/-": { 210 Response: &vtctldatapb.EmergencyReparentShardResponse{ 211 Keyspace: "ks1", 212 Shard: "-", 213 PromotedPrimary: &topodatapb.TabletAlias{ 214 Cell: "zone1", 215 Uid: 100, 216 }, 217 Events: []*logutilpb.Event{{}, {}, {}}, 218 }, 219 }, 220 "ks2/-80": { 221 Error: fmt.Errorf("some error: %w", assert.AnError), 222 }, 223 }, 224 }, 225 emergencyFailoverPool: pools.NewRPCPool(1, time.Second, nil), 226 }, 227 req: &vtctldatapb.EmergencyReparentShardRequest{ 228 Keyspace: "ks1", 229 Shard: "-", 230 NewPrimary: &topodatapb.TabletAlias{ 231 Cell: "zone1", 232 Uid: 100, 233 }, 234 }, 235 expected: &vtadminpb.EmergencyFailoverShardResponse{ 236 Cluster: testClusterProto, 237 Keyspace: "ks1", 238 Shard: "-", 239 PromotedPrimary: &topodatapb.TabletAlias{ 240 Cell: "zone1", 241 Uid: 100, 242 }, 243 Events: []*logutilpb.Event{{}, {}, {}}, 244 }, 245 }, 246 { 247 name: "error", 248 cluster: &Cluster{ 249 ID: "test", 250 Name: "test", 251 Vtctld: &fakevtctldclient.VtctldClient{ 252 EmergencyReparentShardResults: map[string]struct { 253 Response *vtctldatapb.EmergencyReparentShardResponse 254 Error error 255 }{ 256 "ks1/-": { 257 Response: &vtctldatapb.EmergencyReparentShardResponse{ 258 Keyspace: "ks1", 259 Shard: "-", 260 PromotedPrimary: &topodatapb.TabletAlias{ 261 Cell: "zone1", 262 Uid: 100, 263 }, 264 Events: []*logutilpb.Event{{}, {}, {}}, 265 }, 266 }, 267 "ks2/-80": { 268 Error: fmt.Errorf("some error: %w", assert.AnError), 269 }, 270 }, 271 }, 272 emergencyFailoverPool: pools.NewRPCPool(1, time.Second, nil), 273 }, 274 req: &vtctldatapb.EmergencyReparentShardRequest{ 275 Keyspace: "ks2", 276 Shard: "-80", 277 NewPrimary: &topodatapb.TabletAlias{ 278 Cell: "zone1", 279 Uid: 200, 280 }, 281 }, 282 shouldErr: true, 283 }, 284 { 285 name: "pool full", 286 cluster: &Cluster{ 287 ID: "test", 288 Name: "test", 289 Vtctld: &fakevtctldclient.VtctldClient{ 290 EmergencyReparentShardResults: map[string]struct { 291 Response *vtctldatapb.EmergencyReparentShardResponse 292 Error error 293 }{ 294 "ks1/-": { 295 Response: &vtctldatapb.EmergencyReparentShardResponse{ 296 Keyspace: "ks1", 297 Shard: "-", 298 PromotedPrimary: &topodatapb.TabletAlias{ 299 Cell: "zone1", 300 Uid: 100, 301 }, 302 Events: []*logutilpb.Event{{}, {}, {}}, 303 }, 304 }, 305 "ks2/-80": { 306 Error: fmt.Errorf("some error: %w", assert.AnError), 307 }, 308 }, 309 }, 310 emergencyFailoverPool: pools.NewRPCPool(1, time.Millisecond*25, nil), 311 }, 312 setup: func(t testing.TB, c *Cluster) { 313 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50) 314 defer cancel() 315 316 err := c.emergencyFailoverPool.Acquire(ctx) 317 require.NoError(t, err, "could not block ers pool in setup") 318 t.Cleanup(c.emergencyFailoverPool.Release) 319 }, 320 timeout: time.Millisecond * 50, 321 req: &vtctldatapb.EmergencyReparentShardRequest{ 322 Keyspace: "ks1", 323 Shard: "-", 324 NewPrimary: &topodatapb.TabletAlias{ 325 Cell: "zone1", 326 Uid: 100, 327 }, 328 }, 329 shouldErr: true, 330 }, 331 } 332 333 for _, tt := range tests { 334 tt := tt 335 336 t.Run(tt.name, func(t *testing.T) { 337 t.Parallel() 338 339 if tt.setup != nil { 340 func(t *testing.T) { 341 t.Helper() 342 tt.setup(t, tt.cluster) 343 }(t) 344 } 345 346 var ( 347 ctx context.Context 348 cancel context.CancelFunc 349 ) 350 switch tt.timeout { 351 case 0: 352 ctx, cancel = context.WithCancel(context.Background()) 353 default: 354 ctx, cancel = context.WithTimeout(context.Background(), tt.timeout) 355 } 356 defer cancel() 357 358 resp, err := tt.cluster.EmergencyFailoverShard(ctx, tt.req) 359 if tt.shouldErr { 360 assert.Error(t, err) 361 return 362 } 363 364 require.NoError(t, err) 365 utils.MustMatch(t, tt.expected, resp) 366 }) 367 } 368 } 369 370 func Test_getShardSets(t *testing.T) { 371 t.Parallel() 372 373 c := &Cluster{ 374 Vtctld: &vtctldProxy{ 375 VtctldClient: &fakevtctldclient.VtctldClient{ 376 GetKeyspaceResults: map[string]struct { 377 Response *vtctldatapb.GetKeyspaceResponse 378 Error error 379 }{ 380 "ks1": { 381 Response: &vtctldatapb.GetKeyspaceResponse{ 382 Keyspace: &vtctldatapb.Keyspace{ 383 Name: "ks1", 384 Keyspace: &topodatapb.Keyspace{}, 385 }, 386 }, 387 }, 388 "ks2": { 389 Response: &vtctldatapb.GetKeyspaceResponse{ 390 Keyspace: &vtctldatapb.Keyspace{ 391 Name: "ks2", 392 Keyspace: &topodatapb.Keyspace{}, 393 }, 394 }, 395 }, 396 "ks3": { 397 Error: topo.NewError(topo.NoNode, "ks3"), /* we need to fail in a particular way */ 398 }, 399 }, 400 GetKeyspacesResults: &struct { 401 Keyspaces []*vtctldatapb.Keyspace 402 Error error 403 }{ 404 Keyspaces: []*vtctldatapb.Keyspace{ 405 { 406 Name: "ks1", 407 Keyspace: &topodatapb.Keyspace{}, 408 }, 409 { 410 Name: "ks2", 411 Keyspace: &topodatapb.Keyspace{}, 412 }, 413 }, 414 }, 415 FindAllShardsInKeyspaceResults: map[string]struct { 416 Response *vtctldatapb.FindAllShardsInKeyspaceResponse 417 Error error 418 }{ 419 "ks1": { 420 Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{ 421 Shards: map[string]*vtctldatapb.Shard{ 422 "-80": { 423 Keyspace: "ks1", 424 Name: "-80", 425 Shard: &topodatapb.Shard{}, 426 }, 427 "80-": { 428 Keyspace: "ks1", 429 Name: "80-", 430 Shard: &topodatapb.Shard{}, 431 }, 432 }, 433 }, 434 }, 435 "ks2": { 436 Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{ 437 Shards: map[string]*vtctldatapb.Shard{ 438 "-": { 439 Keyspace: "ks2", 440 Name: "-", 441 Shard: &topodatapb.Shard{}, 442 }, 443 }, 444 }, 445 }, 446 }, 447 }, 448 }, 449 topoReadPool: pools.NewRPCPool(5, 0, nil), 450 } 451 452 tests := []struct { 453 name string 454 keyspaces []string 455 keyspaceShards []string 456 result map[string]sets.Set[string] 457 shouldErr bool 458 }{ 459 { 460 name: "all keyspaces and shards", 461 keyspaces: nil, 462 keyspaceShards: nil, 463 result: map[string]sets.Set[string]{ 464 "ks1": sets.New[string]("-80", "80-"), 465 "ks2": sets.New[string]("-"), 466 }, 467 }, 468 { 469 name: "keyspaceShards filter", 470 keyspaces: nil, 471 keyspaceShards: []string{"ks1/-80", "ks2/-"}, 472 result: map[string]sets.Set[string]{ 473 "ks1": sets.New[string]("-80"), 474 "ks2": sets.New[string]("-"), 475 }, 476 }, 477 { 478 name: "keyspace and shards filters", 479 keyspaces: []string{"ks1"}, 480 keyspaceShards: []string{"ks1/80-"}, 481 result: map[string]sets.Set[string]{ 482 "ks1": sets.New[string]("80-"), 483 }, 484 }, 485 { 486 name: "skipped non-existing shards and keyspaces", 487 keyspaces: nil, 488 keyspaceShards: []string{"ks1/-" /* does not exist */, "ks1/-80", "ks1/80-", "ks3/-" /* does not exist */}, 489 result: map[string]sets.Set[string]{ 490 "ks1": sets.New[string]("-80", "80-"), 491 }, 492 }, 493 } 494 495 for _, tt := range tests { 496 tt := tt 497 498 t.Run(tt.name, func(t *testing.T) { 499 t.Parallel() 500 501 result, err := c.getShardSets(context.Background(), tt.keyspaces, tt.keyspaceShards) 502 if tt.shouldErr { 503 assert.Error(t, err) 504 return 505 } 506 507 require.NoError(t, err) 508 assert.Equal(t, tt.result, result) 509 }) 510 } 511 } 512 513 func TestPlannedFailoverShard(t *testing.T) { 514 t.Parallel() 515 516 testClusterProto := &vtadminpb.Cluster{ 517 Id: "test", 518 Name: "test", 519 } 520 521 tests := []struct { 522 name string 523 cluster *Cluster 524 setup func(t testing.TB, c *Cluster) 525 timeout time.Duration 526 req *vtctldatapb.PlannedReparentShardRequest 527 expected *vtadminpb.PlannedFailoverShardResponse 528 shouldErr bool 529 }{ 530 { 531 name: "ok", 532 cluster: &Cluster{ 533 ID: "test", 534 Name: "test", 535 Vtctld: &fakevtctldclient.VtctldClient{ 536 PlannedReparentShardResults: map[string]struct { 537 Response *vtctldatapb.PlannedReparentShardResponse 538 Error error 539 }{ 540 "ks1/-": { 541 Response: &vtctldatapb.PlannedReparentShardResponse{ 542 Keyspace: "ks1", 543 Shard: "-", 544 PromotedPrimary: &topodatapb.TabletAlias{ 545 Cell: "zone1", 546 Uid: 100, 547 }, 548 Events: []*logutilpb.Event{{}, {}, {}}, 549 }, 550 }, 551 "ks2/-80": { 552 Error: fmt.Errorf("some error: %w", assert.AnError), 553 }, 554 }, 555 }, 556 failoverPool: pools.NewRPCPool(1, time.Second, nil), 557 }, 558 req: &vtctldatapb.PlannedReparentShardRequest{ 559 Keyspace: "ks1", 560 Shard: "-", 561 NewPrimary: &topodatapb.TabletAlias{ 562 Cell: "zone1", 563 Uid: 100, 564 }, 565 }, 566 expected: &vtadminpb.PlannedFailoverShardResponse{ 567 Cluster: testClusterProto, 568 Keyspace: "ks1", 569 Shard: "-", 570 PromotedPrimary: &topodatapb.TabletAlias{ 571 Cell: "zone1", 572 Uid: 100, 573 }, 574 Events: []*logutilpb.Event{{}, {}, {}}, 575 }, 576 }, 577 { 578 name: "error", 579 cluster: &Cluster{ 580 ID: "test", 581 Name: "test", 582 Vtctld: &fakevtctldclient.VtctldClient{ 583 PlannedReparentShardResults: map[string]struct { 584 Response *vtctldatapb.PlannedReparentShardResponse 585 Error error 586 }{ 587 "ks1/-": { 588 Response: &vtctldatapb.PlannedReparentShardResponse{ 589 Keyspace: "ks1", 590 Shard: "-", 591 PromotedPrimary: &topodatapb.TabletAlias{ 592 Cell: "zone1", 593 Uid: 100, 594 }, 595 Events: []*logutilpb.Event{{}, {}, {}}, 596 }, 597 }, 598 "ks2/-80": { 599 Error: fmt.Errorf("some error: %w", assert.AnError), 600 }, 601 }, 602 }, 603 failoverPool: pools.NewRPCPool(1, time.Second, nil), 604 }, 605 req: &vtctldatapb.PlannedReparentShardRequest{ 606 Keyspace: "ks2", 607 Shard: "-80", 608 NewPrimary: &topodatapb.TabletAlias{ 609 Cell: "zone1", 610 Uid: 200, 611 }, 612 }, 613 shouldErr: true, 614 }, 615 { 616 name: "pool full", 617 cluster: &Cluster{ 618 ID: "test", 619 Name: "test", 620 Vtctld: &fakevtctldclient.VtctldClient{ 621 PlannedReparentShardResults: map[string]struct { 622 Response *vtctldatapb.PlannedReparentShardResponse 623 Error error 624 }{ 625 "ks1/-": { 626 Response: &vtctldatapb.PlannedReparentShardResponse{ 627 Keyspace: "ks1", 628 Shard: "-", 629 PromotedPrimary: &topodatapb.TabletAlias{ 630 Cell: "zone1", 631 Uid: 100, 632 }, 633 Events: []*logutilpb.Event{{}, {}, {}}, 634 }, 635 }, 636 "ks2/-80": { 637 Error: fmt.Errorf("some error: %w", assert.AnError), 638 }, 639 }, 640 }, 641 failoverPool: pools.NewRPCPool(1, time.Millisecond*25, nil), 642 }, 643 setup: func(t testing.TB, c *Cluster) { 644 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50) 645 defer cancel() 646 647 err := c.failoverPool.Acquire(ctx) 648 require.NoError(t, err, "could not block prs pool in setup") 649 t.Cleanup(c.failoverPool.Release) 650 }, 651 timeout: time.Millisecond * 50, 652 req: &vtctldatapb.PlannedReparentShardRequest{ 653 Keyspace: "ks1", 654 Shard: "-", 655 NewPrimary: &topodatapb.TabletAlias{ 656 Cell: "zone1", 657 Uid: 100, 658 }, 659 }, 660 shouldErr: true, 661 }, 662 } 663 664 for _, tt := range tests { 665 tt := tt 666 667 t.Run(tt.name, func(t *testing.T) { 668 t.Parallel() 669 670 if tt.setup != nil { 671 func(t *testing.T) { 672 t.Helper() 673 tt.setup(t, tt.cluster) 674 }(t) 675 } 676 677 var ( 678 ctx context.Context 679 cancel context.CancelFunc 680 ) 681 switch tt.timeout { 682 case 0: 683 ctx, cancel = context.WithCancel(context.Background()) 684 default: 685 ctx, cancel = context.WithTimeout(context.Background(), tt.timeout) 686 } 687 defer cancel() 688 689 resp, err := tt.cluster.PlannedFailoverShard(ctx, tt.req) 690 if tt.shouldErr { 691 assert.Error(t, err) 692 return 693 } 694 695 require.NoError(t, err) 696 utils.MustMatch(t, tt.expected, resp) 697 }) 698 } 699 } 700 701 func TestRefreshState(t *testing.T) { 702 t.Parallel() 703 704 testClusterProto := &vtadminpb.Cluster{ 705 Id: "test", 706 Name: "test", 707 } 708 709 tests := []struct { 710 name string 711 cluster *Cluster 712 timeout time.Duration 713 setup func(t testing.TB, c *Cluster) 714 tablet *vtadminpb.Tablet 715 assertion func(t assert.TestingT, err error, msgAndArgs ...any) bool 716 assertionMsgExtra []any 717 }{ 718 { 719 name: "ok", 720 cluster: &Cluster{ 721 Vtctld: &fakevtctldclient.VtctldClient{ 722 RefreshStateResults: map[string]error{ 723 "zone1-0000000100": nil, 724 }, 725 }, 726 topoReadPool: pools.NewRPCPool(1, time.Millisecond*100, nil), 727 }, 728 tablet: &vtadminpb.Tablet{ 729 Tablet: &topodatapb.Tablet{ 730 Alias: &topodatapb.TabletAlias{ 731 Cell: "zone1", 732 Uid: 100, 733 }, 734 }, 735 }, 736 assertion: assert.NoError, 737 }, 738 { 739 name: "error", 740 cluster: &Cluster{ 741 Vtctld: &fakevtctldclient.VtctldClient{ 742 RefreshStateResults: map[string]error{ 743 "zone1-0000000100": fmt.Errorf("some error"), 744 }, 745 }, 746 topoReadPool: pools.NewRPCPool(1, time.Millisecond*100, nil), 747 }, 748 tablet: &vtadminpb.Tablet{ 749 Tablet: &topodatapb.Tablet{ 750 Alias: &topodatapb.TabletAlias{ 751 Cell: "zone1", 752 Uid: 100, 753 }, 754 }, 755 }, 756 assertion: assert.Error, 757 }, 758 { 759 name: "RPC pool full", 760 cluster: &Cluster{ 761 Vtctld: &fakevtctldclient.VtctldClient{}, 762 topoReadPool: pools.NewRPCPool(1, time.Millisecond*10, nil), 763 }, 764 timeout: time.Millisecond * 50, 765 setup: func(t testing.TB, c *Cluster) { 766 err := c.topoReadPool.Acquire(context.Background()) 767 require.NoError(t, err, "failed to lock RPC pool") 768 t.Cleanup(c.topoReadPool.Release) 769 }, 770 tablet: &vtadminpb.Tablet{ 771 Tablet: &topodatapb.Tablet{Alias: &topodatapb.TabletAlias{}}, 772 }, 773 assertion: assert.Error, 774 }, 775 } 776 777 for _, tt := range tests { 778 tt := tt 779 780 t.Run(tt.name, func(t *testing.T) { 781 t.Parallel() 782 783 tt.cluster.ID = testClusterProto.Id 784 tt.cluster.Name = testClusterProto.Name 785 786 if tt.setup != nil { 787 tt.setup(t, tt.cluster) 788 } 789 790 var ( 791 ctx context.Context 792 cancel context.CancelFunc 793 ) 794 795 switch tt.timeout { 796 case 0: 797 ctx, cancel = context.WithCancel(context.Background()) 798 default: 799 ctx, cancel = context.WithTimeout(context.Background(), tt.timeout) 800 } 801 defer cancel() 802 803 err := tt.cluster.RefreshState(ctx, tt.tablet) 804 tt.assertion(t, err, tt.assertionMsgExtra...) 805 }) 806 } 807 } 808 809 func TestRefreshTabletReplicationSource(t *testing.T) { 810 t.Parallel() 811 812 testClusterProto := &vtadminpb.Cluster{ 813 Id: "test", 814 Name: "test", 815 } 816 817 tests := []struct { 818 name string 819 cluster *Cluster 820 timeout time.Duration 821 setup func(t testing.TB, c *Cluster) 822 tablet *vtadminpb.Tablet 823 expected *vtadminpb.RefreshTabletReplicationSourceResponse 824 shouldErr bool 825 }{ 826 { 827 name: "ok", 828 cluster: &Cluster{ 829 Vtctld: &fakevtctldclient.VtctldClient{ 830 ReparentTabletResults: map[string]struct { 831 Response *vtctldatapb.ReparentTabletResponse 832 Error error 833 }{ 834 "zone1-0000000100": { 835 Response: &vtctldatapb.ReparentTabletResponse{ 836 Keyspace: "testks", 837 Shard: "-", 838 Primary: &topodatapb.TabletAlias{ 839 Cell: "zone1", 840 Uid: 500, 841 }, 842 }, 843 }, 844 }, 845 }, 846 topoRWPool: pools.NewRPCPool(1, time.Millisecond*100, nil), 847 }, 848 tablet: &vtadminpb.Tablet{ 849 Tablet: &topodatapb.Tablet{ 850 Alias: &topodatapb.TabletAlias{ 851 Cell: "zone1", 852 Uid: 100, 853 }, 854 }, 855 }, 856 expected: &vtadminpb.RefreshTabletReplicationSourceResponse{ 857 Keyspace: "testks", 858 Shard: "-", 859 Primary: &topodatapb.TabletAlias{ 860 Cell: "zone1", 861 Uid: 500, 862 }, 863 Cluster: testClusterProto, 864 }, 865 }, 866 { 867 name: "error", 868 cluster: &Cluster{ 869 Vtctld: &fakevtctldclient.VtctldClient{}, 870 topoRWPool: pools.NewRPCPool(1, time.Millisecond*100, nil), 871 }, 872 tablet: &vtadminpb.Tablet{ 873 Tablet: &topodatapb.Tablet{ 874 Alias: &topodatapb.TabletAlias{ 875 Cell: "zone1", 876 Uid: 100, 877 }, 878 }, 879 }, 880 shouldErr: true, 881 }, 882 { 883 name: "RPC pool full", 884 cluster: &Cluster{ 885 Vtctld: &fakevtctldclient.VtctldClient{}, 886 topoRWPool: pools.NewRPCPool(1, time.Millisecond*10, nil), 887 }, 888 timeout: time.Millisecond * 50, 889 setup: func(t testing.TB, c *Cluster) { 890 err := c.topoRWPool.Acquire(context.Background()) 891 require.NoError(t, err, "failed to lock RPC pool") 892 t.Cleanup(c.topoRWPool.Release) 893 }, 894 tablet: &vtadminpb.Tablet{ 895 Tablet: &topodatapb.Tablet{Alias: &topodatapb.TabletAlias{}}, 896 }, 897 shouldErr: true, 898 }, 899 } 900 901 for _, tt := range tests { 902 tt := tt 903 904 t.Run(tt.name, func(t *testing.T) { 905 t.Parallel() 906 907 tt.cluster.ID = testClusterProto.Id 908 tt.cluster.Name = testClusterProto.Name 909 910 if tt.setup != nil { 911 tt.setup(t, tt.cluster) 912 } 913 914 var ( 915 ctx context.Context 916 cancel context.CancelFunc 917 ) 918 919 switch tt.timeout { 920 case 0: 921 ctx, cancel = context.WithCancel(context.Background()) 922 default: 923 ctx, cancel = context.WithTimeout(context.Background(), tt.timeout) 924 } 925 defer cancel() 926 927 resp, err := tt.cluster.RefreshTabletReplicationSource(ctx, tt.tablet) 928 if tt.shouldErr { 929 assert.Error(t, err) 930 return 931 } 932 933 require.NoError(t, err) 934 utils.MustMatch(t, tt.expected, resp) 935 }) 936 } 937 } 938 939 func Test_reloadKeyspaceSchemas(t *testing.T) { 940 t.Parallel() 941 942 ctx := context.Background() 943 tests := []struct { 944 name string 945 cluster *Cluster 946 req *vtadminpb.ReloadSchemasRequest 947 expected []*vtadminpb.ReloadSchemasResponse_KeyspaceResult 948 shouldErr bool 949 }{ 950 { 951 name: "ok", 952 cluster: &Cluster{ 953 ID: "test", 954 Name: "test", 955 Vtctld: &fakevtctldclient.VtctldClient{ 956 GetKeyspacesResults: &struct { 957 Keyspaces []*vtctldatapb.Keyspace 958 Error error 959 }{ 960 Keyspaces: []*vtctldatapb.Keyspace{ 961 { 962 Name: "ks1", 963 }, 964 { 965 Name: "ks2", 966 }, 967 }, 968 }, 969 ReloadSchemaKeyspaceResults: map[string]struct { 970 Response *vtctldatapb.ReloadSchemaKeyspaceResponse 971 Error error 972 }{ 973 "ks1": { 974 Response: &vtctldatapb.ReloadSchemaKeyspaceResponse{ 975 Events: []*logutilpb.Event{ 976 {}, {}, {}, 977 }, 978 }, 979 }, 980 "ks2": { 981 Error: assert.AnError, // _would_ cause a failure but our request filters it out 982 }, 983 }, 984 }, 985 topoReadPool: pools.NewRPCPool(5, 0, nil), 986 }, 987 req: &vtadminpb.ReloadSchemasRequest{ 988 Keyspaces: []string{"ks1"}, 989 }, 990 expected: []*vtadminpb.ReloadSchemasResponse_KeyspaceResult{ 991 { 992 Keyspace: &vtadminpb.Keyspace{ 993 Cluster: &vtadminpb.Cluster{ 994 Id: "test", 995 Name: "test", 996 }, 997 Keyspace: &vtctldatapb.Keyspace{ 998 Name: "ks1", 999 }, 1000 }, 1001 Events: []*logutilpb.Event{ 1002 {}, {}, {}, 1003 }, 1004 }, 1005 }, 1006 }, 1007 { 1008 name: "no keyspaces specified defaults to all", 1009 cluster: &Cluster{ 1010 ID: "test", 1011 Name: "test", 1012 Vtctld: &fakevtctldclient.VtctldClient{ 1013 GetKeyspacesResults: &struct { 1014 Keyspaces []*vtctldatapb.Keyspace 1015 Error error 1016 }{ 1017 Keyspaces: []*vtctldatapb.Keyspace{ 1018 { 1019 Name: "ks1", 1020 }, 1021 { 1022 Name: "ks2", 1023 }, 1024 }, 1025 }, 1026 ReloadSchemaKeyspaceResults: map[string]struct { 1027 Response *vtctldatapb.ReloadSchemaKeyspaceResponse 1028 Error error 1029 }{ 1030 "ks1": { 1031 Response: &vtctldatapb.ReloadSchemaKeyspaceResponse{ 1032 Events: []*logutilpb.Event{ 1033 {}, 1034 }, 1035 }, 1036 }, 1037 "ks2": { 1038 Response: &vtctldatapb.ReloadSchemaKeyspaceResponse{ 1039 Events: []*logutilpb.Event{ 1040 {}, {}, 1041 }, 1042 }, 1043 }, 1044 }, 1045 }, 1046 topoReadPool: pools.NewRPCPool(5, 0, nil), 1047 }, 1048 req: &vtadminpb.ReloadSchemasRequest{}, 1049 expected: []*vtadminpb.ReloadSchemasResponse_KeyspaceResult{ 1050 { 1051 Keyspace: &vtadminpb.Keyspace{ 1052 Cluster: &vtadminpb.Cluster{ 1053 Id: "test", 1054 Name: "test", 1055 }, 1056 Keyspace: &vtctldatapb.Keyspace{ 1057 Name: "ks1", 1058 }, 1059 }, 1060 Events: []*logutilpb.Event{ 1061 {}, 1062 }, 1063 }, 1064 { 1065 Keyspace: &vtadminpb.Keyspace{ 1066 Cluster: &vtadminpb.Cluster{ 1067 Id: "test", 1068 Name: "test", 1069 }, 1070 Keyspace: &vtctldatapb.Keyspace{ 1071 Name: "ks2", 1072 }, 1073 }, 1074 Events: []*logutilpb.Event{ 1075 {}, {}, 1076 }, 1077 }, 1078 }, 1079 }, 1080 { 1081 name: "skip keyspaces not in cluster", 1082 cluster: &Cluster{ 1083 ID: "test", 1084 Name: "test", 1085 Vtctld: &fakevtctldclient.VtctldClient{ 1086 GetKeyspacesResults: &struct { 1087 Keyspaces []*vtctldatapb.Keyspace 1088 Error error 1089 }{ 1090 Keyspaces: []*vtctldatapb.Keyspace{ 1091 { 1092 Name: "ks1", 1093 }, 1094 }, 1095 }, 1096 ReloadSchemaKeyspaceResults: map[string]struct { 1097 Response *vtctldatapb.ReloadSchemaKeyspaceResponse 1098 Error error 1099 }{ 1100 "ks1": { 1101 Response: &vtctldatapb.ReloadSchemaKeyspaceResponse{ 1102 Events: []*logutilpb.Event{ 1103 {}, {}, {}, 1104 }, 1105 }, 1106 }, 1107 }, 1108 }, 1109 topoReadPool: pools.NewRPCPool(5, 0, nil), 1110 }, 1111 req: &vtadminpb.ReloadSchemasRequest{ 1112 Keyspaces: []string{"ks1", "anotherclusterks1"}, 1113 }, 1114 expected: []*vtadminpb.ReloadSchemasResponse_KeyspaceResult{ 1115 { 1116 Keyspace: &vtadminpb.Keyspace{ 1117 Cluster: &vtadminpb.Cluster{ 1118 Id: "test", 1119 Name: "test", 1120 }, 1121 Keyspace: &vtctldatapb.Keyspace{ 1122 Name: "ks1", 1123 }, 1124 }, 1125 Events: []*logutilpb.Event{ 1126 {}, {}, {}, 1127 }, 1128 }, 1129 }, 1130 }, 1131 { 1132 name: "GetKeyspaces error", 1133 cluster: &Cluster{ 1134 ID: "test", 1135 Name: "test", 1136 Vtctld: &fakevtctldclient.VtctldClient{ 1137 GetKeyspacesResults: &struct { 1138 Keyspaces []*vtctldatapb.Keyspace 1139 Error error 1140 }{ 1141 Error: assert.AnError, 1142 }, 1143 }, 1144 topoReadPool: pools.NewRPCPool(5, 0, nil), 1145 }, 1146 req: &vtadminpb.ReloadSchemasRequest{}, 1147 shouldErr: true, 1148 }, 1149 { 1150 name: "ReloadSchemaKeyspace error", 1151 cluster: &Cluster{ 1152 ID: "test", 1153 Name: "test", 1154 Vtctld: &fakevtctldclient.VtctldClient{ 1155 GetKeyspacesResults: &struct { 1156 Keyspaces []*vtctldatapb.Keyspace 1157 Error error 1158 }{ 1159 Keyspaces: []*vtctldatapb.Keyspace{ 1160 { 1161 Name: "ks1", 1162 }, 1163 { 1164 Name: "ks2", 1165 }, 1166 }, 1167 }, 1168 ReloadSchemaKeyspaceResults: map[string]struct { 1169 Response *vtctldatapb.ReloadSchemaKeyspaceResponse 1170 Error error 1171 }{ 1172 "ks1": { 1173 Response: &vtctldatapb.ReloadSchemaKeyspaceResponse{ 1174 Events: []*logutilpb.Event{ 1175 {}, {}, {}, 1176 }, 1177 }, 1178 }, 1179 "ks2": { 1180 Error: assert.AnError, 1181 }, 1182 }, 1183 }, 1184 topoReadPool: pools.NewRPCPool(5, 0, nil), 1185 }, 1186 req: &vtadminpb.ReloadSchemasRequest{}, 1187 shouldErr: true, 1188 }, 1189 } 1190 1191 for _, tt := range tests { 1192 tt := tt 1193 1194 t.Run(tt.name, func(t *testing.T) { 1195 t.Parallel() 1196 1197 results, err := tt.cluster.reloadKeyspaceSchemas(ctx, tt.req) 1198 if tt.shouldErr { 1199 assert.Error(t, err) 1200 return 1201 } 1202 1203 require.NoError(t, err) 1204 1205 sort.Slice(tt.expected, func(i, j int) bool { 1206 return tt.expected[i].Keyspace.Keyspace.Name < tt.expected[j].Keyspace.Keyspace.Name 1207 }) 1208 sort.Slice(results, func(i, j int) bool { 1209 return results[i].Keyspace.Keyspace.Name < results[j].Keyspace.Keyspace.Name 1210 }) 1211 utils.MustMatch(t, tt.expected, results) 1212 }) 1213 } 1214 } 1215 1216 func Test_reloadShardSchemas(t *testing.T) { 1217 t.Parallel() 1218 1219 ctx := context.Background() 1220 tests := []struct { 1221 name string 1222 cluster *Cluster 1223 req *vtadminpb.ReloadSchemasRequest 1224 expected []*vtadminpb.ReloadSchemasResponse_ShardResult 1225 shouldErr bool 1226 }{ 1227 { 1228 name: "ok", 1229 cluster: &Cluster{ 1230 ID: "test", 1231 Name: "test", 1232 Vtctld: &fakevtctldclient.VtctldClient{ 1233 FindAllShardsInKeyspaceResults: map[string]struct { 1234 Response *vtctldatapb.FindAllShardsInKeyspaceResponse 1235 Error error 1236 }{ 1237 "ks1": { 1238 Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{ 1239 Shards: map[string]*vtctldatapb.Shard{ 1240 "-": { 1241 Keyspace: "ks1", 1242 Name: "-", 1243 }, 1244 }, 1245 }, 1246 }, 1247 "ks2": { 1248 Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{ 1249 Shards: map[string]*vtctldatapb.Shard{ 1250 "-80": { 1251 Keyspace: "ks2", 1252 Name: "-80", 1253 }, 1254 "80-": { 1255 Keyspace: "ks2", 1256 Name: "80-", 1257 }, 1258 }, 1259 }, 1260 }, 1261 }, 1262 GetKeyspaceResults: map[string]struct { 1263 Response *vtctldatapb.GetKeyspaceResponse 1264 Error error 1265 }{ 1266 "ks1": { 1267 Response: &vtctldatapb.GetKeyspaceResponse{ 1268 Keyspace: &vtctldatapb.Keyspace{ 1269 Name: "ks1", 1270 }, 1271 }, 1272 }, 1273 "ks2": { 1274 Response: &vtctldatapb.GetKeyspaceResponse{ 1275 Keyspace: &vtctldatapb.Keyspace{ 1276 Name: "ks2", 1277 }, 1278 }, 1279 }, 1280 }, 1281 GetKeyspacesResults: &struct { 1282 Keyspaces []*vtctldatapb.Keyspace 1283 Error error 1284 }{ 1285 Keyspaces: []*vtctldatapb.Keyspace{ 1286 { 1287 Name: "ks1", 1288 }, 1289 { 1290 Name: "ks2", 1291 }, 1292 }, 1293 }, 1294 ReloadSchemaShardResults: map[string]struct { 1295 Response *vtctldatapb.ReloadSchemaShardResponse 1296 Error error 1297 }{ 1298 "ks1/-": { 1299 Response: &vtctldatapb.ReloadSchemaShardResponse{ 1300 Events: []*logutilpb.Event{ 1301 {}, 1302 }, 1303 }, 1304 }, 1305 "ks2/-80": { 1306 Response: &vtctldatapb.ReloadSchemaShardResponse{ 1307 Events: []*logutilpb.Event{ 1308 {}, {}, 1309 }, 1310 }, 1311 }, 1312 "ks2/80-": { // skipped via request params 1313 Error: assert.AnError, 1314 }, 1315 }, 1316 }, 1317 topoReadPool: pools.NewRPCPool(5, 0, nil), 1318 }, 1319 req: &vtadminpb.ReloadSchemasRequest{ 1320 KeyspaceShards: []string{"ks1/-", "ks2/-80"}, 1321 }, 1322 expected: []*vtadminpb.ReloadSchemasResponse_ShardResult{ 1323 { 1324 Shard: &vtadminpb.Shard{ 1325 Cluster: &vtadminpb.Cluster{ 1326 Id: "test", 1327 Name: "test", 1328 }, 1329 Shard: &vtctldatapb.Shard{ 1330 Keyspace: "ks1", 1331 Name: "-", 1332 }, 1333 }, 1334 Events: []*logutilpb.Event{ 1335 {}, 1336 }, 1337 }, 1338 { 1339 Shard: &vtadminpb.Shard{ 1340 Cluster: &vtadminpb.Cluster{ 1341 Id: "test", 1342 Name: "test", 1343 }, 1344 Shard: &vtctldatapb.Shard{ 1345 Keyspace: "ks2", 1346 Name: "-80", 1347 }, 1348 }, 1349 Events: []*logutilpb.Event{ 1350 {}, {}, 1351 }, 1352 }, 1353 }, 1354 }, 1355 { 1356 name: "one shard fails", 1357 cluster: &Cluster{ 1358 ID: "test", 1359 Name: "test", 1360 Vtctld: &fakevtctldclient.VtctldClient{ 1361 FindAllShardsInKeyspaceResults: map[string]struct { 1362 Response *vtctldatapb.FindAllShardsInKeyspaceResponse 1363 Error error 1364 }{ 1365 "ks1": { 1366 Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{ 1367 Shards: map[string]*vtctldatapb.Shard{ 1368 "-": { 1369 Keyspace: "ks1", 1370 Name: "-", 1371 }, 1372 }, 1373 }, 1374 }, 1375 "ks2": { 1376 Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{ 1377 Shards: map[string]*vtctldatapb.Shard{ 1378 "-80": { 1379 Keyspace: "ks2", 1380 Name: "-80", 1381 }, 1382 "80-": { 1383 Keyspace: "ks2", 1384 Name: "80-", 1385 }, 1386 }, 1387 }, 1388 }, 1389 }, 1390 GetKeyspaceResults: map[string]struct { 1391 Response *vtctldatapb.GetKeyspaceResponse 1392 Error error 1393 }{ 1394 "ks1": { 1395 Response: &vtctldatapb.GetKeyspaceResponse{ 1396 Keyspace: &vtctldatapb.Keyspace{ 1397 Name: "ks1", 1398 }, 1399 }, 1400 }, 1401 "ks2": { 1402 Response: &vtctldatapb.GetKeyspaceResponse{ 1403 Keyspace: &vtctldatapb.Keyspace{ 1404 Name: "ks2", 1405 }, 1406 }, 1407 }, 1408 }, 1409 GetKeyspacesResults: &struct { 1410 Keyspaces []*vtctldatapb.Keyspace 1411 Error error 1412 }{ 1413 Keyspaces: []*vtctldatapb.Keyspace{ 1414 { 1415 Name: "ks1", 1416 }, 1417 { 1418 Name: "ks2", 1419 }, 1420 }, 1421 }, 1422 ReloadSchemaShardResults: map[string]struct { 1423 Response *vtctldatapb.ReloadSchemaShardResponse 1424 Error error 1425 }{ 1426 "ks1/-": { 1427 Response: &vtctldatapb.ReloadSchemaShardResponse{ 1428 Events: []*logutilpb.Event{ 1429 {}, 1430 }, 1431 }, 1432 }, 1433 "ks2/-80": { 1434 Response: &vtctldatapb.ReloadSchemaShardResponse{ 1435 Events: []*logutilpb.Event{ 1436 {}, {}, 1437 }, 1438 }, 1439 }, 1440 "ks2/80-": { 1441 Error: assert.AnError, 1442 }, 1443 }, 1444 }, 1445 topoReadPool: pools.NewRPCPool(5, 0, nil), 1446 }, 1447 req: &vtadminpb.ReloadSchemasRequest{ 1448 KeyspaceShards: []string{"ks1/-", "ks2/-80", "ks2/80-"}, 1449 }, 1450 shouldErr: true, 1451 }, 1452 { 1453 name: "getShardSets failure", 1454 cluster: &Cluster{ 1455 ID: "test", 1456 Name: "test", 1457 Vtctld: &fakevtctldclient.VtctldClient{ 1458 GetKeyspaceResults: map[string]struct { 1459 Response *vtctldatapb.GetKeyspaceResponse 1460 Error error 1461 }{ 1462 "ks1": { 1463 Error: assert.AnError, 1464 }, 1465 "ks2": { 1466 Response: &vtctldatapb.GetKeyspaceResponse{ 1467 Keyspace: &vtctldatapb.Keyspace{ 1468 Name: "ks2", 1469 }, 1470 }, 1471 }, 1472 }, 1473 GetKeyspacesResults: &struct { 1474 Keyspaces []*vtctldatapb.Keyspace 1475 Error error 1476 }{ 1477 Keyspaces: []*vtctldatapb.Keyspace{ 1478 { 1479 Name: "ks1", 1480 }, 1481 { 1482 Name: "ks2", 1483 }, 1484 }, 1485 }, 1486 ReloadSchemaShardResults: map[string]struct { 1487 Response *vtctldatapb.ReloadSchemaShardResponse 1488 Error error 1489 }{ 1490 "ks1/-": { 1491 Response: &vtctldatapb.ReloadSchemaShardResponse{ 1492 Events: []*logutilpb.Event{ 1493 {}, 1494 }, 1495 }, 1496 }, 1497 "ks2/-80": { 1498 Response: &vtctldatapb.ReloadSchemaShardResponse{ 1499 Events: []*logutilpb.Event{ 1500 {}, {}, 1501 }, 1502 }, 1503 }, 1504 "ks2/80-": { // skipped via request params 1505 Error: assert.AnError, 1506 }, 1507 }, 1508 }, 1509 topoReadPool: pools.NewRPCPool(5, 0, nil), 1510 }, 1511 req: &vtadminpb.ReloadSchemasRequest{ 1512 KeyspaceShards: []string{"ks1/-"}, 1513 }, 1514 shouldErr: true, 1515 }, 1516 } 1517 1518 for _, tt := range tests { 1519 tt := tt 1520 1521 t.Run(tt.name, func(t *testing.T) { 1522 t.Parallel() 1523 1524 results, err := tt.cluster.reloadShardSchemas(ctx, tt.req) 1525 if tt.shouldErr { 1526 assert.Error(t, err) 1527 return 1528 } 1529 1530 require.NoError(t, err) 1531 1532 keyFn := func(shard *vtadminpb.Shard) string { 1533 return fmt.Sprintf("%s/%s", shard.Shard.Keyspace, shard.Shard.Name) 1534 } 1535 sort.Slice(tt.expected, func(i, j int) bool { 1536 return keyFn(tt.expected[i].Shard) < keyFn(tt.expected[j].Shard) 1537 }) 1538 sort.Slice(results, func(i, j int) bool { 1539 return keyFn(results[i].Shard) < keyFn(results[j].Shard) 1540 }) 1541 utils.MustMatch(t, tt.expected, results) 1542 }) 1543 } 1544 } 1545 1546 func Test_reloadTabletSchemas(t *testing.T) { 1547 t.Parallel() 1548 1549 ctx := context.Background() 1550 tests := []struct { 1551 name string 1552 cluster *Cluster 1553 tablets []*vtadminpb.Tablet 1554 dbErr bool 1555 req *vtadminpb.ReloadSchemasRequest 1556 expected []*vtadminpb.ReloadSchemasResponse_TabletResult 1557 shouldErr bool 1558 }{ 1559 { 1560 name: "ok", 1561 cluster: &Cluster{ 1562 ID: "test", 1563 Name: "test", 1564 Vtctld: &fakevtctldclient.VtctldClient{ 1565 ReloadSchemaResults: map[string]struct { 1566 Response *vtctldatapb.ReloadSchemaResponse 1567 Error error 1568 }{ 1569 "zone1-0000000100": { 1570 Response: &vtctldatapb.ReloadSchemaResponse{}, 1571 }, 1572 "zone1-0000000101": { 1573 Response: &vtctldatapb.ReloadSchemaResponse{}, 1574 }, 1575 "zone2-0000000200": { 1576 Response: &vtctldatapb.ReloadSchemaResponse{}, 1577 }, 1578 "zone5-0000000500": { 1579 Error: assert.AnError, 1580 }, 1581 }, 1582 }, 1583 }, 1584 tablets: []*vtadminpb.Tablet{ 1585 { 1586 Tablet: &topodatapb.Tablet{ 1587 Alias: &topodatapb.TabletAlias{ 1588 Cell: "zone1", 1589 Uid: 100, 1590 }, 1591 }, 1592 }, 1593 { 1594 Tablet: &topodatapb.Tablet{ 1595 Alias: &topodatapb.TabletAlias{ 1596 Cell: "zone1", 1597 Uid: 101, 1598 }, 1599 }, 1600 }, 1601 { 1602 Tablet: &topodatapb.Tablet{ 1603 Alias: &topodatapb.TabletAlias{ 1604 Cell: "zone2", 1605 Uid: 200, 1606 }, 1607 }, 1608 }, 1609 { 1610 Tablet: &topodatapb.Tablet{ 1611 Alias: &topodatapb.TabletAlias{ 1612 Cell: "zone5", 1613 Uid: 500, 1614 }, 1615 }, 1616 }, 1617 }, 1618 req: &vtadminpb.ReloadSchemasRequest{ 1619 Tablets: []*topodatapb.TabletAlias{ 1620 {Cell: "zone1", Uid: 100}, 1621 {Cell: "zone1", Uid: 101}, 1622 {Cell: "zone2", Uid: 200}, 1623 {Cell: "zone5", Uid: 500}, 1624 }, 1625 }, 1626 expected: []*vtadminpb.ReloadSchemasResponse_TabletResult{ 1627 { 1628 Tablet: &vtadminpb.Tablet{ 1629 Cluster: &vtadminpb.Cluster{ 1630 Id: "test", 1631 Name: "test", 1632 }, 1633 Tablet: &topodatapb.Tablet{ 1634 Alias: &topodatapb.TabletAlias{ 1635 Cell: "zone1", 1636 Uid: 100, 1637 }, 1638 }, 1639 }, 1640 Result: "ok", 1641 }, 1642 { 1643 Tablet: &vtadminpb.Tablet{ 1644 Cluster: &vtadminpb.Cluster{ 1645 Id: "test", 1646 Name: "test", 1647 }, 1648 Tablet: &topodatapb.Tablet{ 1649 Alias: &topodatapb.TabletAlias{ 1650 Cell: "zone1", 1651 Uid: 101, 1652 }, 1653 }, 1654 }, 1655 Result: "ok", 1656 }, 1657 { 1658 Tablet: &vtadminpb.Tablet{ 1659 Cluster: &vtadminpb.Cluster{ 1660 Id: "test", 1661 Name: "test", 1662 }, 1663 Tablet: &topodatapb.Tablet{ 1664 Alias: &topodatapb.TabletAlias{ 1665 Cell: "zone2", 1666 Uid: 200, 1667 }, 1668 }, 1669 }, 1670 Result: "ok", 1671 }, 1672 { 1673 Tablet: &vtadminpb.Tablet{ 1674 Cluster: &vtadminpb.Cluster{ 1675 Id: "test", 1676 Name: "test", 1677 }, 1678 Tablet: &topodatapb.Tablet{ 1679 Alias: &topodatapb.TabletAlias{ 1680 Cell: "zone5", 1681 Uid: 500, 1682 }, 1683 }, 1684 }, 1685 Result: assert.AnError.Error(), 1686 }, 1687 }, 1688 }, 1689 { 1690 name: "FindTablets error", 1691 cluster: &Cluster{}, 1692 dbErr: true, 1693 req: &vtadminpb.ReloadSchemasRequest{ 1694 Tablets: []*topodatapb.TabletAlias{ 1695 {Cell: "zone1", Uid: 100}, 1696 }, 1697 }, 1698 shouldErr: true, 1699 }, 1700 } 1701 1702 for _, tt := range tests { 1703 tt := tt 1704 1705 t.Run(tt.name, func(t *testing.T) { 1706 t.Parallel() 1707 1708 cfg := vtsql.WithDialFunc(func(c vitessdriver.Configuration) (*sql.DB, error) { 1709 return sql.OpenDB(&fakevtsql.Connector{ 1710 Tablets: tt.tablets, 1711 ShouldErr: tt.dbErr, 1712 }), nil 1713 })(&vtsql.Config{ 1714 Cluster: tt.cluster.ToProto(), 1715 ResolverOptions: &resolver.Options{}, 1716 }) 1717 db, err := vtsql.New(ctx, cfg) 1718 require.NoError(t, err) 1719 defer db.Close() 1720 1721 tt.cluster.DB = db 1722 1723 results, err := tt.cluster.reloadTabletSchemas(ctx, tt.req) 1724 if tt.shouldErr { 1725 assert.Error(t, err) 1726 return 1727 } 1728 1729 require.NoError(t, err) 1730 1731 sort.Slice(tt.expected, func(i, j int) bool { 1732 return topoproto.TabletAliasString(tt.expected[i].Tablet.Tablet.Alias) < topoproto.TabletAliasString(tt.expected[j].Tablet.Tablet.Alias) 1733 }) 1734 sort.Slice(results, func(i, j int) bool { 1735 return topoproto.TabletAliasString(results[i].Tablet.Tablet.Alias) < topoproto.TabletAliasString(results[j].Tablet.Tablet.Alias) 1736 }) 1737 utils.MustMatch(t, tt.expected, results) 1738 }) 1739 } 1740 } 1741 1742 func TestTabletExternallyPromoted(t *testing.T) { 1743 t.Parallel() 1744 1745 testClusterProto := &vtadminpb.Cluster{ 1746 Id: "test", 1747 Name: "test", 1748 } 1749 1750 tests := []struct { 1751 name string 1752 cluster *Cluster 1753 setup func(t testing.TB, c *Cluster) 1754 timeout time.Duration 1755 tablet *vtadminpb.Tablet 1756 expected *vtadminpb.TabletExternallyPromotedResponse 1757 shouldErr bool 1758 }{ 1759 { 1760 name: "ok", 1761 cluster: &Cluster{ 1762 ID: "test", 1763 Name: "test", 1764 Vtctld: &fakevtctldclient.VtctldClient{ 1765 TabletExternallyReparentedResults: map[string]struct { 1766 Response *vtctldatapb.TabletExternallyReparentedResponse 1767 Error error 1768 }{ 1769 "zone1-0000000100": { 1770 Response: &vtctldatapb.TabletExternallyReparentedResponse{ 1771 Keyspace: "ks1", 1772 Shard: "-", 1773 NewPrimary: &topodatapb.TabletAlias{ 1774 Cell: "zone1", 1775 Uid: 100, 1776 }, 1777 OldPrimary: &topodatapb.TabletAlias{ 1778 Cell: "zone1", 1779 Uid: 200, 1780 }, 1781 }, 1782 }, 1783 "zone1-0000000200": { 1784 Error: fmt.Errorf("some error: %w", assert.AnError), 1785 }, 1786 }, 1787 }, 1788 topoRWPool: pools.NewRPCPool(1, time.Second, nil), 1789 }, 1790 tablet: &vtadminpb.Tablet{ 1791 Tablet: &topodatapb.Tablet{ 1792 Alias: &topodatapb.TabletAlias{ 1793 Cell: "zone1", 1794 Uid: 100, 1795 }, 1796 }, 1797 }, 1798 expected: &vtadminpb.TabletExternallyPromotedResponse{ 1799 Cluster: testClusterProto, 1800 Keyspace: "ks1", 1801 Shard: "-", 1802 NewPrimary: &topodatapb.TabletAlias{ 1803 Cell: "zone1", 1804 Uid: 100, 1805 }, 1806 OldPrimary: &topodatapb.TabletAlias{ 1807 Cell: "zone1", 1808 Uid: 200, 1809 }, 1810 }, 1811 }, 1812 { 1813 name: "error", 1814 cluster: &Cluster{ 1815 ID: "test", 1816 Name: "test", 1817 Vtctld: &fakevtctldclient.VtctldClient{ 1818 TabletExternallyReparentedResults: map[string]struct { 1819 Response *vtctldatapb.TabletExternallyReparentedResponse 1820 Error error 1821 }{ 1822 "zone1-0000000100": { 1823 Response: &vtctldatapb.TabletExternallyReparentedResponse{ 1824 Keyspace: "ks1", 1825 Shard: "-", 1826 NewPrimary: &topodatapb.TabletAlias{ 1827 Cell: "zone1", 1828 Uid: 100, 1829 }, 1830 OldPrimary: &topodatapb.TabletAlias{ 1831 Cell: "zone1", 1832 Uid: 200, 1833 }, 1834 }, 1835 }, 1836 "zone1-0000000200": { 1837 Error: fmt.Errorf("some error: %w", assert.AnError), 1838 }, 1839 }, 1840 }, 1841 topoRWPool: pools.NewRPCPool(1, time.Second, nil), 1842 }, 1843 tablet: &vtadminpb.Tablet{ 1844 Tablet: &topodatapb.Tablet{ 1845 Alias: &topodatapb.TabletAlias{ 1846 Cell: "zone1", 1847 Uid: 200, 1848 }, 1849 }, 1850 }, 1851 shouldErr: true, 1852 }, 1853 { 1854 name: "pool full", 1855 cluster: &Cluster{ 1856 ID: "test", 1857 Name: "test", 1858 Vtctld: &fakevtctldclient.VtctldClient{ 1859 TabletExternallyReparentedResults: map[string]struct { 1860 Response *vtctldatapb.TabletExternallyReparentedResponse 1861 Error error 1862 }{ 1863 "zone1-0000000100": { 1864 Response: &vtctldatapb.TabletExternallyReparentedResponse{ 1865 Keyspace: "ks1", 1866 Shard: "-", 1867 NewPrimary: &topodatapb.TabletAlias{ 1868 Cell: "zone1", 1869 Uid: 100, 1870 }, 1871 OldPrimary: &topodatapb.TabletAlias{ 1872 Cell: "zone1", 1873 Uid: 200, 1874 }, 1875 }, 1876 }, 1877 "zone1-0000000200": { 1878 Error: fmt.Errorf("some error: %w", assert.AnError), 1879 }, 1880 }, 1881 }, 1882 topoRWPool: pools.NewRPCPool(1, time.Millisecond*25, nil), 1883 }, 1884 setup: func(t testing.TB, c *Cluster) { 1885 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50) 1886 defer cancel() 1887 1888 err := c.topoRWPool.Acquire(ctx) 1889 require.NoError(t, err, "could not block topoRW pool in setup") 1890 t.Cleanup(c.topoRWPool.Release) 1891 }, 1892 timeout: time.Millisecond * 50, 1893 tablet: &vtadminpb.Tablet{ 1894 Tablet: &topodatapb.Tablet{ 1895 Alias: &topodatapb.TabletAlias{ 1896 Cell: "zone1", 1897 Uid: 100, 1898 }, 1899 }, 1900 }, 1901 shouldErr: true, 1902 }, 1903 } 1904 1905 for _, tt := range tests { 1906 tt := tt 1907 1908 t.Run(tt.name, func(t *testing.T) { 1909 t.Parallel() 1910 1911 if tt.setup != nil { 1912 func(t *testing.T) { 1913 t.Helper() 1914 tt.setup(t, tt.cluster) 1915 }(t) 1916 } 1917 1918 var ( 1919 ctx context.Context 1920 cancel context.CancelFunc 1921 ) 1922 switch tt.timeout { 1923 case 0: 1924 ctx, cancel = context.WithCancel(context.Background()) 1925 default: 1926 ctx, cancel = context.WithTimeout(context.Background(), tt.timeout) 1927 } 1928 defer cancel() 1929 1930 resp, err := tt.cluster.TabletExternallyPromoted(ctx, tt.tablet) 1931 if tt.shouldErr { 1932 assert.Error(t, err) 1933 return 1934 } 1935 1936 require.NoError(t, err) 1937 utils.MustMatch(t, tt.expected, resp) 1938 }) 1939 } 1940 }