vitess.io/vitess@v0.16.2/go/vt/vtadmin/cluster/cluster_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_test 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "sort" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/stretchr/testify/assert" 29 "github.com/stretchr/testify/require" 30 "google.golang.org/protobuf/proto" 31 "k8s.io/apimachinery/pkg/util/sets" 32 33 "vitess.io/vitess/go/mysql" 34 "vitess.io/vitess/go/protoutil" 35 "vitess.io/vitess/go/test/utils" 36 "vitess.io/vitess/go/vt/topo" 37 "vitess.io/vitess/go/vt/vtadmin/cluster" 38 vtadminerrors "vitess.io/vitess/go/vt/vtadmin/errors" 39 "vitess.io/vitess/go/vt/vtadmin/testutil" 40 "vitess.io/vitess/go/vt/vtadmin/vtctldclient/fakevtctldclient" 41 "vitess.io/vitess/go/vt/vtctl/vtctldclient" 42 43 replicationdatapb "vitess.io/vitess/go/vt/proto/replicationdata" 44 tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" 45 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 46 vschemapb "vitess.io/vitess/go/vt/proto/vschema" 47 vtadminpb "vitess.io/vitess/go/vt/proto/vtadmin" 48 vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" 49 ) 50 51 func TestCreateKeyspace(t *testing.T) { 52 t.Parallel() 53 54 ctx := context.Background() 55 tests := []struct { 56 name string 57 cfg testutil.TestClusterConfig 58 req *vtctldatapb.CreateKeyspaceRequest 59 expected *vtadminpb.Keyspace 60 shouldErr bool 61 }{ 62 { 63 name: "success", 64 cfg: testutil.TestClusterConfig{ 65 Cluster: &vtadminpb.Cluster{ 66 Id: "c1", 67 Name: "cluster1", 68 }, 69 VtctldClient: &fakevtctldclient.VtctldClient{}, 70 }, 71 req: &vtctldatapb.CreateKeyspaceRequest{ 72 Name: "testkeyspace", 73 }, 74 expected: &vtadminpb.Keyspace{ 75 Cluster: &vtadminpb.Cluster{ 76 Id: "c1", 77 Name: "cluster1", 78 }, 79 Keyspace: &vtctldatapb.Keyspace{ 80 Name: "testkeyspace", 81 Keyspace: &topodatapb.Keyspace{}, 82 }, 83 Shards: map[string]*vtctldatapb.Shard{}, 84 }, 85 }, 86 { 87 name: "snapshot", 88 cfg: testutil.TestClusterConfig{ 89 Cluster: &vtadminpb.Cluster{ 90 Id: "c1", 91 Name: "cluster1", 92 }, 93 VtctldClient: &fakevtctldclient.VtctldClient{}, 94 }, 95 req: &vtctldatapb.CreateKeyspaceRequest{ 96 Name: "testkeyspace_snapshot", 97 Type: topodatapb.KeyspaceType_SNAPSHOT, 98 BaseKeyspace: "testkeyspace", 99 SnapshotTime: protoutil.TimeToProto(time.Date(2006, time.January, 2, 3, 4, 5, 0, time.UTC)), 100 }, 101 expected: &vtadminpb.Keyspace{ 102 Cluster: &vtadminpb.Cluster{ 103 Id: "c1", 104 Name: "cluster1", 105 }, 106 Keyspace: &vtctldatapb.Keyspace{ 107 Name: "testkeyspace_snapshot", 108 Keyspace: &topodatapb.Keyspace{ 109 KeyspaceType: topodatapb.KeyspaceType_SNAPSHOT, 110 BaseKeyspace: "testkeyspace", 111 SnapshotTime: protoutil.TimeToProto(time.Date(2006, time.January, 2, 3, 4, 5, 0, time.UTC)), 112 }, 113 }, 114 Shards: map[string]*vtctldatapb.Shard{}, 115 }, 116 }, 117 { 118 name: "nil request", 119 cfg: testutil.TestClusterConfig{ 120 Cluster: &vtadminpb.Cluster{ 121 Id: "c1", 122 Name: "cluster1", 123 }, 124 VtctldClient: &fakevtctldclient.VtctldClient{}, 125 }, 126 req: nil, 127 shouldErr: true, 128 }, 129 { 130 name: "missing name", 131 cfg: testutil.TestClusterConfig{ 132 Cluster: &vtadminpb.Cluster{ 133 Id: "c1", 134 Name: "cluster1", 135 }, 136 VtctldClient: &fakevtctldclient.VtctldClient{}, 137 }, 138 req: &vtctldatapb.CreateKeyspaceRequest{}, 139 shouldErr: true, 140 }, 141 { 142 name: "failure", 143 cfg: testutil.TestClusterConfig{ 144 Cluster: &vtadminpb.Cluster{ 145 Id: "c1", 146 Name: "cluster1", 147 }, 148 VtctldClient: &fakevtctldclient.VtctldClient{ 149 CreateKeyspaceShouldErr: true, 150 }, 151 }, 152 req: &vtctldatapb.CreateKeyspaceRequest{ 153 Name: "testkeyspace", 154 }, 155 shouldErr: true, 156 }, 157 } 158 159 for _, tt := range tests { 160 tt := tt 161 t.Run(tt.name, func(t *testing.T) { 162 t.Parallel() 163 164 cluster := testutil.BuildCluster(t, tt.cfg) 165 166 resp, err := cluster.CreateKeyspace(ctx, tt.req) 167 if tt.shouldErr { 168 assert.Error(t, err) 169 return 170 } 171 172 assert.NoError(t, err) 173 assert.Equal(t, tt.expected, resp) 174 }) 175 } 176 } 177 178 func TestCreateShard(t *testing.T) { 179 t.Parallel() 180 181 type test struct { 182 name string 183 tc *testutil.IntegrationTestCluster 184 req *vtctldatapb.CreateShardRequest 185 shouldErr bool 186 assertion func(t *testing.T, tt *test) 187 } 188 ctx := context.Background() 189 tests := []*test{ 190 { 191 name: "ok", 192 tc: testutil.BuildIntegrationTestCluster(t, &vtadminpb.Cluster{ 193 Id: "local", 194 Name: "local", 195 }, "zone1"), 196 req: &vtctldatapb.CreateShardRequest{ 197 Keyspace: "ks1", 198 ShardName: "-", 199 IncludeParent: true, 200 }, 201 assertion: func(t *testing.T, tt *test) { 202 shard, err := tt.tc.Topo.GetShard(ctx, "ks1", "-") 203 require.NoError(t, err, "topo.GetShard(ks1/-) failed") 204 205 utils.MustMatch(t, &topodatapb.Shard{ 206 KeyRange: &topodatapb.KeyRange{}, 207 IsPrimaryServing: true, 208 }, shard.Shard) 209 }, 210 }, 211 { 212 name: "nil request", 213 tc: testutil.BuildIntegrationTestCluster(t, &vtadminpb.Cluster{ 214 Id: "local", 215 Name: "local", 216 }, "zone1"), 217 req: nil, 218 shouldErr: true, 219 }, 220 { 221 name: "no keyspace in request", 222 tc: testutil.BuildIntegrationTestCluster(t, &vtadminpb.Cluster{ 223 Id: "local", 224 Name: "local", 225 }, "zone1"), 226 req: &vtctldatapb.CreateShardRequest{ 227 Keyspace: "", 228 ShardName: "-", 229 }, 230 shouldErr: true, 231 }, 232 { 233 name: "no shard name in request", 234 tc: testutil.BuildIntegrationTestCluster(t, &vtadminpb.Cluster{ 235 Id: "local", 236 Name: "local", 237 }, "zone1"), 238 req: &vtctldatapb.CreateShardRequest{ 239 Keyspace: "ks1", 240 ShardName: "", 241 }, 242 shouldErr: true, 243 }, 244 { 245 name: "vtctld.CreateShard fails", 246 tc: testutil.BuildIntegrationTestCluster(t, &vtadminpb.Cluster{ 247 Id: "local", 248 Name: "local", 249 }, "zone1"), 250 req: &vtctldatapb.CreateShardRequest{ 251 Keyspace: "ks1", // because IncludeParent=false and ks1 does not exist, we fail 252 ShardName: "-", 253 }, 254 shouldErr: true, 255 }, 256 } 257 258 for _, tt := range tests { 259 tt := tt 260 t.Run(tt.name, func(t *testing.T) { 261 _, err := tt.tc.Cluster.CreateShard(ctx, tt.req) 262 if tt.shouldErr { 263 assert.Error(t, err) 264 } else { 265 require.NoError(t, err) 266 } 267 268 if tt.assertion != nil { 269 func() { 270 t.Helper() 271 tt.assertion(t, tt) 272 }() 273 } 274 }) 275 } 276 } 277 278 func TestDeleteKeyspace(t *testing.T) { 279 t.Parallel() 280 281 ctx := context.Background() 282 tests := []struct { 283 name string 284 cfg testutil.TestClusterConfig 285 req *vtctldatapb.DeleteKeyspaceRequest 286 expected *vtctldatapb.DeleteKeyspaceResponse 287 shouldErr bool 288 }{ 289 { 290 name: "success", 291 cfg: testutil.TestClusterConfig{ 292 Cluster: &vtadminpb.Cluster{ 293 Id: "c1", 294 Name: "cluster1", 295 }, 296 VtctldClient: &fakevtctldclient.VtctldClient{}, 297 }, 298 req: &vtctldatapb.DeleteKeyspaceRequest{ 299 Keyspace: "ks1", 300 }, 301 expected: &vtctldatapb.DeleteKeyspaceResponse{}, 302 }, 303 { 304 name: "nil request", 305 cfg: testutil.TestClusterConfig{ 306 Cluster: &vtadminpb.Cluster{ 307 Id: "c1", 308 Name: "cluster1", 309 }, 310 VtctldClient: &fakevtctldclient.VtctldClient{}, 311 }, 312 req: nil, 313 shouldErr: true, 314 }, 315 { 316 name: "missing name", 317 cfg: testutil.TestClusterConfig{ 318 Cluster: &vtadminpb.Cluster{ 319 Id: "c1", 320 Name: "cluster1", 321 }, 322 VtctldClient: &fakevtctldclient.VtctldClient{}, 323 }, 324 req: &vtctldatapb.DeleteKeyspaceRequest{}, 325 shouldErr: true, 326 }, 327 { 328 name: "failure", 329 cfg: testutil.TestClusterConfig{ 330 Cluster: &vtadminpb.Cluster{ 331 Id: "c1", 332 Name: "cluster1", 333 }, 334 VtctldClient: &fakevtctldclient.VtctldClient{ 335 DeleteKeyspaceShouldErr: true, 336 }, 337 }, 338 req: &vtctldatapb.DeleteKeyspaceRequest{ 339 Keyspace: "ks1", 340 }, 341 shouldErr: true, 342 }, 343 } 344 345 for _, tt := range tests { 346 tt := tt 347 t.Run(tt.name, func(t *testing.T) { 348 t.Parallel() 349 350 cluster := testutil.BuildCluster(t, tt.cfg) 351 352 resp, err := cluster.DeleteKeyspace(ctx, tt.req) 353 if tt.shouldErr { 354 assert.Error(t, err) 355 return 356 } 357 358 assert.NoError(t, err) 359 assert.Equal(t, tt.expected, resp) 360 }) 361 } 362 } 363 364 func TestDeleteShards(t *testing.T) { 365 t.Parallel() 366 367 type test struct { 368 name string 369 tc *testutil.IntegrationTestCluster 370 setup func(t *testing.T, tt *test) 371 req *vtctldatapb.DeleteShardsRequest 372 shouldErr bool 373 assertion func(t *testing.T, tt *test) 374 } 375 ctx := context.Background() 376 tests := []*test{ 377 { 378 name: "ok", 379 tc: testutil.BuildIntegrationTestCluster(t, &vtadminpb.Cluster{ 380 Id: "local", 381 Name: "local", 382 }, "zone1"), 383 setup: func(t *testing.T, tt *test) { 384 ctx := context.Background() 385 shards := []string{"-80", "80-"} 386 for _, shard := range shards { 387 _, err := tt.tc.Cluster.CreateShard(ctx, &vtctldatapb.CreateShardRequest{ 388 Keyspace: "ks1", 389 ShardName: shard, 390 IncludeParent: true, 391 Force: true, 392 }) 393 require.NoError(t, err) 394 } 395 }, 396 req: &vtctldatapb.DeleteShardsRequest{ 397 Shards: []*vtctldatapb.Shard{ 398 { 399 Keyspace: "ks1", 400 Name: "80-", 401 }, 402 }, 403 }, 404 assertion: func(t *testing.T, tt *test) { 405 shard, err := tt.tc.Topo.GetShard(ctx, "ks1", "-80") 406 require.NoError(t, err, "topo.GetShard(ks1/-80) failed") 407 408 utils.MustMatch(t, &topodatapb.Shard{ 409 KeyRange: &topodatapb.KeyRange{ 410 End: []byte{0x80}, 411 }, 412 IsPrimaryServing: true, 413 }, shard.Shard) 414 415 shard2, err2 := tt.tc.Topo.GetShard(ctx, "ks1", "80-") 416 assert.True(t, topo.IsErrType(err2, topo.NoNode), "expected ks1/80- to be deleted, found %+v", shard2) 417 }, 418 }, 419 { 420 name: "nil request", 421 tc: testutil.BuildIntegrationTestCluster(t, &vtadminpb.Cluster{ 422 Id: "local", 423 Name: "local", 424 }, "zone1"), 425 req: nil, 426 shouldErr: true, 427 }, 428 { 429 name: "vtctld.DeleteShards fails", 430 tc: testutil.BuildIntegrationTestCluster(t, &vtadminpb.Cluster{ 431 Id: "local", 432 Name: "local", 433 }, "zone1"), 434 setup: func(t *testing.T, tt *test) { 435 _, err := tt.tc.Cluster.Vtctld.CreateShard(ctx, &vtctldatapb.CreateShardRequest{ 436 Keyspace: "ks1", 437 ShardName: "-", 438 IncludeParent: true, 439 }) 440 require.NoError(t, err, "CreateShard(ks1/-) failed") 441 442 srvks := &topodatapb.SrvKeyspace{ 443 Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ 444 { 445 ServedType: topodatapb.TabletType_PRIMARY, 446 ShardReferences: []*topodatapb.ShardReference{ 447 { 448 Name: "-", 449 }, 450 }, 451 }, 452 }, 453 } 454 err = tt.tc.Topo.UpdateSrvKeyspace(ctx, "zone1", "ks1", srvks) 455 require.NoError(t, err, "UpdateSrvKeyspace(zone1, ks1, %+v) failed", srvks) 456 }, 457 req: &vtctldatapb.DeleteShardsRequest{ 458 Shards: []*vtctldatapb.Shard{ 459 { 460 Keyspace: "ks1", 461 Name: "-", 462 }, 463 }, 464 }, 465 shouldErr: true, 466 assertion: func(t *testing.T, tt *test) { 467 shard, err := tt.tc.Topo.GetShard(ctx, "ks1", "-") 468 require.NoError(t, err, "GetShard(ks1/-) failed") 469 utils.MustMatch(t, &topodatapb.Shard{ 470 IsPrimaryServing: true, 471 KeyRange: &topodatapb.KeyRange{}, 472 }, shard.Shard) 473 }, 474 }, 475 } 476 477 for _, tt := range tests { 478 tt := tt 479 t.Run(tt.name, func(t *testing.T) { 480 if tt.setup != nil { 481 func() { 482 t.Helper() 483 tt.setup(t, tt) 484 }() 485 } 486 487 _, err := tt.tc.Cluster.DeleteShards(ctx, tt.req) 488 if tt.shouldErr { 489 assert.Error(t, err) 490 } else { 491 require.NoError(t, err) 492 } 493 494 if tt.assertion != nil { 495 func() { 496 t.Helper() 497 tt.assertion(t, tt) 498 }() 499 } 500 }) 501 } 502 } 503 504 func TestFindTablet(t *testing.T) { 505 t.Parallel() 506 507 tests := []struct { 508 name string 509 tablets []*vtadminpb.Tablet 510 filter func(*vtadminpb.Tablet) bool 511 expected *vtadminpb.Tablet 512 expectedError error 513 }{ 514 { 515 name: "returns the first matching tablet", 516 tablets: []*vtadminpb.Tablet{ 517 { 518 State: vtadminpb.Tablet_NOT_SERVING, 519 Tablet: &topodatapb.Tablet{ 520 Alias: &topodatapb.TabletAlias{ 521 Cell: "c0_cell1", 522 Uid: 100, 523 }, 524 Keyspace: "commerce", 525 }, 526 }, 527 { 528 State: vtadminpb.Tablet_SERVING, 529 Tablet: &topodatapb.Tablet{ 530 Alias: &topodatapb.TabletAlias{ 531 Cell: "c0_cell1", 532 Uid: 101, 533 }, 534 Keyspace: "commerce", 535 }, 536 }, 537 { 538 State: vtadminpb.Tablet_SERVING, 539 Tablet: &topodatapb.Tablet{ 540 Alias: &topodatapb.TabletAlias{ 541 Cell: "c0_cell1", 542 Uid: 102, 543 }, 544 Keyspace: "commerce", 545 }, 546 }, 547 }, 548 549 filter: func(t *vtadminpb.Tablet) bool { 550 return t.State == vtadminpb.Tablet_SERVING 551 }, 552 expected: &vtadminpb.Tablet{ 553 Cluster: &vtadminpb.Cluster{ 554 Id: "c0", 555 Name: "cluster0", 556 }, 557 State: vtadminpb.Tablet_SERVING, 558 Tablet: &topodatapb.Tablet{ 559 Alias: &topodatapb.TabletAlias{ 560 Cell: "c0_cell1", 561 Uid: 101, 562 }, 563 Keyspace: "commerce", 564 }, 565 }, 566 }, 567 { 568 name: "returns an error if no match found", 569 tablets: []*vtadminpb.Tablet{ 570 { 571 State: vtadminpb.Tablet_NOT_SERVING, 572 Tablet: &topodatapb.Tablet{ 573 Alias: &topodatapb.TabletAlias{ 574 Cell: "c0_cell1", 575 Uid: 100, 576 }, 577 Keyspace: "commerce", 578 }, 579 }, 580 { 581 State: vtadminpb.Tablet_NOT_SERVING, 582 Tablet: &topodatapb.Tablet{ 583 Alias: &topodatapb.TabletAlias{ 584 Cell: "c0_cell1", 585 Uid: 101, 586 }, 587 Keyspace: "commerce", 588 }, 589 }, 590 }, 591 filter: func(t *vtadminpb.Tablet) bool { 592 return t.State == vtadminpb.Tablet_SERVING 593 }, 594 expectedError: vtadminerrors.ErrNoTablet, 595 }, 596 } 597 598 ctx := context.Background() 599 600 for _, tt := range tests { 601 tt := tt 602 603 t.Run(tt.name, func(t *testing.T) { 604 t.Parallel() 605 606 cluster := testutil.BuildCluster(t, testutil.TestClusterConfig{ 607 Cluster: &vtadminpb.Cluster{ 608 Id: "c0", 609 Name: "cluster0", 610 }, 611 Tablets: tt.tablets, 612 }) 613 tablet, err := cluster.FindTablet(ctx, tt.filter) 614 615 if tt.expectedError != nil { 616 assert.True(t, errors.Is(err, tt.expectedError), "expected error type %w does not match actual error type %w", err, tt.expectedError) 617 } else { 618 assert.NoError(t, err) 619 utils.MustMatch(t, tt.expected, tablet) 620 } 621 }) 622 } 623 } 624 625 func TestFindTablets(t *testing.T) { 626 t.Parallel() 627 628 tests := []struct { 629 name string 630 tablets []*vtadminpb.Tablet 631 filter func(*vtadminpb.Tablet) bool 632 n int 633 expected []*vtadminpb.Tablet 634 }{ 635 { 636 name: "returns n filtered tablets", 637 tablets: []*vtadminpb.Tablet{ 638 { 639 State: vtadminpb.Tablet_SERVING, 640 Tablet: &topodatapb.Tablet{ 641 Alias: &topodatapb.TabletAlias{ 642 Cell: "c0_cell1", 643 Uid: 100, 644 }, 645 Keyspace: "commerce", 646 }, 647 }, 648 { 649 State: vtadminpb.Tablet_NOT_SERVING, 650 Tablet: &topodatapb.Tablet{ 651 Alias: &topodatapb.TabletAlias{ 652 Cell: "c0_cell1", 653 Uid: 101, 654 }, 655 Keyspace: "commerce", 656 }, 657 }, 658 { 659 State: vtadminpb.Tablet_SERVING, 660 Tablet: &topodatapb.Tablet{ 661 Alias: &topodatapb.TabletAlias{ 662 Cell: "c0_cell1", 663 Uid: 102, 664 }, 665 Keyspace: "commerce", 666 }, 667 }, 668 { 669 State: vtadminpb.Tablet_SERVING, 670 Tablet: &topodatapb.Tablet{ 671 Alias: &topodatapb.TabletAlias{ 672 Cell: "c0_cell1", 673 Uid: 103, 674 }, 675 Keyspace: "commerce", 676 }, 677 }, 678 }, 679 filter: func(t *vtadminpb.Tablet) bool { 680 return t.State == vtadminpb.Tablet_SERVING 681 }, 682 n: 2, 683 expected: []*vtadminpb.Tablet{ 684 { 685 Cluster: &vtadminpb.Cluster{ 686 Id: "c0", 687 Name: "cluster0", 688 }, 689 State: vtadminpb.Tablet_SERVING, 690 Tablet: &topodatapb.Tablet{ 691 Alias: &topodatapb.TabletAlias{ 692 Cell: "c0_cell1", 693 Uid: 100, 694 }, 695 Keyspace: "commerce", 696 }, 697 }, 698 { 699 Cluster: &vtadminpb.Cluster{ 700 Id: "c0", 701 Name: "cluster0", 702 }, 703 State: vtadminpb.Tablet_SERVING, 704 Tablet: &topodatapb.Tablet{ 705 Alias: &topodatapb.TabletAlias{ 706 Cell: "c0_cell1", 707 Uid: 102, 708 }, 709 Keyspace: "commerce", 710 }, 711 }, 712 }, 713 }, 714 { 715 name: "returns all filtered tablets when n == -1", 716 tablets: []*vtadminpb.Tablet{ 717 { 718 State: vtadminpb.Tablet_SERVING, 719 Tablet: &topodatapb.Tablet{ 720 Alias: &topodatapb.TabletAlias{ 721 Cell: "c0_cell1", 722 Uid: 100, 723 }, 724 Keyspace: "commerce", 725 }, 726 }, 727 { 728 State: vtadminpb.Tablet_NOT_SERVING, 729 Tablet: &topodatapb.Tablet{ 730 Alias: &topodatapb.TabletAlias{ 731 Cell: "c0_cell1", 732 Uid: 101, 733 }, 734 Keyspace: "commerce", 735 }, 736 }, 737 { 738 State: vtadminpb.Tablet_SERVING, 739 Tablet: &topodatapb.Tablet{ 740 Alias: &topodatapb.TabletAlias{ 741 Cell: "c0_cell1", 742 Uid: 102, 743 }, 744 Keyspace: "commerce", 745 }, 746 }, 747 { 748 State: vtadminpb.Tablet_SERVING, 749 Tablet: &topodatapb.Tablet{ 750 Alias: &topodatapb.TabletAlias{ 751 Cell: "c0_cell1", 752 Uid: 103, 753 }, 754 Keyspace: "commerce", 755 }, 756 }, 757 }, 758 filter: func(t *vtadminpb.Tablet) bool { 759 return t.State == vtadminpb.Tablet_SERVING 760 }, 761 n: -1, 762 expected: []*vtadminpb.Tablet{ 763 { 764 Cluster: &vtadminpb.Cluster{ 765 Id: "c0", 766 Name: "cluster0", 767 }, 768 State: vtadminpb.Tablet_SERVING, 769 Tablet: &topodatapb.Tablet{ 770 Alias: &topodatapb.TabletAlias{ 771 Cell: "c0_cell1", 772 Uid: 100, 773 }, 774 Keyspace: "commerce", 775 }, 776 }, 777 { 778 Cluster: &vtadminpb.Cluster{ 779 Id: "c0", 780 Name: "cluster0", 781 }, 782 State: vtadminpb.Tablet_SERVING, 783 Tablet: &topodatapb.Tablet{ 784 Alias: &topodatapb.TabletAlias{ 785 Cell: "c0_cell1", 786 Uid: 102, 787 }, 788 Keyspace: "commerce", 789 }, 790 }, 791 { 792 Cluster: &vtadminpb.Cluster{ 793 Id: "c0", 794 Name: "cluster0", 795 }, 796 State: vtadminpb.Tablet_SERVING, 797 Tablet: &topodatapb.Tablet{ 798 Alias: &topodatapb.TabletAlias{ 799 Cell: "c0_cell1", 800 Uid: 103, 801 }, 802 Keyspace: "commerce", 803 }, 804 }, 805 }, 806 }, 807 } 808 809 ctx := context.Background() 810 811 for _, tt := range tests { 812 tt := tt 813 814 t.Run(tt.name, func(t *testing.T) { 815 t.Parallel() 816 817 cluster := testutil.BuildCluster(t, testutil.TestClusterConfig{ 818 Cluster: &vtadminpb.Cluster{ 819 Id: "c0", 820 Name: "cluster0", 821 }, 822 Tablets: tt.tablets, 823 }) 824 tablets, err := cluster.FindTablets(ctx, tt.filter, tt.n) 825 826 assert.NoError(t, err) 827 testutil.AssertTabletSlicesEqual(t, tt.expected, tablets) 828 }) 829 } 830 } 831 832 func TestFindWorkflows(t *testing.T) { 833 t.Parallel() 834 835 tests := []struct { 836 name string 837 cfg testutil.TestClusterConfig 838 keyspaces []string 839 opts cluster.FindWorkflowsOptions 840 expected *vtadminpb.ClusterWorkflows 841 shouldErr bool 842 }{ 843 { 844 name: "success", 845 cfg: testutil.TestClusterConfig{ 846 Cluster: &vtadminpb.Cluster{ 847 Id: "c1", 848 Name: "cluster1", 849 }, 850 VtctldClient: &fakevtctldclient.VtctldClient{ 851 GetWorkflowsResults: map[string]struct { 852 Response *vtctldatapb.GetWorkflowsResponse 853 Error error 854 }{ 855 "ks1": { 856 Response: &vtctldatapb.GetWorkflowsResponse{ 857 Workflows: []*vtctldatapb.Workflow{ 858 { 859 Name: "workflow1", 860 }, 861 }, 862 }, 863 }, 864 }, 865 }, 866 }, 867 keyspaces: []string{"ks1"}, 868 expected: &vtadminpb.ClusterWorkflows{ 869 Workflows: []*vtadminpb.Workflow{ 870 { 871 Cluster: &vtadminpb.Cluster{ 872 Id: "c1", 873 Name: "cluster1", 874 }, 875 Keyspace: "ks1", 876 Workflow: &vtctldatapb.Workflow{ 877 Name: "workflow1", 878 }, 879 }, 880 }, 881 }, 882 shouldErr: false, 883 }, 884 { 885 name: "error getting keyspaces is fatal", 886 cfg: testutil.TestClusterConfig{ 887 Cluster: &vtadminpb.Cluster{ 888 Id: "c1", 889 Name: "cluster1", 890 }, 891 VtctldClient: &fakevtctldclient.VtctldClient{ 892 GetKeyspacesResults: &struct { 893 Keyspaces []*vtctldatapb.Keyspace 894 Error error 895 }{ 896 Keyspaces: nil, 897 Error: assert.AnError, 898 }, 899 }, 900 }, 901 keyspaces: nil, 902 expected: nil, 903 shouldErr: true, 904 }, 905 { 906 name: "no keyspaces found", 907 cfg: testutil.TestClusterConfig{ 908 Cluster: &vtadminpb.Cluster{ 909 Id: "c1", 910 Name: "cluster1", 911 }, 912 VtctldClient: &fakevtctldclient.VtctldClient{ 913 GetKeyspacesResults: &struct { 914 Keyspaces []*vtctldatapb.Keyspace 915 Error error 916 }{ 917 Keyspaces: []*vtctldatapb.Keyspace{}, 918 Error: nil, 919 }, 920 }, 921 }, 922 keyspaces: nil, 923 expected: &vtadminpb.ClusterWorkflows{ 924 Workflows: []*vtadminpb.Workflow{}, 925 }, 926 shouldErr: false, 927 }, 928 { 929 name: "when specifying keyspaces and IgnoreKeyspaces, IgnoreKeyspaces is discarded", 930 cfg: testutil.TestClusterConfig{ 931 Cluster: &vtadminpb.Cluster{ 932 Id: "c1", 933 Name: "cluster1", 934 }, 935 VtctldClient: &fakevtctldclient.VtctldClient{ 936 GetKeyspacesResults: &struct { 937 Keyspaces []*vtctldatapb.Keyspace 938 Error error 939 }{ 940 Keyspaces: []*vtctldatapb.Keyspace{ 941 { 942 Name: "ks1", 943 }, 944 { 945 Name: "ks2", 946 }, 947 }, 948 Error: nil, 949 }, 950 GetWorkflowsResults: map[string]struct { 951 Response *vtctldatapb.GetWorkflowsResponse 952 Error error 953 }{ 954 "ks1": { 955 Response: &vtctldatapb.GetWorkflowsResponse{ 956 Workflows: []*vtctldatapb.Workflow{ 957 { 958 Name: "workflow1", 959 }, 960 { 961 Name: "workflow2", 962 }, 963 }, 964 }, 965 }, 966 "ks2": { 967 Response: &vtctldatapb.GetWorkflowsResponse{ 968 Workflows: []*vtctldatapb.Workflow{ 969 { 970 Name: "workflow_a", 971 }, 972 }, 973 }, 974 }, 975 }, 976 }, 977 }, 978 keyspaces: []string{"ks2"}, 979 opts: cluster.FindWorkflowsOptions{ 980 IgnoreKeyspaces: sets.New[string]("ks2"), 981 }, 982 expected: &vtadminpb.ClusterWorkflows{ 983 Workflows: []*vtadminpb.Workflow{ 984 { 985 Cluster: &vtadminpb.Cluster{ 986 Id: "c1", 987 Name: "cluster1", 988 }, 989 Keyspace: "ks2", 990 Workflow: &vtctldatapb.Workflow{ 991 Name: "workflow_a", 992 }, 993 }, 994 }, 995 }, 996 shouldErr: false, 997 }, 998 { 999 name: "ignore keyspaces", 1000 cfg: testutil.TestClusterConfig{ 1001 Cluster: &vtadminpb.Cluster{ 1002 Id: "c1", 1003 Name: "cluster1", 1004 }, 1005 VtctldClient: &fakevtctldclient.VtctldClient{ 1006 GetKeyspacesResults: &struct { 1007 Keyspaces []*vtctldatapb.Keyspace 1008 Error error 1009 }{ 1010 Keyspaces: []*vtctldatapb.Keyspace{ 1011 { 1012 Name: "ks1", 1013 }, 1014 { 1015 Name: "ks2", 1016 }, 1017 }, 1018 Error: nil, 1019 }, 1020 GetWorkflowsResults: map[string]struct { 1021 Response *vtctldatapb.GetWorkflowsResponse 1022 Error error 1023 }{ 1024 "ks1": { 1025 Response: &vtctldatapb.GetWorkflowsResponse{ 1026 Workflows: []*vtctldatapb.Workflow{ 1027 { 1028 Name: "workflow1", 1029 }, 1030 { 1031 Name: "workflow2", 1032 }, 1033 }, 1034 }, 1035 }, 1036 "ks2": { 1037 Response: &vtctldatapb.GetWorkflowsResponse{ 1038 Workflows: []*vtctldatapb.Workflow{ 1039 { 1040 Name: "workflow_a", 1041 }, 1042 }, 1043 }, 1044 }, 1045 }, 1046 }, 1047 }, 1048 keyspaces: nil, 1049 opts: cluster.FindWorkflowsOptions{ 1050 IgnoreKeyspaces: sets.New[string]("ks2"), 1051 }, 1052 expected: &vtadminpb.ClusterWorkflows{ 1053 Workflows: []*vtadminpb.Workflow{ 1054 { 1055 Cluster: &vtadminpb.Cluster{ 1056 Id: "c1", 1057 Name: "cluster1", 1058 }, 1059 Keyspace: "ks1", 1060 Workflow: &vtctldatapb.Workflow{ 1061 Name: "workflow1", 1062 }, 1063 }, 1064 { 1065 Cluster: &vtadminpb.Cluster{ 1066 Id: "c1", 1067 Name: "cluster1", 1068 }, 1069 Keyspace: "ks1", 1070 Workflow: &vtctldatapb.Workflow{ 1071 Name: "workflow2", 1072 }, 1073 }, 1074 }, 1075 }, 1076 shouldErr: false, 1077 }, 1078 { 1079 name: "error getting workflows is fatal if all keyspaces fail", 1080 cfg: testutil.TestClusterConfig{ 1081 Cluster: &vtadminpb.Cluster{ 1082 Id: "c1", 1083 Name: "cluster1", 1084 }, 1085 VtctldClient: &fakevtctldclient.VtctldClient{ 1086 GetWorkflowsResults: map[string]struct { 1087 Response *vtctldatapb.GetWorkflowsResponse 1088 Error error 1089 }{ 1090 "ks1": { 1091 Error: assert.AnError, 1092 }, 1093 }, 1094 }, 1095 }, 1096 keyspaces: []string{"ks1"}, 1097 expected: nil, 1098 shouldErr: true, 1099 }, 1100 { 1101 name: "error getting workflows is non-fatal if some keyspaces fail", 1102 cfg: testutil.TestClusterConfig{ 1103 Cluster: &vtadminpb.Cluster{ 1104 Id: "c1", 1105 Name: "cluster1", 1106 }, 1107 VtctldClient: &fakevtctldclient.VtctldClient{ 1108 GetWorkflowsResults: map[string]struct { 1109 Response *vtctldatapb.GetWorkflowsResponse 1110 Error error 1111 }{ 1112 "ks1": { 1113 Error: assert.AnError, 1114 }, 1115 "ks2": { 1116 Response: &vtctldatapb.GetWorkflowsResponse{ 1117 Workflows: []*vtctldatapb.Workflow{ 1118 { 1119 Name: "workflow1", 1120 }, 1121 }, 1122 }, 1123 }, 1124 }, 1125 }, 1126 }, 1127 keyspaces: []string{"ks1", "ks2"}, 1128 expected: &vtadminpb.ClusterWorkflows{ 1129 Workflows: []*vtadminpb.Workflow{ 1130 { 1131 Cluster: &vtadminpb.Cluster{ 1132 Id: "c1", 1133 Name: "cluster1", 1134 }, 1135 Keyspace: "ks2", 1136 Workflow: &vtctldatapb.Workflow{ 1137 Name: "workflow1", 1138 }, 1139 }, 1140 }, 1141 Warnings: []string{"something about ks1"}, 1142 }, 1143 shouldErr: false, 1144 }, 1145 { 1146 name: "filtered workflows", 1147 cfg: testutil.TestClusterConfig{ 1148 Cluster: &vtadminpb.Cluster{ 1149 Id: "c1", 1150 Name: "cluster1", 1151 }, 1152 VtctldClient: &fakevtctldclient.VtctldClient{ 1153 GetWorkflowsResults: map[string]struct { 1154 Response *vtctldatapb.GetWorkflowsResponse 1155 Error error 1156 }{ 1157 "ks1": { 1158 Response: &vtctldatapb.GetWorkflowsResponse{ 1159 Workflows: []*vtctldatapb.Workflow{ 1160 { 1161 Name: "include_me", 1162 }, 1163 { 1164 Name: "dont_include_me", 1165 }, 1166 }, 1167 }, 1168 }, 1169 }, 1170 }, 1171 }, 1172 keyspaces: []string{"ks1"}, 1173 opts: cluster.FindWorkflowsOptions{ 1174 Filter: func(workflow *vtadminpb.Workflow) bool { 1175 return strings.HasPrefix(workflow.Workflow.Name, "include_me") 1176 }, 1177 }, 1178 expected: &vtadminpb.ClusterWorkflows{ 1179 Workflows: []*vtadminpb.Workflow{ 1180 { 1181 Cluster: &vtadminpb.Cluster{ 1182 Id: "c1", 1183 Name: "cluster1", 1184 }, 1185 Keyspace: "ks1", 1186 Workflow: &vtctldatapb.Workflow{ 1187 Name: "include_me", 1188 }, 1189 }, 1190 }, 1191 }, 1192 shouldErr: false, 1193 }, 1194 } 1195 1196 ctx := context.Background() 1197 1198 for _, tt := range tests { 1199 tt := tt 1200 1201 t.Run(tt.name, func(t *testing.T) { 1202 t.Parallel() 1203 1204 c := testutil.BuildCluster(t, tt.cfg) 1205 workflows, err := c.FindWorkflows(ctx, tt.keyspaces, tt.opts) 1206 if tt.shouldErr { 1207 assert.Error(t, err) 1208 1209 return 1210 } 1211 1212 assert.NoError(t, err) 1213 testutil.AssertClusterWorkflowsEqual(t, tt.expected, workflows) 1214 }) 1215 } 1216 } 1217 1218 func TestGetCellInfos(t *testing.T) { 1219 t.Parallel() 1220 1221 cpb := &vtadminpb.Cluster{ 1222 Id: "test", 1223 Name: "test", 1224 } 1225 1226 tests := []struct { 1227 name string 1228 vtctld *fakevtctldclient.VtctldClient 1229 req *vtadminpb.GetCellInfosRequest 1230 expected []*vtadminpb.ClusterCellInfo 1231 shouldErr bool 1232 }{ 1233 { 1234 name: "all cells - ok", 1235 vtctld: &fakevtctldclient.VtctldClient{ 1236 GetCellInfoNamesResults: &struct { 1237 Response *vtctldatapb.GetCellInfoNamesResponse 1238 Error error 1239 }{ 1240 Response: &vtctldatapb.GetCellInfoNamesResponse{ 1241 Names: []string{"c1", "c2"}, 1242 }, 1243 }, 1244 GetCellInfoResults: map[string]struct { 1245 Response *vtctldatapb.GetCellInfoResponse 1246 Error error 1247 }{ 1248 "c1": { 1249 Response: &vtctldatapb.GetCellInfoResponse{ 1250 CellInfo: &topodatapb.CellInfo{ 1251 ServerAddress: "http://cell1", 1252 Root: "/vitess/cell1", 1253 }, 1254 }, 1255 }, 1256 "c2": { 1257 Response: &vtctldatapb.GetCellInfoResponse{ 1258 CellInfo: &topodatapb.CellInfo{ 1259 ServerAddress: "http://cell2", 1260 Root: "/vitess/cell2", 1261 }, 1262 }, 1263 }, 1264 }, 1265 }, 1266 req: &vtadminpb.GetCellInfosRequest{}, 1267 expected: []*vtadminpb.ClusterCellInfo{ 1268 { 1269 Cluster: cpb, 1270 Name: "c1", 1271 CellInfo: &topodatapb.CellInfo{ 1272 ServerAddress: "http://cell1", 1273 Root: "/vitess/cell1", 1274 }, 1275 }, 1276 { 1277 Cluster: cpb, 1278 Name: "c2", 1279 CellInfo: &topodatapb.CellInfo{ 1280 ServerAddress: "http://cell2", 1281 Root: "/vitess/cell2", 1282 }, 1283 }, 1284 }, 1285 }, 1286 { 1287 name: "all cells - names only", 1288 vtctld: &fakevtctldclient.VtctldClient{ 1289 GetCellInfoNamesResults: &struct { 1290 Response *vtctldatapb.GetCellInfoNamesResponse 1291 Error error 1292 }{ 1293 Response: &vtctldatapb.GetCellInfoNamesResponse{ 1294 Names: []string{"c1", "c2"}, 1295 }, 1296 }, 1297 GetCellInfoResults: map[string]struct { 1298 Response *vtctldatapb.GetCellInfoResponse 1299 Error error 1300 }{ 1301 "c1": { 1302 Response: &vtctldatapb.GetCellInfoResponse{ 1303 CellInfo: &topodatapb.CellInfo{ 1304 ServerAddress: "http://cell1", 1305 Root: "/vitess/cell1", 1306 }, 1307 }, 1308 }, 1309 "c2": { 1310 Response: &vtctldatapb.GetCellInfoResponse{ 1311 CellInfo: &topodatapb.CellInfo{ 1312 ServerAddress: "http://cell2", 1313 Root: "/vitess/cell2", 1314 }, 1315 }, 1316 }, 1317 }, 1318 }, 1319 req: &vtadminpb.GetCellInfosRequest{ 1320 NamesOnly: true, 1321 }, 1322 expected: []*vtadminpb.ClusterCellInfo{ 1323 { 1324 Cluster: cpb, 1325 Name: "c1", 1326 }, 1327 { 1328 Cluster: cpb, 1329 Name: "c2", 1330 }, 1331 }, 1332 }, 1333 { 1334 name: "all cells - fail", 1335 vtctld: &fakevtctldclient.VtctldClient{ 1336 GetCellInfoNamesResults: &struct { 1337 Response *vtctldatapb.GetCellInfoNamesResponse 1338 Error error 1339 }{ 1340 Error: fmt.Errorf("getcellinfonames failed"), 1341 }, 1342 }, 1343 req: &vtadminpb.GetCellInfosRequest{}, 1344 shouldErr: true, 1345 }, 1346 { 1347 name: "cells - ok", 1348 vtctld: &fakevtctldclient.VtctldClient{ 1349 GetCellInfoNamesResults: &struct { 1350 Response *vtctldatapb.GetCellInfoNamesResponse 1351 Error error 1352 }{ 1353 Response: &vtctldatapb.GetCellInfoNamesResponse{ 1354 Names: []string{"c1", "c2"}, 1355 }, 1356 }, 1357 GetCellInfoResults: map[string]struct { 1358 Response *vtctldatapb.GetCellInfoResponse 1359 Error error 1360 }{ 1361 "c1": { 1362 Response: &vtctldatapb.GetCellInfoResponse{ 1363 CellInfo: &topodatapb.CellInfo{ 1364 ServerAddress: "http://cell1", 1365 Root: "/vitess/cell1", 1366 }, 1367 }, 1368 }, 1369 "c2": { 1370 Response: &vtctldatapb.GetCellInfoResponse{ 1371 CellInfo: &topodatapb.CellInfo{ 1372 ServerAddress: "http://cell2", 1373 Root: "/vitess/cell2", 1374 }, 1375 }, 1376 }, 1377 }, 1378 }, 1379 req: &vtadminpb.GetCellInfosRequest{ 1380 Cells: []string{"c1"}, 1381 }, 1382 expected: []*vtadminpb.ClusterCellInfo{ 1383 { 1384 Cluster: cpb, 1385 Name: "c1", 1386 CellInfo: &topodatapb.CellInfo{ 1387 ServerAddress: "http://cell1", 1388 Root: "/vitess/cell1", 1389 }, 1390 }, 1391 }, 1392 }, 1393 { 1394 name: "getcellinfo - fail", 1395 vtctld: &fakevtctldclient.VtctldClient{ 1396 GetCellInfoNamesResults: &struct { 1397 Response *vtctldatapb.GetCellInfoNamesResponse 1398 Error error 1399 }{ 1400 Response: &vtctldatapb.GetCellInfoNamesResponse{ 1401 Names: []string{"c1", "c2"}, 1402 }, 1403 }, 1404 GetCellInfoResults: map[string]struct { 1405 Response *vtctldatapb.GetCellInfoResponse 1406 Error error 1407 }{ 1408 "c1": { 1409 Error: fmt.Errorf("GetCellInfo(c1) fail"), 1410 }, 1411 "c2": { 1412 Response: &vtctldatapb.GetCellInfoResponse{ 1413 CellInfo: &topodatapb.CellInfo{ 1414 ServerAddress: "http://cell2", 1415 Root: "/vitess/cell2", 1416 }, 1417 }, 1418 }, 1419 }, 1420 }, 1421 req: &vtadminpb.GetCellInfosRequest{ 1422 Cells: []string{"c1", "c2"}, 1423 }, 1424 shouldErr: true, 1425 }, 1426 { 1427 name: "cells with names only still gives full info", 1428 vtctld: &fakevtctldclient.VtctldClient{ 1429 GetCellInfoNamesResults: &struct { 1430 Response *vtctldatapb.GetCellInfoNamesResponse 1431 Error error 1432 }{ 1433 Response: &vtctldatapb.GetCellInfoNamesResponse{ 1434 Names: []string{"c1", "c2"}, 1435 }, 1436 }, 1437 GetCellInfoResults: map[string]struct { 1438 Response *vtctldatapb.GetCellInfoResponse 1439 Error error 1440 }{ 1441 "c1": { 1442 Response: &vtctldatapb.GetCellInfoResponse{ 1443 CellInfo: &topodatapb.CellInfo{ 1444 ServerAddress: "http://cell1", 1445 Root: "/vitess/cell1", 1446 }, 1447 }, 1448 }, 1449 "c2": { 1450 Response: &vtctldatapb.GetCellInfoResponse{ 1451 CellInfo: &topodatapb.CellInfo{ 1452 ServerAddress: "http://cell2", 1453 Root: "/vitess/cell2", 1454 }, 1455 }, 1456 }, 1457 }, 1458 }, 1459 req: &vtadminpb.GetCellInfosRequest{ 1460 Cells: []string{"c1"}, 1461 NamesOnly: true, 1462 }, 1463 expected: []*vtadminpb.ClusterCellInfo{ 1464 { 1465 Cluster: cpb, 1466 Name: "c1", 1467 CellInfo: &topodatapb.CellInfo{ 1468 ServerAddress: "http://cell1", 1469 Root: "/vitess/cell1", 1470 }, 1471 }, 1472 }, 1473 }, 1474 } 1475 1476 for _, tt := range tests { 1477 tt := tt 1478 t.Run(tt.name, func(t *testing.T) { 1479 t.Parallel() 1480 1481 c := testutil.BuildCluster(t, testutil.TestClusterConfig{ 1482 Cluster: cpb, 1483 VtctldClient: tt.vtctld, 1484 }) 1485 cellInfos, err := c.GetCellInfos(context.Background(), tt.req) 1486 if tt.shouldErr { 1487 assert.Error(t, err) 1488 return 1489 } 1490 1491 require.NoError(t, err) 1492 1493 sort.Slice(tt.expected, func(i, j int) bool { 1494 return tt.expected[i].Name < tt.expected[j].Name 1495 }) 1496 sort.Slice(cellInfos, func(i, j int) bool { 1497 return cellInfos[i].Name < cellInfos[j].Name 1498 }) 1499 1500 utils.MustMatch(t, tt.expected, cellInfos) 1501 }) 1502 } 1503 } 1504 1505 func TestGetCellsAliases(t *testing.T) { 1506 t.Parallel() 1507 1508 cpb := &vtadminpb.Cluster{ 1509 Id: "test", 1510 Name: "test", 1511 } 1512 1513 tests := []struct { 1514 name string 1515 vtctld *fakevtctldclient.VtctldClient 1516 expected *vtadminpb.ClusterCellsAliases 1517 shouldErr bool 1518 }{ 1519 { 1520 name: "ok", 1521 vtctld: &fakevtctldclient.VtctldClient{ 1522 GetCellsAliasesResults: &struct { 1523 Response *vtctldatapb.GetCellsAliasesResponse 1524 Error error 1525 }{ 1526 Response: &vtctldatapb.GetCellsAliasesResponse{ 1527 Aliases: map[string]*topodatapb.CellsAlias{ 1528 "a1": { 1529 Cells: []string{"c1", "c2"}, 1530 }, 1531 }, 1532 }, 1533 }, 1534 }, 1535 expected: &vtadminpb.ClusterCellsAliases{ 1536 Cluster: cpb, 1537 Aliases: map[string]*topodatapb.CellsAlias{ 1538 "a1": { 1539 Cells: []string{"c1", "c2"}, 1540 }, 1541 }, 1542 }, 1543 }, 1544 { 1545 name: "error", 1546 vtctld: &fakevtctldclient.VtctldClient{ 1547 GetCellsAliasesResults: &struct { 1548 Response *vtctldatapb.GetCellsAliasesResponse 1549 Error error 1550 }{ 1551 Error: fmt.Errorf("this should fail"), 1552 }, 1553 }, 1554 shouldErr: true, 1555 }, 1556 } 1557 1558 for _, tt := range tests { 1559 tt := tt 1560 t.Run(tt.name, func(t *testing.T) { 1561 t.Parallel() 1562 1563 c := testutil.BuildCluster(t, testutil.TestClusterConfig{ 1564 Cluster: cpb, 1565 VtctldClient: tt.vtctld, 1566 }) 1567 cellsAliases, err := c.GetCellsAliases(context.Background()) 1568 if tt.shouldErr { 1569 assert.Error(t, err) 1570 return 1571 } 1572 1573 require.NoError(t, err) 1574 utils.MustMatch(t, tt.expected, cellsAliases) 1575 }) 1576 } 1577 } 1578 1579 func TestGetSchema(t *testing.T) { 1580 t.Parallel() 1581 1582 tests := []struct { 1583 name string 1584 vtctld vtctldclient.VtctldClient 1585 req *vtctldatapb.GetSchemaRequest 1586 tablet *vtadminpb.Tablet 1587 expected *vtadminpb.Schema 1588 shouldErr bool 1589 }{ 1590 { 1591 name: "success", 1592 vtctld: &fakevtctldclient.VtctldClient{ 1593 GetSchemaResults: map[string]struct { 1594 Response *vtctldatapb.GetSchemaResponse 1595 Error error 1596 }{ 1597 "zone1-0000000100": { 1598 Response: &vtctldatapb.GetSchemaResponse{ 1599 Schema: &tabletmanagerdatapb.SchemaDefinition{ 1600 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 1601 { 1602 Name: "some_table", 1603 }, 1604 }, 1605 }, 1606 }, 1607 }, 1608 }, 1609 }, 1610 req: &vtctldatapb.GetSchemaRequest{}, 1611 tablet: &vtadminpb.Tablet{ 1612 State: vtadminpb.Tablet_SERVING, 1613 Tablet: &topodatapb.Tablet{ 1614 Alias: &topodatapb.TabletAlias{ 1615 Cell: "zone1", 1616 Uid: 100, 1617 }, 1618 Keyspace: "testkeyspace", 1619 }, 1620 }, 1621 expected: &vtadminpb.Schema{ 1622 Cluster: &vtadminpb.Cluster{ 1623 Name: "cluster0", 1624 Id: "c0", 1625 }, 1626 Keyspace: "testkeyspace", 1627 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 1628 { 1629 Name: "some_table", 1630 }, 1631 }, 1632 TableSizes: map[string]*vtadminpb.Schema_TableSize{}, 1633 }, 1634 shouldErr: false, 1635 }, 1636 { 1637 name: "error getting schema", 1638 vtctld: &fakevtctldclient.VtctldClient{ 1639 GetSchemaResults: map[string]struct { 1640 Response *vtctldatapb.GetSchemaResponse 1641 Error error 1642 }{ 1643 "zone1-0000000100": { 1644 Error: assert.AnError, 1645 }, 1646 }, 1647 }, 1648 req: &vtctldatapb.GetSchemaRequest{}, 1649 tablet: &vtadminpb.Tablet{ 1650 State: vtadminpb.Tablet_SERVING, 1651 Tablet: &topodatapb.Tablet{ 1652 Alias: &topodatapb.TabletAlias{ 1653 Cell: "zone1", 1654 Uid: 100, 1655 }, 1656 Keyspace: "testkeyspace", 1657 }, 1658 }, 1659 expected: nil, 1660 shouldErr: true, 1661 }, 1662 { 1663 name: "underlying schema is nil", 1664 vtctld: &fakevtctldclient.VtctldClient{ 1665 GetSchemaResults: map[string]struct { 1666 Response *vtctldatapb.GetSchemaResponse 1667 Error error 1668 }{ 1669 "zone1-0000000100": { 1670 Response: &vtctldatapb.GetSchemaResponse{ 1671 Schema: nil, 1672 }, 1673 Error: nil, 1674 }, 1675 }, 1676 }, 1677 req: &vtctldatapb.GetSchemaRequest{}, 1678 tablet: &vtadminpb.Tablet{ 1679 State: vtadminpb.Tablet_SERVING, 1680 Tablet: &topodatapb.Tablet{ 1681 Alias: &topodatapb.TabletAlias{ 1682 Cell: "zone1", 1683 Uid: 100, 1684 }, 1685 Keyspace: "testkeyspace", 1686 }, 1687 }, 1688 expected: &vtadminpb.Schema{ 1689 Cluster: &vtadminpb.Cluster{ 1690 Id: "c2", 1691 Name: "cluster2", 1692 }, 1693 Keyspace: "testkeyspace", 1694 TableDefinitions: nil, 1695 TableSizes: map[string]*vtadminpb.Schema_TableSize{}, 1696 }, 1697 shouldErr: false, 1698 }, 1699 } 1700 1701 ctx := context.Background() 1702 1703 for i, tt := range tests { 1704 i := i 1705 tt := tt 1706 1707 t.Run(tt.name, func(t *testing.T) { 1708 t.Parallel() 1709 1710 c := testutil.BuildCluster(t, testutil.TestClusterConfig{ 1711 Cluster: &vtadminpb.Cluster{ 1712 Id: fmt.Sprintf("c%d", i), 1713 Name: fmt.Sprintf("cluster%d", i), 1714 }, 1715 VtctldClient: tt.vtctld, 1716 Tablets: []*vtadminpb.Tablet{tt.tablet}, 1717 DBConfig: testutil.Dbcfg{}, 1718 }) 1719 1720 schema, err := c.GetSchema(ctx, "testkeyspace", cluster.GetSchemaOptions{ 1721 BaseRequest: tt.req, 1722 }) 1723 if tt.shouldErr { 1724 assert.Error(t, err) 1725 1726 return 1727 } 1728 1729 assert.NoError(t, err) 1730 assert.Equal(t, tt.expected, schema) 1731 }) 1732 } 1733 1734 t.Run("does not modify passed-in request", func(t *testing.T) { 1735 t.Parallel() 1736 1737 vtctld := &fakevtctldclient.VtctldClient{ 1738 GetSchemaResults: map[string]struct { 1739 Response *vtctldatapb.GetSchemaResponse 1740 Error error 1741 }{ 1742 "zone1-0000000100": { 1743 Response: &vtctldatapb.GetSchemaResponse{}, 1744 }, 1745 }, 1746 } 1747 1748 req := &vtctldatapb.GetSchemaRequest{ 1749 TabletAlias: &topodatapb.TabletAlias{ 1750 Cell: "otherzone", 1751 Uid: 500, 1752 }, 1753 } 1754 tablet := &vtadminpb.Tablet{ 1755 Tablet: &topodatapb.Tablet{ 1756 Alias: &topodatapb.TabletAlias{ 1757 Cell: "zone1", 1758 Uid: 100, 1759 }, 1760 }, 1761 } 1762 1763 c := testutil.BuildCluster(t, testutil.TestClusterConfig{ 1764 Cluster: &vtadminpb.Cluster{ 1765 Id: "c0", 1766 Name: "cluster0", 1767 }, 1768 VtctldClient: vtctld, 1769 }) 1770 1771 _, _ = c.GetSchema(ctx, "testkeyspace", cluster.GetSchemaOptions{ 1772 BaseRequest: req, 1773 }) 1774 1775 assert.NotEqual(t, req.TabletAlias, tablet.Tablet.Alias, "expected GetSchema to not modify original request object") 1776 }) 1777 1778 t.Run("size aggregation", func(t *testing.T) { 1779 t.Parallel() 1780 1781 tests := []struct { 1782 name string 1783 cfg testutil.TestClusterConfig 1784 keyspace string 1785 opts cluster.GetSchemaOptions 1786 expected *vtadminpb.Schema 1787 shouldErr bool 1788 }{ 1789 { 1790 name: "success", 1791 cfg: testutil.TestClusterConfig{ 1792 Cluster: &vtadminpb.Cluster{ 1793 Id: "c0", 1794 Name: "cluster0", 1795 }, 1796 Tablets: []*vtadminpb.Tablet{ 1797 { 1798 Tablet: &topodatapb.Tablet{ 1799 Alias: &topodatapb.TabletAlias{ 1800 Cell: "zone1", 1801 Uid: 100, 1802 }, 1803 Keyspace: "testkeyspace", 1804 Shard: "-80", 1805 }, 1806 State: vtadminpb.Tablet_SERVING, 1807 }, 1808 { 1809 Tablet: &topodatapb.Tablet{ 1810 Alias: &topodatapb.TabletAlias{ 1811 Cell: "zone1", 1812 Uid: 200, 1813 }, 1814 Keyspace: "testkeyspace", 1815 Shard: "80-", 1816 }, 1817 State: vtadminpb.Tablet_SERVING, 1818 }, 1819 }, 1820 VtctldClient: &fakevtctldclient.VtctldClient{ 1821 FindAllShardsInKeyspaceResults: map[string]struct { 1822 Response *vtctldatapb.FindAllShardsInKeyspaceResponse 1823 Error error 1824 }{ 1825 "testkeyspace": { 1826 Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{ 1827 Shards: map[string]*vtctldatapb.Shard{ 1828 "-80": { 1829 Name: "-80", 1830 Shard: &topodatapb.Shard{ 1831 IsPrimaryServing: true, 1832 PrimaryAlias: &topodatapb.TabletAlias{ 1833 Cell: "zone1", 1834 Uid: 100, 1835 }, 1836 }, 1837 }, 1838 "80-": { 1839 Name: "80-", 1840 Shard: &topodatapb.Shard{ 1841 IsPrimaryServing: true, 1842 PrimaryAlias: &topodatapb.TabletAlias{ 1843 Cell: "zone1", 1844 Uid: 200, 1845 }, 1846 }, 1847 }, 1848 "-": { 1849 Name: "-", 1850 Shard: &topodatapb.Shard{ 1851 IsPrimaryServing: false, 1852 }, 1853 }, 1854 }, 1855 }, 1856 }, 1857 }, 1858 GetSchemaResults: map[string]struct { 1859 Response *vtctldatapb.GetSchemaResponse 1860 Error error 1861 }{ 1862 "zone1-0000000100": { 1863 Response: &vtctldatapb.GetSchemaResponse{ 1864 Schema: &tabletmanagerdatapb.SchemaDefinition{ 1865 DatabaseSchema: "CREATE DATABASE vt_testkeyspcae", 1866 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 1867 { 1868 Name: "foo", 1869 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 1870 DataLength: 100, 1871 RowCount: 5, 1872 }, 1873 }, 1874 }, 1875 }, 1876 }, 1877 "zone1-0000000200": { 1878 Response: &vtctldatapb.GetSchemaResponse{ 1879 Schema: &tabletmanagerdatapb.SchemaDefinition{ 1880 DatabaseSchema: "CREATE DATABASE vt_testkeyspcae", 1881 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 1882 { 1883 Name: "foo", 1884 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 1885 DataLength: 200, 1886 RowCount: 420, 1887 }, 1888 }, 1889 }, 1890 }, 1891 }, 1892 }, 1893 }, 1894 }, 1895 keyspace: "testkeyspace", 1896 opts: cluster.GetSchemaOptions{ 1897 TableSizeOptions: &vtadminpb.GetSchemaTableSizeOptions{ 1898 AggregateSizes: true, 1899 }, 1900 }, 1901 expected: &vtadminpb.Schema{ 1902 Cluster: &vtadminpb.Cluster{ 1903 Id: "c0", 1904 Name: "cluster0", 1905 }, 1906 Keyspace: "testkeyspace", 1907 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 1908 { 1909 Name: "foo", 1910 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 1911 DataLength: 0, 1912 RowCount: 0, 1913 }, 1914 }, 1915 TableSizes: map[string]*vtadminpb.Schema_TableSize{ 1916 "foo": { 1917 DataLength: 100 + 200, 1918 RowCount: 5 + 420, 1919 ByShard: map[string]*vtadminpb.Schema_ShardTableSize{ 1920 "-80": { 1921 DataLength: 100, 1922 RowCount: 5, 1923 }, 1924 "80-": { 1925 DataLength: 200, 1926 RowCount: 420, 1927 }, 1928 }, 1929 }, 1930 }, 1931 }, 1932 shouldErr: false, 1933 }, 1934 { 1935 name: "include nonserving shards", 1936 cfg: testutil.TestClusterConfig{ 1937 Cluster: &vtadminpb.Cluster{ 1938 Id: "c0", 1939 Name: "cluster0", 1940 }, 1941 Tablets: []*vtadminpb.Tablet{ 1942 { 1943 Tablet: &topodatapb.Tablet{ 1944 Alias: &topodatapb.TabletAlias{ 1945 Cell: "zone1", 1946 Uid: 100, 1947 }, 1948 Keyspace: "testkeyspace", 1949 Shard: "-80", 1950 }, 1951 State: vtadminpb.Tablet_SERVING, 1952 }, 1953 { 1954 Tablet: &topodatapb.Tablet{ 1955 Alias: &topodatapb.TabletAlias{ 1956 Cell: "zone1", 1957 Uid: 200, 1958 }, 1959 Keyspace: "testkeyspace", 1960 Shard: "80-", 1961 }, 1962 State: vtadminpb.Tablet_SERVING, 1963 }, 1964 { 1965 Tablet: &topodatapb.Tablet{ 1966 Alias: &topodatapb.TabletAlias{ 1967 Cell: "zone1", 1968 Uid: 300, 1969 }, 1970 Keyspace: "testkeyspace", 1971 Shard: "-", 1972 }, 1973 State: vtadminpb.Tablet_SERVING, 1974 }, 1975 }, 1976 VtctldClient: &fakevtctldclient.VtctldClient{ 1977 FindAllShardsInKeyspaceResults: map[string]struct { 1978 Response *vtctldatapb.FindAllShardsInKeyspaceResponse 1979 Error error 1980 }{ 1981 "testkeyspace": { 1982 Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{ 1983 Shards: map[string]*vtctldatapb.Shard{ 1984 "-80": { 1985 Name: "-80", 1986 Shard: &topodatapb.Shard{ 1987 IsPrimaryServing: true, 1988 }, 1989 }, 1990 "80-": { 1991 Name: "80-", 1992 Shard: &topodatapb.Shard{ 1993 IsPrimaryServing: true, 1994 }, 1995 }, 1996 "-": { 1997 Name: "-", 1998 Shard: &topodatapb.Shard{ 1999 IsPrimaryServing: false, 2000 }, 2001 }, 2002 }, 2003 }, 2004 }, 2005 }, 2006 GetSchemaResults: map[string]struct { 2007 Response *vtctldatapb.GetSchemaResponse 2008 Error error 2009 }{ 2010 "zone1-0000000100": { 2011 Response: &vtctldatapb.GetSchemaResponse{ 2012 Schema: &tabletmanagerdatapb.SchemaDefinition{ 2013 DatabaseSchema: "CREATE DATABASE vt_testkeyspcae", 2014 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 2015 { 2016 Name: "foo", 2017 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 2018 DataLength: 100, 2019 RowCount: 5, 2020 }, 2021 }, 2022 }, 2023 }, 2024 }, 2025 "zone1-0000000200": { 2026 Response: &vtctldatapb.GetSchemaResponse{ 2027 Schema: &tabletmanagerdatapb.SchemaDefinition{ 2028 DatabaseSchema: "CREATE DATABASE vt_testkeyspcae", 2029 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 2030 { 2031 Name: "foo", 2032 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 2033 DataLength: 200, 2034 RowCount: 420, 2035 }, 2036 }, 2037 }, 2038 }, 2039 }, 2040 "zone1-0000000300": { 2041 Response: &vtctldatapb.GetSchemaResponse{ 2042 Schema: &tabletmanagerdatapb.SchemaDefinition{ 2043 DatabaseSchema: "CREATE DATABASE vt_testkeyspcae", 2044 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 2045 { 2046 Name: "foo", 2047 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 2048 DataLength: 1000, 2049 RowCount: 200, 2050 }, 2051 }, 2052 }, 2053 }, 2054 }, 2055 }, 2056 }, 2057 }, 2058 keyspace: "testkeyspace", 2059 opts: cluster.GetSchemaOptions{ 2060 TableSizeOptions: &vtadminpb.GetSchemaTableSizeOptions{ 2061 AggregateSizes: true, 2062 IncludeNonServingShards: true, 2063 }, 2064 }, 2065 expected: &vtadminpb.Schema{ 2066 Cluster: &vtadminpb.Cluster{ 2067 Id: "c0", 2068 Name: "cluster0", 2069 }, 2070 Keyspace: "testkeyspace", 2071 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 2072 { 2073 Name: "foo", 2074 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 2075 DataLength: 0, 2076 RowCount: 0, 2077 }, 2078 }, 2079 TableSizes: map[string]*vtadminpb.Schema_TableSize{ 2080 "foo": { 2081 DataLength: 100 + 200 + 1000, 2082 RowCount: 5 + 420 + 200, 2083 ByShard: map[string]*vtadminpb.Schema_ShardTableSize{ 2084 "-80": { 2085 DataLength: 100, 2086 RowCount: 5, 2087 }, 2088 "80-": { 2089 DataLength: 200, 2090 RowCount: 420, 2091 }, 2092 "-": { 2093 DataLength: 1000, 2094 RowCount: 200, 2095 }, 2096 }, 2097 }, 2098 }, 2099 }, 2100 shouldErr: false, 2101 }, 2102 { 2103 name: "no serving tablets found for shard", 2104 cfg: testutil.TestClusterConfig{ 2105 Cluster: &vtadminpb.Cluster{ 2106 Id: "c0", 2107 Name: "cluster0", 2108 }, 2109 Tablets: []*vtadminpb.Tablet{ 2110 { 2111 Tablet: &topodatapb.Tablet{ 2112 Alias: &topodatapb.TabletAlias{ 2113 Cell: "zone1", 2114 Uid: 100, 2115 }, 2116 Keyspace: "testkeyspace", 2117 Shard: "-80", 2118 }, 2119 State: vtadminpb.Tablet_NOT_SERVING, 2120 }, 2121 { 2122 Tablet: &topodatapb.Tablet{ 2123 Alias: &topodatapb.TabletAlias{ 2124 Cell: "zone1", 2125 Uid: 200, 2126 }, 2127 Keyspace: "testkeyspace", 2128 Shard: "80-", 2129 }, 2130 State: vtadminpb.Tablet_SERVING, 2131 }, 2132 }, 2133 VtctldClient: &fakevtctldclient.VtctldClient{ 2134 FindAllShardsInKeyspaceResults: map[string]struct { 2135 Response *vtctldatapb.FindAllShardsInKeyspaceResponse 2136 Error error 2137 }{ 2138 "testkeyspace": { 2139 Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{ 2140 Shards: map[string]*vtctldatapb.Shard{ 2141 "-80": { 2142 Name: "-80", 2143 Shard: &topodatapb.Shard{ 2144 IsPrimaryServing: true, 2145 PrimaryAlias: &topodatapb.TabletAlias{ 2146 Cell: "zone1", 2147 Uid: 100, 2148 }, 2149 }, 2150 }, 2151 "80-": { 2152 Name: "80-", 2153 Shard: &topodatapb.Shard{ 2154 IsPrimaryServing: true, 2155 PrimaryAlias: &topodatapb.TabletAlias{ 2156 Cell: "zone1", 2157 Uid: 200, 2158 }, 2159 }, 2160 }, 2161 "-": { 2162 Name: "-", 2163 Shard: &topodatapb.Shard{ 2164 IsPrimaryServing: false, 2165 }, 2166 }, 2167 }, 2168 }, 2169 }, 2170 }, 2171 GetSchemaResults: map[string]struct { 2172 Response *vtctldatapb.GetSchemaResponse 2173 Error error 2174 }{ 2175 "zone1-0000000100": { 2176 Response: &vtctldatapb.GetSchemaResponse{ 2177 Schema: &tabletmanagerdatapb.SchemaDefinition{ 2178 DatabaseSchema: "CREATE DATABASE vt_testkeyspcae", 2179 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 2180 { 2181 Name: "foo", 2182 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 2183 DataLength: 100, 2184 RowCount: 5, 2185 }, 2186 }, 2187 }, 2188 }, 2189 }, 2190 "zone1-0000000200": { 2191 Response: &vtctldatapb.GetSchemaResponse{ 2192 Schema: &tabletmanagerdatapb.SchemaDefinition{ 2193 DatabaseSchema: "CREATE DATABASE vt_testkeyspcae", 2194 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 2195 { 2196 Name: "foo", 2197 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 2198 DataLength: 200, 2199 RowCount: 420, 2200 }, 2201 }, 2202 }, 2203 }, 2204 }, 2205 }, 2206 }, 2207 }, 2208 keyspace: "testkeyspace", 2209 opts: cluster.GetSchemaOptions{ 2210 TableSizeOptions: &vtadminpb.GetSchemaTableSizeOptions{ 2211 AggregateSizes: true, 2212 }, 2213 }, 2214 expected: nil, 2215 shouldErr: true, 2216 }, 2217 { 2218 name: "ignore TableNamesOnly", 2219 cfg: testutil.TestClusterConfig{ 2220 Cluster: &vtadminpb.Cluster{ 2221 Id: "c0", 2222 Name: "cluster0", 2223 }, 2224 Tablets: []*vtadminpb.Tablet{ 2225 { 2226 Tablet: &topodatapb.Tablet{ 2227 Alias: &topodatapb.TabletAlias{ 2228 Cell: "zone1", 2229 Uid: 100, 2230 }, 2231 Keyspace: "testkeyspace", 2232 Shard: "-80", 2233 }, 2234 State: vtadminpb.Tablet_SERVING, 2235 }, 2236 { 2237 Tablet: &topodatapb.Tablet{ 2238 Alias: &topodatapb.TabletAlias{ 2239 Cell: "zone1", 2240 Uid: 200, 2241 }, 2242 Keyspace: "testkeyspace", 2243 Shard: "80-", 2244 }, 2245 State: vtadminpb.Tablet_SERVING, 2246 }, 2247 }, 2248 VtctldClient: &fakevtctldclient.VtctldClient{ 2249 FindAllShardsInKeyspaceResults: map[string]struct { 2250 Response *vtctldatapb.FindAllShardsInKeyspaceResponse 2251 Error error 2252 }{ 2253 "testkeyspace": { 2254 Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{ 2255 Shards: map[string]*vtctldatapb.Shard{ 2256 "-80": { 2257 Name: "-80", 2258 Shard: &topodatapb.Shard{ 2259 IsPrimaryServing: true, 2260 PrimaryAlias: &topodatapb.TabletAlias{ 2261 Cell: "zone1", 2262 Uid: 100, 2263 }, 2264 }, 2265 }, 2266 "80-": { 2267 Name: "80-", 2268 Shard: &topodatapb.Shard{ 2269 IsPrimaryServing: true, 2270 PrimaryAlias: &topodatapb.TabletAlias{ 2271 Cell: "zone1", 2272 Uid: 200, 2273 }, 2274 }, 2275 }, 2276 "-": { 2277 Name: "-", 2278 Shard: &topodatapb.Shard{ 2279 IsPrimaryServing: false, 2280 }, 2281 }, 2282 }, 2283 }, 2284 }, 2285 }, 2286 GetSchemaResults: map[string]struct { 2287 Response *vtctldatapb.GetSchemaResponse 2288 Error error 2289 }{ 2290 "zone1-0000000100": { 2291 Response: &vtctldatapb.GetSchemaResponse{ 2292 Schema: &tabletmanagerdatapb.SchemaDefinition{ 2293 DatabaseSchema: "CREATE DATABASE vt_testkeyspcae", 2294 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 2295 { 2296 Name: "foo", 2297 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 2298 DataLength: 100, 2299 RowCount: 5, 2300 }, 2301 }, 2302 }, 2303 }, 2304 }, 2305 "zone1-0000000200": { 2306 Response: &vtctldatapb.GetSchemaResponse{ 2307 Schema: &tabletmanagerdatapb.SchemaDefinition{ 2308 DatabaseSchema: "CREATE DATABASE vt_testkeyspcae", 2309 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 2310 { 2311 Name: "foo", 2312 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 2313 DataLength: 200, 2314 RowCount: 420, 2315 }, 2316 }, 2317 }, 2318 }, 2319 }, 2320 }, 2321 }, 2322 }, 2323 keyspace: "testkeyspace", 2324 opts: cluster.GetSchemaOptions{ 2325 BaseRequest: &vtctldatapb.GetSchemaRequest{ 2326 TableNamesOnly: true, // Just checking things to blow up if this gets set. 2327 }, 2328 TableSizeOptions: &vtadminpb.GetSchemaTableSizeOptions{ 2329 AggregateSizes: true, 2330 }, 2331 }, 2332 expected: &vtadminpb.Schema{ 2333 Cluster: &vtadminpb.Cluster{ 2334 Id: "c0", 2335 Name: "cluster0", 2336 }, 2337 Keyspace: "testkeyspace", 2338 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 2339 { 2340 Name: "foo", 2341 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 2342 DataLength: 0, 2343 RowCount: 0, 2344 }, 2345 }, 2346 TableSizes: map[string]*vtadminpb.Schema_TableSize{ 2347 "foo": { 2348 DataLength: 100 + 200, 2349 RowCount: 5 + 420, 2350 ByShard: map[string]*vtadminpb.Schema_ShardTableSize{ 2351 "-80": { 2352 DataLength: 100, 2353 RowCount: 5, 2354 }, 2355 "80-": { 2356 DataLength: 200, 2357 RowCount: 420, 2358 }, 2359 }, 2360 }, 2361 }, 2362 }, 2363 shouldErr: false, 2364 }, 2365 { 2366 name: "single GetSchema error fails the request", 2367 cfg: testutil.TestClusterConfig{ 2368 Cluster: &vtadminpb.Cluster{ 2369 Id: "c0", 2370 Name: "cluster0", 2371 }, 2372 Tablets: []*vtadminpb.Tablet{ 2373 { 2374 Tablet: &topodatapb.Tablet{ 2375 Alias: &topodatapb.TabletAlias{ 2376 Cell: "zone1", 2377 Uid: 100, 2378 }, 2379 Keyspace: "testkeyspace", 2380 Shard: "-80", 2381 }, 2382 State: vtadminpb.Tablet_SERVING, 2383 }, 2384 { 2385 Tablet: &topodatapb.Tablet{ 2386 Alias: &topodatapb.TabletAlias{ 2387 Cell: "zone1", 2388 Uid: 200, 2389 }, 2390 Keyspace: "testkeyspace", 2391 Shard: "80-", 2392 }, 2393 State: vtadminpb.Tablet_SERVING, 2394 }, 2395 }, 2396 VtctldClient: &fakevtctldclient.VtctldClient{ 2397 FindAllShardsInKeyspaceResults: map[string]struct { 2398 Response *vtctldatapb.FindAllShardsInKeyspaceResponse 2399 Error error 2400 }{ 2401 "testkeyspace": { 2402 Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{ 2403 Shards: map[string]*vtctldatapb.Shard{ 2404 "-80": { 2405 Name: "-80", 2406 Shard: &topodatapb.Shard{ 2407 IsPrimaryServing: true, 2408 PrimaryAlias: &topodatapb.TabletAlias{ 2409 Cell: "zone1", 2410 Uid: 100, 2411 }, 2412 }, 2413 }, 2414 "80-": { 2415 Name: "80-", 2416 Shard: &topodatapb.Shard{ 2417 IsPrimaryServing: true, 2418 PrimaryAlias: &topodatapb.TabletAlias{ 2419 Cell: "zone1", 2420 Uid: 200, 2421 }, 2422 }, 2423 }, 2424 "-": { 2425 Name: "-", 2426 Shard: &topodatapb.Shard{ 2427 IsPrimaryServing: false, 2428 }, 2429 }, 2430 }, 2431 }, 2432 }, 2433 }, 2434 GetSchemaResults: map[string]struct { 2435 Response *vtctldatapb.GetSchemaResponse 2436 Error error 2437 }{ 2438 "zone1-0000000100": { 2439 Response: &vtctldatapb.GetSchemaResponse{ 2440 Schema: &tabletmanagerdatapb.SchemaDefinition{ 2441 DatabaseSchema: "CREATE DATABASE vt_testkeyspcae", 2442 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 2443 { 2444 Name: "foo", 2445 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 2446 DataLength: 100, 2447 RowCount: 5, 2448 }, 2449 }, 2450 }, 2451 }, 2452 }, 2453 "zone1-0000000200": { 2454 Error: assert.AnError, 2455 }, 2456 }, 2457 }, 2458 }, 2459 keyspace: "testkeyspace", 2460 opts: cluster.GetSchemaOptions{ 2461 TableSizeOptions: &vtadminpb.GetSchemaTableSizeOptions{ 2462 AggregateSizes: true, 2463 }, 2464 }, 2465 expected: nil, 2466 shouldErr: true, 2467 }, 2468 { 2469 name: "FindAllShardsInKeyspace error", 2470 cfg: testutil.TestClusterConfig{ 2471 Cluster: &vtadminpb.Cluster{ 2472 Id: "c0", 2473 Name: "cluster0", 2474 }, 2475 Tablets: []*vtadminpb.Tablet{ 2476 { 2477 Tablet: &topodatapb.Tablet{ 2478 Alias: &topodatapb.TabletAlias{ 2479 Cell: "zone1", 2480 Uid: 100, 2481 }, 2482 Keyspace: "testkeyspace", 2483 Shard: "-80", 2484 }, 2485 State: vtadminpb.Tablet_SERVING, 2486 }, 2487 { 2488 Tablet: &topodatapb.Tablet{ 2489 Alias: &topodatapb.TabletAlias{ 2490 Cell: "zone1", 2491 Uid: 200, 2492 }, 2493 Keyspace: "testkeyspace", 2494 Shard: "80-", 2495 }, 2496 State: vtadminpb.Tablet_SERVING, 2497 }, 2498 }, 2499 VtctldClient: &fakevtctldclient.VtctldClient{ 2500 FindAllShardsInKeyspaceResults: map[string]struct { 2501 Response *vtctldatapb.FindAllShardsInKeyspaceResponse 2502 Error error 2503 }{ 2504 "testkeyspace": { 2505 Error: assert.AnError, 2506 }, 2507 }, 2508 GetSchemaResults: map[string]struct { 2509 Response *vtctldatapb.GetSchemaResponse 2510 Error error 2511 }{ 2512 "zone1-0000000100": { 2513 Response: &vtctldatapb.GetSchemaResponse{ 2514 Schema: &tabletmanagerdatapb.SchemaDefinition{ 2515 DatabaseSchema: "CREATE DATABASE vt_testkeyspcae", 2516 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 2517 { 2518 Name: "foo", 2519 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 2520 DataLength: 100, 2521 RowCount: 5, 2522 }, 2523 }, 2524 }, 2525 }, 2526 }, 2527 "zone1-0000000200": { 2528 Response: &vtctldatapb.GetSchemaResponse{ 2529 Schema: &tabletmanagerdatapb.SchemaDefinition{ 2530 DatabaseSchema: "CREATE DATABASE vt_testkeyspcae", 2531 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 2532 { 2533 Name: "foo", 2534 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 2535 DataLength: 200, 2536 RowCount: 420, 2537 }, 2538 }, 2539 }, 2540 }, 2541 }, 2542 }, 2543 }, 2544 }, 2545 keyspace: "testkeyspace", 2546 opts: cluster.GetSchemaOptions{ 2547 TableSizeOptions: &vtadminpb.GetSchemaTableSizeOptions{ 2548 AggregateSizes: true, 2549 }, 2550 }, 2551 expected: nil, 2552 shouldErr: true, 2553 }, 2554 { 2555 name: "tablet filtering checks keyspace field", 2556 cfg: testutil.TestClusterConfig{ 2557 Cluster: &vtadminpb.Cluster{ 2558 Id: "c0", 2559 Name: "cluster0", 2560 }, 2561 Tablets: []*vtadminpb.Tablet{ 2562 { 2563 Tablet: &topodatapb.Tablet{ 2564 Alias: &topodatapb.TabletAlias{ 2565 Cell: "zone1", 2566 Uid: 100, 2567 }, 2568 Keyspace: "testkeyspace", 2569 Shard: "-80", 2570 }, 2571 State: vtadminpb.Tablet_SERVING, 2572 }, 2573 { 2574 Tablet: &topodatapb.Tablet{ 2575 Alias: &topodatapb.TabletAlias{ 2576 Cell: "zone1", 2577 Uid: 300, 2578 }, 2579 // Note this is for another keyspace, so we fail to find a tablet for testkeyspace/-80. 2580 Keyspace: "otherkeyspace", 2581 Shard: "80-", 2582 }, 2583 State: vtadminpb.Tablet_SERVING, 2584 }, 2585 }, 2586 VtctldClient: &fakevtctldclient.VtctldClient{ 2587 FindAllShardsInKeyspaceResults: map[string]struct { 2588 Response *vtctldatapb.FindAllShardsInKeyspaceResponse 2589 Error error 2590 }{ 2591 "testkeyspace": { 2592 Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{ 2593 Shards: map[string]*vtctldatapb.Shard{ 2594 "-80": { 2595 Name: "-80", 2596 Shard: &topodatapb.Shard{ 2597 IsPrimaryServing: true, 2598 PrimaryAlias: &topodatapb.TabletAlias{ 2599 Cell: "zone1", 2600 Uid: 100, 2601 }, 2602 }, 2603 }, 2604 "80-": { 2605 Name: "80-", 2606 Shard: &topodatapb.Shard{ 2607 IsPrimaryServing: true, 2608 PrimaryAlias: &topodatapb.TabletAlias{ 2609 Cell: "zone1", 2610 Uid: 200, 2611 }, 2612 }, 2613 }, 2614 }, 2615 }, 2616 }, 2617 }, 2618 GetSchemaResults: map[string]struct { 2619 Response *vtctldatapb.GetSchemaResponse 2620 Error error 2621 }{ 2622 "zone1-0000000100": { 2623 Response: &vtctldatapb.GetSchemaResponse{ 2624 Schema: &tabletmanagerdatapb.SchemaDefinition{ 2625 DatabaseSchema: "CREATE DATABASE vt_testkeyspcae", 2626 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 2627 { 2628 Name: "foo", 2629 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 2630 DataLength: 100, 2631 RowCount: 5, 2632 }, 2633 }, 2634 }, 2635 }, 2636 }, 2637 "zone1-0000000200": { 2638 Response: &vtctldatapb.GetSchemaResponse{ 2639 Schema: &tabletmanagerdatapb.SchemaDefinition{ 2640 DatabaseSchema: "CREATE DATABASE vt_testkeyspcae", 2641 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 2642 { 2643 Name: "foo", 2644 Schema: "CREATE TABLE foo (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 2645 DataLength: 200, 2646 RowCount: 420, 2647 }, 2648 }, 2649 }, 2650 }, 2651 }, 2652 "zone1-0000000300": { 2653 Response: &vtctldatapb.GetSchemaResponse{ 2654 Schema: &tabletmanagerdatapb.SchemaDefinition{ 2655 DatabaseSchema: "CREATE DATABASE vt_otherkeyspacekeyspcae", 2656 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 2657 { 2658 Name: "bar", 2659 Schema: "CREATE TABLE bar (\n\tid INT(11) NOT NULL\n) ENGINE=InnoDB", 2660 DataLength: 101, 2661 RowCount: 202, 2662 }, 2663 }, 2664 }, 2665 }, 2666 }, 2667 }, 2668 }, 2669 }, 2670 keyspace: "testkeyspace", 2671 opts: cluster.GetSchemaOptions{ 2672 TableSizeOptions: &vtadminpb.GetSchemaTableSizeOptions{ 2673 AggregateSizes: true, 2674 }, 2675 }, 2676 expected: nil, 2677 shouldErr: true, 2678 }, 2679 } 2680 2681 for _, tt := range tests { 2682 tt := tt 2683 2684 t.Run(tt.name, func(t *testing.T) { 2685 t.Parallel() 2686 2687 if tt.keyspace == "" { 2688 t.SkipNow() 2689 } 2690 2691 c := testutil.BuildCluster(t, tt.cfg) 2692 schema, err := c.GetSchema(ctx, tt.keyspace, tt.opts) 2693 if tt.shouldErr { 2694 assert.Error(t, err) 2695 2696 return 2697 } 2698 2699 // Clone so our mutation below doesn't trip the race detector. 2700 schema = proto.Clone(schema).(*vtadminpb.Schema) 2701 2702 if schema.TableDefinitions != nil { 2703 // For simplicity, we're going to assert only on the state 2704 // of the aggregated sizes (in schema.TableSizes), since the 2705 // TableDefinitions size values depends on tablet iteration 2706 // order, and that's not something we're interested in 2707 // coupling the implementation to. 2708 for _, td := range schema.TableDefinitions { 2709 td.DataLength = 0 2710 td.RowCount = 0 2711 } 2712 } 2713 2714 assert.NoError(t, err) 2715 testutil.AssertSchemaSlicesEqual(t, []*vtadminpb.Schema{tt.expected}, []*vtadminpb.Schema{schema}) 2716 }) 2717 } 2718 }) 2719 } 2720 2721 func TestGetShardReplicationPositions(t *testing.T) { 2722 t.Parallel() 2723 2724 ctx := context.Background() 2725 tests := []struct { 2726 name string 2727 cfg testutil.TestClusterConfig 2728 req *vtadminpb.GetShardReplicationPositionsRequest 2729 expected []*vtadminpb.ClusterShardReplicationPosition 2730 shouldErr bool 2731 }{ 2732 { 2733 cfg: testutil.TestClusterConfig{ 2734 Cluster: &vtadminpb.Cluster{ 2735 Id: "c1", 2736 Name: "cluster1", 2737 }, 2738 VtctldClient: &fakevtctldclient.VtctldClient{ 2739 GetKeyspaceResults: map[string]struct { 2740 Response *vtctldatapb.GetKeyspaceResponse 2741 Error error 2742 }{ 2743 "ks": { 2744 Response: &vtctldatapb.GetKeyspaceResponse{ 2745 Keyspace: &vtctldatapb.Keyspace{ 2746 Keyspace: &topodatapb.Keyspace{}, 2747 }, 2748 }, 2749 }, 2750 }, 2751 FindAllShardsInKeyspaceResults: map[string]struct { 2752 Response *vtctldatapb.FindAllShardsInKeyspaceResponse 2753 Error error 2754 }{ 2755 "ks": { 2756 Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{ 2757 Shards: map[string]*vtctldatapb.Shard{ 2758 "-": { 2759 Keyspace: "ks", 2760 Name: "-", 2761 Shard: &topodatapb.Shard{}, 2762 }, 2763 }, 2764 }, 2765 }, 2766 }, 2767 ShardReplicationPositionsResults: map[string]struct { 2768 Response *vtctldatapb.ShardReplicationPositionsResponse 2769 Error error 2770 }{ 2771 "ks/-": { 2772 Response: &vtctldatapb.ShardReplicationPositionsResponse{ 2773 ReplicationStatuses: map[string]*replicationdatapb.Status{ 2774 "zone1-001": { 2775 IoState: int32(mysql.ReplicationStateStopped), 2776 SqlState: int32(mysql.ReplicationStateStopped), 2777 Position: "MySQL56/08d0dbbb-be29-11eb-9fea-0aafb9701138:1-109848265", 2778 }, 2779 "zone1-002": { // Note: in reality other fields will be set on replicating hosts as well, but this is sufficient to illustrate in the testing. 2780 IoState: int32(mysql.ReplicationStateRunning), 2781 SqlState: int32(mysql.ReplicationStateRunning), 2782 Position: "MySQL56/08d0dbbb-be29-11eb-9fea-0aafb9701138:1-109848265", 2783 }, 2784 "zone1-003": { 2785 IoState: int32(mysql.ReplicationStateRunning), 2786 SqlState: int32(mysql.ReplicationStateRunning), 2787 Position: "MySQL56/08d0dbbb-be29-11eb-9fea-0aafb9701138:1-109848265", 2788 }, 2789 }, 2790 TabletMap: map[string]*topodatapb.Tablet{ 2791 "zone1-001": { 2792 Keyspace: "ks", 2793 Shard: "-", 2794 Type: topodatapb.TabletType_PRIMARY, 2795 Alias: &topodatapb.TabletAlias{ 2796 Cell: "zone1", 2797 Uid: 1, 2798 }, 2799 }, 2800 "zone1-002": { 2801 Keyspace: "ks", 2802 Shard: "-", 2803 Type: topodatapb.TabletType_REPLICA, 2804 Alias: &topodatapb.TabletAlias{ 2805 Cell: "zone1", 2806 Uid: 2, 2807 }, 2808 }, 2809 "zone1-003": { 2810 Keyspace: "ks", 2811 Shard: "-", 2812 Type: topodatapb.TabletType_RDONLY, 2813 Alias: &topodatapb.TabletAlias{ 2814 Cell: "zone1", 2815 Uid: 3, 2816 }, 2817 }, 2818 }, 2819 }, 2820 }, 2821 }, 2822 }, 2823 }, 2824 req: &vtadminpb.GetShardReplicationPositionsRequest{ 2825 KeyspaceShards: []string{"ks/-"}, 2826 }, 2827 expected: []*vtadminpb.ClusterShardReplicationPosition{ 2828 { 2829 Cluster: &vtadminpb.Cluster{ 2830 Id: "c1", 2831 Name: "cluster1", 2832 }, 2833 Keyspace: "ks", 2834 Shard: "-", 2835 PositionInfo: &vtctldatapb.ShardReplicationPositionsResponse{ 2836 ReplicationStatuses: map[string]*replicationdatapb.Status{ 2837 "zone1-001": { 2838 IoState: int32(mysql.ReplicationStateStopped), 2839 SqlState: int32(mysql.ReplicationStateStopped), 2840 Position: "MySQL56/08d0dbbb-be29-11eb-9fea-0aafb9701138:1-109848265", 2841 }, 2842 "zone1-002": { 2843 IoState: int32(mysql.ReplicationStateRunning), 2844 SqlState: int32(mysql.ReplicationStateRunning), 2845 Position: "MySQL56/08d0dbbb-be29-11eb-9fea-0aafb9701138:1-109848265", 2846 }, 2847 "zone1-003": { 2848 IoState: int32(mysql.ReplicationStateRunning), 2849 SqlState: int32(mysql.ReplicationStateRunning), 2850 Position: "MySQL56/08d0dbbb-be29-11eb-9fea-0aafb9701138:1-109848265", 2851 }, 2852 }, 2853 TabletMap: map[string]*topodatapb.Tablet{ 2854 "zone1-001": { 2855 Keyspace: "ks", 2856 Shard: "-", 2857 Type: topodatapb.TabletType_PRIMARY, 2858 Alias: &topodatapb.TabletAlias{ 2859 Cell: "zone1", 2860 Uid: 1, 2861 }, 2862 }, 2863 "zone1-002": { 2864 Keyspace: "ks", 2865 Shard: "-", 2866 Type: topodatapb.TabletType_REPLICA, 2867 Alias: &topodatapb.TabletAlias{ 2868 Cell: "zone1", 2869 Uid: 2, 2870 }, 2871 }, 2872 "zone1-003": { 2873 Keyspace: "ks", 2874 Shard: "-", 2875 Type: topodatapb.TabletType_RDONLY, 2876 Alias: &topodatapb.TabletAlias{ 2877 Cell: "zone1", 2878 Uid: 3, 2879 }, 2880 }, 2881 }, 2882 }, 2883 }, 2884 }, 2885 }, 2886 { 2887 name: "error", 2888 cfg: testutil.TestClusterConfig{ 2889 Cluster: &vtadminpb.Cluster{ 2890 Id: "c1", 2891 Name: "cluster1", 2892 }, 2893 VtctldClient: &fakevtctldclient.VtctldClient{ 2894 GetKeyspaceResults: map[string]struct { 2895 Response *vtctldatapb.GetKeyspaceResponse 2896 Error error 2897 }{ 2898 "ks": { 2899 Response: &vtctldatapb.GetKeyspaceResponse{ 2900 Keyspace: &vtctldatapb.Keyspace{ 2901 Keyspace: &topodatapb.Keyspace{}, 2902 }, 2903 }, 2904 }, 2905 }, 2906 FindAllShardsInKeyspaceResults: map[string]struct { 2907 Response *vtctldatapb.FindAllShardsInKeyspaceResponse 2908 Error error 2909 }{ 2910 "ks": { 2911 Response: &vtctldatapb.FindAllShardsInKeyspaceResponse{ 2912 Shards: map[string]*vtctldatapb.Shard{ 2913 "-": { 2914 Keyspace: "ks", 2915 Name: "-", 2916 Shard: &topodatapb.Shard{}, 2917 }, 2918 }, 2919 }, 2920 }, 2921 }, 2922 ShardReplicationPositionsResults: map[string]struct { 2923 Response *vtctldatapb.ShardReplicationPositionsResponse 2924 Error error 2925 }{ 2926 "ks/-": { 2927 Error: assert.AnError, 2928 }, 2929 }, 2930 }, 2931 }, 2932 req: &vtadminpb.GetShardReplicationPositionsRequest{ 2933 KeyspaceShards: []string{"ks/-"}, 2934 }, 2935 expected: nil, 2936 shouldErr: true, 2937 }, 2938 } 2939 2940 for _, tt := range tests { 2941 tt := tt 2942 2943 t.Run(tt.name, func(t *testing.T) { 2944 t.Parallel() 2945 2946 c := testutil.BuildCluster(t, tt.cfg) 2947 2948 resp, err := c.GetShardReplicationPositions(ctx, tt.req) 2949 if tt.shouldErr { 2950 assert.Error(t, err) 2951 return 2952 } 2953 2954 require.NoError(t, err) 2955 assert.Equal(t, tt.expected, resp) 2956 }) 2957 } 2958 } 2959 2960 func TestGetVSchema(t *testing.T) { 2961 t.Parallel() 2962 2963 tests := []struct { 2964 name string 2965 cfg testutil.TestClusterConfig 2966 keyspace string 2967 expected *vtadminpb.VSchema 2968 shouldErr bool 2969 }{ 2970 { 2971 name: "success", 2972 cfg: testutil.TestClusterConfig{ 2973 Cluster: &vtadminpb.Cluster{ 2974 Id: "c0", 2975 Name: "cluster0", 2976 }, 2977 VtctldClient: &fakevtctldclient.VtctldClient{ 2978 GetVSchemaResults: map[string]struct { 2979 Response *vtctldatapb.GetVSchemaResponse 2980 Error error 2981 }{ 2982 "testkeyspace": { 2983 Response: &vtctldatapb.GetVSchemaResponse{ 2984 VSchema: &vschemapb.Keyspace{Sharded: true}, 2985 }, 2986 }, 2987 }, 2988 }, 2989 }, 2990 keyspace: "testkeyspace", 2991 expected: &vtadminpb.VSchema{ 2992 Cluster: &vtadminpb.Cluster{ 2993 Id: "c0", 2994 Name: "cluster0", 2995 }, 2996 Name: "testkeyspace", 2997 VSchema: &vschemapb.Keyspace{Sharded: true}, 2998 }, 2999 shouldErr: false, 3000 }, 3001 { 3002 name: "error", 3003 cfg: testutil.TestClusterConfig{ 3004 Cluster: &vtadminpb.Cluster{ 3005 Id: "c0", 3006 Name: "cluster0", 3007 }, 3008 VtctldClient: &fakevtctldclient.VtctldClient{ 3009 GetVSchemaResults: map[string]struct { 3010 Response *vtctldatapb.GetVSchemaResponse 3011 Error error 3012 }{ 3013 "testkeyspace": { 3014 Response: &vtctldatapb.GetVSchemaResponse{ 3015 VSchema: &vschemapb.Keyspace{Sharded: true}, 3016 }, 3017 }, 3018 }, 3019 }, 3020 }, 3021 keyspace: "notfound", 3022 expected: nil, 3023 shouldErr: true, 3024 }, 3025 } 3026 3027 ctx := context.Background() 3028 3029 for _, tt := range tests { 3030 tt := tt 3031 3032 t.Run(tt.name, func(t *testing.T) { 3033 t.Parallel() 3034 3035 cluster := testutil.BuildCluster(t, tt.cfg) 3036 3037 vschema, err := cluster.GetVSchema(ctx, tt.keyspace) 3038 if tt.shouldErr { 3039 assert.Error(t, err) 3040 3041 return 3042 } 3043 3044 assert.NoError(t, err) 3045 assert.Equal(t, tt.expected, vschema) 3046 }) 3047 } 3048 } 3049 3050 func TestGetWorkflow(t *testing.T) { 3051 t.Parallel() 3052 3053 tests := []struct { 3054 name string 3055 cfg testutil.TestClusterConfig 3056 keyspace string 3057 workflow string 3058 opts cluster.GetWorkflowOptions 3059 expected *vtadminpb.Workflow 3060 shouldErr bool 3061 }{ 3062 { 3063 name: "found", 3064 cfg: testutil.TestClusterConfig{ 3065 Cluster: &vtadminpb.Cluster{ 3066 Id: "c1", 3067 Name: "cluster1", 3068 }, 3069 VtctldClient: &fakevtctldclient.VtctldClient{ 3070 GetWorkflowsResults: map[string]struct { 3071 Response *vtctldatapb.GetWorkflowsResponse 3072 Error error 3073 }{ 3074 "ks1": { 3075 Response: &vtctldatapb.GetWorkflowsResponse{ 3076 Workflows: []*vtctldatapb.Workflow{ 3077 { 3078 Name: "workflow1", 3079 }, 3080 { 3081 Name: "workflow2", 3082 }, 3083 }, 3084 }, 3085 }, 3086 }, 3087 }, 3088 }, 3089 keyspace: "ks1", 3090 workflow: "workflow2", 3091 expected: &vtadminpb.Workflow{ 3092 Cluster: &vtadminpb.Cluster{ 3093 Id: "c1", 3094 Name: "cluster1", 3095 }, 3096 Keyspace: "ks1", 3097 Workflow: &vtctldatapb.Workflow{ 3098 Name: "workflow2", 3099 }, 3100 }, 3101 shouldErr: false, 3102 }, 3103 { 3104 name: "error getting workflows", 3105 cfg: testutil.TestClusterConfig{ 3106 Cluster: &vtadminpb.Cluster{ 3107 Id: "c1", 3108 Name: "cluster1", 3109 }, 3110 VtctldClient: &fakevtctldclient.VtctldClient{ 3111 GetWorkflowsResults: map[string]struct { 3112 Response *vtctldatapb.GetWorkflowsResponse 3113 Error error 3114 }{ 3115 "ks1": { 3116 Error: assert.AnError, 3117 }, 3118 }, 3119 }, 3120 }, 3121 keyspace: "ks1", 3122 workflow: "workflow2", 3123 expected: nil, 3124 shouldErr: true, 3125 }, 3126 { 3127 name: "no workflows found", 3128 cfg: testutil.TestClusterConfig{ 3129 Cluster: &vtadminpb.Cluster{ 3130 Id: "c1", 3131 Name: "cluster1", 3132 }, 3133 VtctldClient: &fakevtctldclient.VtctldClient{ 3134 GetWorkflowsResults: map[string]struct { 3135 Response *vtctldatapb.GetWorkflowsResponse 3136 Error error 3137 }{ 3138 "ks1": { 3139 Response: &vtctldatapb.GetWorkflowsResponse{ 3140 Workflows: []*vtctldatapb.Workflow{}, 3141 }, 3142 }, 3143 }, 3144 }, 3145 }, 3146 keyspace: "ks1", 3147 workflow: "workflow2", 3148 expected: nil, 3149 shouldErr: true, 3150 }, 3151 { 3152 name: "multiple workflows found", 3153 cfg: testutil.TestClusterConfig{ 3154 Cluster: &vtadminpb.Cluster{ 3155 Id: "c1", 3156 Name: "cluster1", 3157 }, 3158 VtctldClient: &fakevtctldclient.VtctldClient{ 3159 GetWorkflowsResults: map[string]struct { 3160 Response *vtctldatapb.GetWorkflowsResponse 3161 Error error 3162 }{ 3163 "ks1": { 3164 Response: &vtctldatapb.GetWorkflowsResponse{ 3165 Workflows: []*vtctldatapb.Workflow{ 3166 { 3167 Name: "duplicate", 3168 }, 3169 { 3170 Name: "duplicate", 3171 }, 3172 }, 3173 }, 3174 }, 3175 }, 3176 }, 3177 }, 3178 keyspace: "ks1", 3179 workflow: "duplicate", 3180 expected: nil, 3181 shouldErr: true, 3182 }, 3183 } 3184 3185 ctx := context.Background() 3186 3187 for _, tt := range tests { 3188 tt := tt 3189 3190 t.Run(tt.name, func(t *testing.T) { 3191 t.Parallel() 3192 3193 c := testutil.BuildCluster(t, tt.cfg) 3194 workflow, err := c.GetWorkflow(ctx, tt.keyspace, tt.workflow, tt.opts) 3195 if tt.shouldErr { 3196 assert.Error(t, err) 3197 3198 return 3199 } 3200 3201 assert.NoError(t, err) 3202 assert.Equal(t, tt.expected, workflow) 3203 }) 3204 } 3205 } 3206 3207 func TestGetWorkflows(t *testing.T) { 3208 t.Parallel() 3209 3210 // Note: GetWorkflows is almost entirely a passthrough to FindWorkflows, so 3211 // these test cases mostly just verify we're calling that function more or 3212 // less correctly. 3213 3214 tests := []struct { 3215 name string 3216 cfg testutil.TestClusterConfig 3217 keyspaces []string 3218 opts cluster.GetWorkflowsOptions 3219 expected *vtadminpb.ClusterWorkflows 3220 shouldErr bool 3221 }{ 3222 { 3223 name: "success", 3224 cfg: testutil.TestClusterConfig{ 3225 Cluster: &vtadminpb.Cluster{ 3226 Id: "c1", 3227 Name: "cluster1", 3228 }, 3229 VtctldClient: &fakevtctldclient.VtctldClient{ 3230 GetWorkflowsResults: map[string]struct { 3231 Response *vtctldatapb.GetWorkflowsResponse 3232 Error error 3233 }{ 3234 "ks1": { 3235 Response: &vtctldatapb.GetWorkflowsResponse{ 3236 Workflows: []*vtctldatapb.Workflow{ 3237 { 3238 Name: "ks1-workflow1", 3239 }, 3240 }, 3241 }, 3242 }, 3243 "ks2": { 3244 Response: &vtctldatapb.GetWorkflowsResponse{ 3245 Workflows: []*vtctldatapb.Workflow{ 3246 { 3247 Name: "ks2-workflow1", 3248 }, 3249 }, 3250 }, 3251 }, 3252 }, 3253 }, 3254 }, 3255 keyspaces: []string{"ks1", "ks2"}, 3256 expected: &vtadminpb.ClusterWorkflows{ 3257 Workflows: []*vtadminpb.Workflow{ 3258 { 3259 Cluster: &vtadminpb.Cluster{ 3260 Id: "c1", 3261 Name: "cluster1", 3262 }, 3263 Keyspace: "ks1", 3264 Workflow: &vtctldatapb.Workflow{ 3265 Name: "ks1-workflow1", 3266 }, 3267 }, 3268 { 3269 Cluster: &vtadminpb.Cluster{ 3270 Id: "c1", 3271 Name: "cluster1", 3272 }, 3273 Keyspace: "ks2", 3274 Workflow: &vtctldatapb.Workflow{ 3275 Name: "ks2-workflow1", 3276 }, 3277 }, 3278 }, 3279 }, 3280 shouldErr: false, 3281 }, 3282 { 3283 name: "partial error", 3284 cfg: testutil.TestClusterConfig{ 3285 Cluster: &vtadminpb.Cluster{ 3286 Id: "c1", 3287 Name: "cluster1", 3288 }, 3289 VtctldClient: &fakevtctldclient.VtctldClient{ 3290 GetWorkflowsResults: map[string]struct { 3291 Response *vtctldatapb.GetWorkflowsResponse 3292 Error error 3293 }{ 3294 "ks1": { 3295 Response: &vtctldatapb.GetWorkflowsResponse{ 3296 Workflows: []*vtctldatapb.Workflow{ 3297 { 3298 Name: "ks1-workflow1", 3299 }, 3300 }, 3301 }, 3302 }, 3303 "ks2": { 3304 Error: assert.AnError, 3305 }, 3306 }, 3307 }, 3308 }, 3309 keyspaces: []string{"ks1", "ks2"}, 3310 expected: &vtadminpb.ClusterWorkflows{ 3311 Workflows: []*vtadminpb.Workflow{ 3312 { 3313 Cluster: &vtadminpb.Cluster{ 3314 Id: "c1", 3315 Name: "cluster1", 3316 }, 3317 Keyspace: "ks1", 3318 Workflow: &vtctldatapb.Workflow{ 3319 Name: "ks1-workflow1", 3320 }, 3321 }, 3322 }, 3323 Warnings: []string{"something about ks2"}, 3324 }, 3325 shouldErr: false, 3326 }, 3327 { 3328 name: "error", 3329 cfg: testutil.TestClusterConfig{ 3330 Cluster: &vtadminpb.Cluster{ 3331 Id: "c1", 3332 Name: "cluster1", 3333 }, 3334 VtctldClient: &fakevtctldclient.VtctldClient{ 3335 GetWorkflowsResults: map[string]struct { 3336 Response *vtctldatapb.GetWorkflowsResponse 3337 Error error 3338 }{ 3339 "ks1": { 3340 Error: assert.AnError, 3341 }, 3342 }, 3343 }, 3344 }, 3345 keyspaces: []string{"ks1"}, 3346 expected: nil, 3347 shouldErr: true, 3348 }, 3349 } 3350 3351 ctx := context.Background() 3352 3353 for _, tt := range tests { 3354 tt := tt 3355 3356 t.Run(tt.name, func(t *testing.T) { 3357 t.Parallel() 3358 3359 c := testutil.BuildCluster(t, tt.cfg) 3360 workflows, err := c.GetWorkflows(ctx, tt.keyspaces, tt.opts) 3361 if tt.shouldErr { 3362 assert.Error(t, err) 3363 3364 return 3365 } 3366 3367 assert.NoError(t, err) 3368 testutil.AssertClusterWorkflowsEqual(t, tt.expected, workflows) 3369 }) 3370 } 3371 } 3372 3373 func TestSetWritable(t *testing.T) { 3374 t.Parallel() 3375 3376 ctx := context.Background() 3377 tests := []struct { 3378 name string 3379 cfg testutil.TestClusterConfig 3380 req *vtctldatapb.SetWritableRequest 3381 assertion func(t assert.TestingT, err error, msgAndArgs ...any) bool 3382 assertionMsgExtra []any 3383 }{ 3384 { 3385 name: "ok", 3386 cfg: testutil.TestClusterConfig{ 3387 Cluster: &vtadminpb.Cluster{ 3388 Id: "test", 3389 Name: "test", 3390 }, 3391 VtctldClient: &fakevtctldclient.VtctldClient{ 3392 SetWritableResults: map[string]error{ 3393 "zone1-0000000100": nil, 3394 }, 3395 }, 3396 }, 3397 req: &vtctldatapb.SetWritableRequest{ 3398 TabletAlias: &topodatapb.TabletAlias{ 3399 Cell: "zone1", 3400 Uid: 100, 3401 }, 3402 Writable: true, 3403 }, 3404 assertion: assert.NoError, 3405 }, 3406 { 3407 name: "error", 3408 cfg: testutil.TestClusterConfig{ 3409 Cluster: &vtadminpb.Cluster{ 3410 Id: "test", 3411 Name: "test", 3412 }, 3413 VtctldClient: &fakevtctldclient.VtctldClient{ 3414 SetWritableResults: map[string]error{ 3415 "zone1-0000000100": fmt.Errorf("some error"), 3416 }, 3417 }, 3418 }, 3419 req: &vtctldatapb.SetWritableRequest{ 3420 TabletAlias: &topodatapb.TabletAlias{ 3421 Cell: "zone1", 3422 Uid: 100, 3423 }, 3424 Writable: true, 3425 }, 3426 assertion: assert.Error, 3427 }, 3428 } 3429 3430 for _, tt := range tests { 3431 tt := tt 3432 3433 t.Run(tt.name, func(t *testing.T) { 3434 t.Parallel() 3435 3436 c := testutil.BuildCluster(t, tt.cfg) 3437 err := c.SetWritable(ctx, tt.req) 3438 tt.assertion(t, err, tt.assertionMsgExtra...) 3439 }) 3440 } 3441 } 3442 3443 func TestToggleTabletReplication(t *testing.T) { 3444 t.Parallel() 3445 3446 type ReplicationState bool 3447 const ( 3448 Start ReplicationState = true 3449 Stop ReplicationState = false 3450 ) 3451 3452 testClusterProto := &vtadminpb.Cluster{ 3453 Id: "test", 3454 Name: "test", 3455 } 3456 3457 ctx := context.Background() 3458 tests := []struct { 3459 name string 3460 cfg testutil.TestClusterConfig 3461 tablet *vtadminpb.Tablet 3462 state ReplicationState 3463 assertion func(t assert.TestingT, err error, msgAndArgs ...any) bool 3464 assertionMsgExtra []any 3465 }{ 3466 { 3467 name: "start/ok", 3468 cfg: testutil.TestClusterConfig{ 3469 Cluster: testClusterProto, 3470 VtctldClient: &fakevtctldclient.VtctldClient{ 3471 StartReplicationResults: map[string]error{ 3472 "zone1-0000000100": nil, 3473 "zone1-0000000101": fmt.Errorf("some error"), 3474 }, 3475 StopReplicationResults: map[string]error{ 3476 "zone1-0000000100": fmt.Errorf("some error"), 3477 "zone1-0000000101": nil, 3478 }, 3479 }, 3480 }, 3481 tablet: &vtadminpb.Tablet{ 3482 Cluster: testClusterProto, 3483 Tablet: &topodatapb.Tablet{ 3484 Alias: &topodatapb.TabletAlias{ 3485 Cell: "zone1", 3486 Uid: 100, 3487 }, 3488 }, 3489 }, 3490 state: Start, 3491 assertion: assert.NoError, 3492 }, 3493 { 3494 name: "start/error", 3495 cfg: testutil.TestClusterConfig{ 3496 Cluster: testClusterProto, 3497 VtctldClient: &fakevtctldclient.VtctldClient{ 3498 StartReplicationResults: map[string]error{ 3499 "zone1-0000000100": fmt.Errorf("some error"), 3500 "zone1-0000000101": nil, 3501 }, 3502 StopReplicationResults: map[string]error{ 3503 "zone1-0000000100": fmt.Errorf("some error"), 3504 "zone1-0000000101": nil, 3505 }, 3506 }, 3507 }, 3508 tablet: &vtadminpb.Tablet{ 3509 Cluster: testClusterProto, 3510 Tablet: &topodatapb.Tablet{ 3511 Alias: &topodatapb.TabletAlias{ 3512 Cell: "zone1", 3513 Uid: 100, 3514 }, 3515 }, 3516 }, 3517 state: Start, 3518 assertion: assert.Error, 3519 }, 3520 { 3521 name: "stop/ok", 3522 cfg: testutil.TestClusterConfig{ 3523 Cluster: testClusterProto, 3524 VtctldClient: &fakevtctldclient.VtctldClient{ 3525 StartReplicationResults: map[string]error{ 3526 "zone1-0000000100": fmt.Errorf("some error"), 3527 "zone1-0000000101": nil, 3528 }, 3529 StopReplicationResults: map[string]error{ 3530 "zone1-0000000100": nil, 3531 "zone1-0000000101": fmt.Errorf("some error"), 3532 }, 3533 }, 3534 }, 3535 tablet: &vtadminpb.Tablet{ 3536 Cluster: testClusterProto, 3537 Tablet: &topodatapb.Tablet{ 3538 Alias: &topodatapb.TabletAlias{ 3539 Cell: "zone1", 3540 Uid: 100, 3541 }, 3542 }, 3543 }, 3544 state: Stop, 3545 assertion: assert.NoError, 3546 }, 3547 { 3548 name: "stop/error", 3549 cfg: testutil.TestClusterConfig{ 3550 Cluster: testClusterProto, 3551 VtctldClient: &fakevtctldclient.VtctldClient{ 3552 StartReplicationResults: map[string]error{ 3553 "zone1-0000000100": fmt.Errorf("some error"), 3554 "zone1-0000000101": nil, 3555 }, 3556 StopReplicationResults: map[string]error{ 3557 "zone1-0000000100": fmt.Errorf("some error"), 3558 "zone1-0000000101": nil, 3559 }, 3560 }, 3561 }, 3562 tablet: &vtadminpb.Tablet{ 3563 Cluster: testClusterProto, 3564 Tablet: &topodatapb.Tablet{ 3565 Alias: &topodatapb.TabletAlias{ 3566 Cell: "zone1", 3567 Uid: 100, 3568 }, 3569 }, 3570 }, 3571 state: Stop, 3572 assertion: assert.Error, 3573 }, 3574 } 3575 3576 for _, tt := range tests { 3577 tt := tt 3578 3579 t.Run(tt.name, func(t *testing.T) { 3580 t.Parallel() 3581 3582 c := testutil.BuildCluster(t, tt.cfg) 3583 err := c.ToggleTabletReplication(ctx, tt.tablet, bool(tt.state)) 3584 tt.assertion(t, err, tt.assertionMsgExtra...) 3585 }) 3586 } 3587 }