vitess.io/vitess@v0.16.2/go/vt/vtctl/grpcvtctldserver/server_test.go (about) 1 /* 2 Copyright 2020 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 grpcvtctldserver 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "io" 24 "os" 25 "sort" 26 "testing" 27 "time" 28 29 _flag "vitess.io/vitess/go/internal/flag" 30 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 "google.golang.org/protobuf/proto" 34 35 "vitess.io/vitess/go/mysql" 36 "vitess.io/vitess/go/protoutil" 37 "vitess.io/vitess/go/sqltypes" 38 "vitess.io/vitess/go/test/utils" 39 hk "vitess.io/vitess/go/vt/hook" 40 "vitess.io/vitess/go/vt/logutil" 41 "vitess.io/vitess/go/vt/mysqlctl/backupstorage" 42 "vitess.io/vitess/go/vt/topo" 43 "vitess.io/vitess/go/vt/topo/memorytopo" 44 "vitess.io/vitess/go/vt/topo/topoproto" 45 "vitess.io/vitess/go/vt/vtctl/grpcvtctldserver/testutil" 46 "vitess.io/vitess/go/vt/vtctl/localvtctldclient" 47 "vitess.io/vitess/go/vt/vttablet/tmclient" 48 "vitess.io/vitess/go/vt/vttablet/tmclienttest" 49 50 logutilpb "vitess.io/vitess/go/vt/proto/logutil" 51 mysqlctlpb "vitess.io/vitess/go/vt/proto/mysqlctl" 52 querypb "vitess.io/vitess/go/vt/proto/query" 53 replicationdatapb "vitess.io/vitess/go/vt/proto/replicationdata" 54 tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" 55 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 56 vschemapb "vitess.io/vitess/go/vt/proto/vschema" 57 vtctldatapb "vitess.io/vitess/go/vt/proto/vtctldata" 58 vtctlservicepb "vitess.io/vitess/go/vt/proto/vtctlservice" 59 "vitess.io/vitess/go/vt/proto/vttime" 60 ) 61 62 func init() { 63 backupstorage.BackupStorageImplementation = testutil.BackupStorageImplementation 64 65 // For tests that don't actually care about mocking the tmclient (i.e. they 66 // call NewVtctldServer to initialize the unit under test), this needs to be 67 // set. 68 // 69 // Tests that do care about the tmclient should use 70 // testutil.NewVtctldServerWithTabletManagerClient to initialize their 71 // VtctldServer. 72 tmclienttest.SetProtocol("go.vt.vtctl.grpcvtctldserver", "grpcvtctldserver.test") 73 tmclient.RegisterTabletManagerClientFactory("grpcvtctldserver.test", func() tmclient.TabletManagerClient { 74 return nil 75 }) 76 } 77 78 func TestPanicHandler(t *testing.T) { 79 t.Parallel() 80 81 defer func() { 82 err := recover() 83 assert.Nil(t, err, "bad request should catch panic") 84 }() 85 86 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, nil, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 87 return NewVtctldServer(ts) 88 }) 89 90 _, err := vtctld.AddCellInfo(context.Background(), nil) 91 assert.Error(t, err) 92 } 93 94 func TestAddCellInfo(t *testing.T) { 95 t.Parallel() 96 97 ctx := context.Background() 98 tests := []struct { 99 name string 100 ts *topo.Server 101 req *vtctldatapb.AddCellInfoRequest 102 shouldErr bool 103 }{ 104 { 105 ts: memorytopo.NewServer("zone1"), 106 req: &vtctldatapb.AddCellInfoRequest{ 107 Name: "zone2", 108 CellInfo: &topodatapb.CellInfo{ 109 ServerAddress: ":2222", 110 Root: "/zone2", 111 }, 112 }, 113 }, 114 { 115 name: "cell already exists", 116 ts: memorytopo.NewServer("zone1"), 117 req: &vtctldatapb.AddCellInfoRequest{ 118 Name: "zone1", 119 CellInfo: &topodatapb.CellInfo{ 120 ServerAddress: ":1111", 121 Root: "/zone1", 122 }, 123 }, 124 shouldErr: true, 125 }, 126 { 127 name: "no cell root", 128 ts: memorytopo.NewServer("zone1"), 129 req: &vtctldatapb.AddCellInfoRequest{ 130 Name: "zone2", 131 CellInfo: &topodatapb.CellInfo{ 132 ServerAddress: ":2222", 133 }, 134 }, 135 shouldErr: true, 136 }, 137 } 138 139 for _, tt := range tests { 140 tt := tt 141 t.Run(tt.name, func(t *testing.T) { 142 t.Parallel() 143 144 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 145 return NewVtctldServer(ts) 146 }) 147 _, err := vtctld.AddCellInfo(ctx, tt.req) 148 if tt.shouldErr { 149 assert.Error(t, err) 150 return 151 } 152 153 require.NoError(t, err) 154 ci, err := tt.ts.GetCellInfo(ctx, tt.req.Name, true) 155 require.NoError(t, err, "failed to read new cell %s from topo", tt.req.Name) 156 utils.MustMatch(t, tt.req.CellInfo, ci) 157 }) 158 } 159 } 160 161 func TestAddCellsAlias(t *testing.T) { 162 t.Parallel() 163 164 ctx := context.Background() 165 tests := []struct { 166 name string 167 ts *topo.Server 168 setup func(ts *topo.Server) error 169 req *vtctldatapb.AddCellsAliasRequest 170 shouldErr bool 171 }{ 172 { 173 ts: memorytopo.NewServer("zone1", "zone2", "zone3"), 174 req: &vtctldatapb.AddCellsAliasRequest{ 175 Name: "zone", 176 Cells: []string{"zone1", "zone2", "zone3"}, 177 }, 178 }, 179 { 180 name: "alias exists", 181 ts: memorytopo.NewServer("zone1", "zone2", "zone3"), 182 setup: func(ts *topo.Server) error { 183 return ts.CreateCellsAlias(ctx, "zone", &topodatapb.CellsAlias{ 184 Cells: []string{"zone1", "zone2"}, 185 }) 186 }, 187 req: &vtctldatapb.AddCellsAliasRequest{ 188 Name: "zone", 189 Cells: []string{"zone1", "zone2", "zone3"}, 190 }, 191 shouldErr: true, 192 }, 193 { 194 name: "alias overlaps", 195 ts: memorytopo.NewServer("zone1", "zone2", "zone3"), 196 setup: func(ts *topo.Server) error { 197 return ts.CreateCellsAlias(context.Background(), "zone_a", &topodatapb.CellsAlias{ 198 Cells: []string{"zone1", "zone3"}, 199 }) 200 }, 201 req: &vtctldatapb.AddCellsAliasRequest{ 202 Name: "zone_b", 203 Cells: []string{"zone1", "zone2", "zone3"}, 204 }, 205 shouldErr: true, 206 }, 207 } 208 209 for _, tt := range tests { 210 tt := tt 211 t.Run(tt.name, func(t *testing.T) { 212 t.Parallel() 213 214 if tt.setup != nil { 215 err := tt.setup(tt.ts) 216 require.NoError(t, err, "test setup failed") 217 } 218 219 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 220 return NewVtctldServer(ts) 221 }) 222 _, err := vtctld.AddCellsAlias(ctx, tt.req) 223 if tt.shouldErr { 224 assert.Error(t, err) 225 return 226 } 227 228 require.NoError(t, err) 229 ca, err := tt.ts.GetCellsAlias(ctx, tt.req.Name, true) 230 require.NoError(t, err, "failed to read new cells alias %s from topo", tt.req.Name) 231 utils.MustMatch(t, &topodatapb.CellsAlias{Cells: tt.req.Cells}, ca) 232 }) 233 } 234 } 235 236 func TestApplyRoutingRules(t *testing.T) { 237 t.Parallel() 238 239 ctx := context.Background() 240 tests := []struct { 241 name string 242 cells []string 243 req *vtctldatapb.ApplyRoutingRulesRequest 244 expectedRules *vschemapb.RoutingRules 245 topoDown bool 246 shouldErr bool 247 }{ 248 { 249 name: "success", 250 cells: []string{"zone1"}, 251 req: &vtctldatapb.ApplyRoutingRulesRequest{ 252 RoutingRules: &vschemapb.RoutingRules{ 253 Rules: []*vschemapb.RoutingRule{ 254 { 255 FromTable: "t1", 256 ToTables: []string{"t1", "t2"}, 257 }, 258 }, 259 }, 260 }, 261 expectedRules: &vschemapb.RoutingRules{ 262 Rules: []*vschemapb.RoutingRule{ 263 { 264 FromTable: "t1", 265 ToTables: []string{"t1", "t2"}, 266 }, 267 }, 268 }, 269 }, 270 { 271 name: "rebuild failed (bad cell)", 272 cells: []string{"zone1"}, 273 req: &vtctldatapb.ApplyRoutingRulesRequest{ 274 RoutingRules: &vschemapb.RoutingRules{ 275 Rules: []*vschemapb.RoutingRule{ 276 { 277 FromTable: "t1", 278 ToTables: []string{"t1", "t2"}, 279 }, 280 }, 281 }, 282 RebuildCells: []string{"zone1", "zone2"}, 283 }, 284 shouldErr: true, 285 }, 286 { 287 // this test case is exactly like the previous, but we don't fail 288 // because we don't rebuild the vschema graph (which would fail the 289 // way we've set up the test case with the bogus cell). 290 name: "rebuild skipped", 291 cells: []string{"zone1"}, 292 req: &vtctldatapb.ApplyRoutingRulesRequest{ 293 RoutingRules: &vschemapb.RoutingRules{ 294 Rules: []*vschemapb.RoutingRule{ 295 { 296 FromTable: "t1", 297 ToTables: []string{"t1", "t2"}, 298 }, 299 }, 300 }, 301 SkipRebuild: true, 302 RebuildCells: []string{"zone1", "zone2"}, 303 }, 304 expectedRules: &vschemapb.RoutingRules{ 305 Rules: []*vschemapb.RoutingRule{ 306 { 307 FromTable: "t1", 308 ToTables: []string{"t1", "t2"}, 309 }, 310 }, 311 }, 312 shouldErr: false, 313 }, 314 { 315 name: "topo down", 316 cells: []string{"zone1"}, 317 req: &vtctldatapb.ApplyRoutingRulesRequest{}, 318 topoDown: true, 319 shouldErr: true, 320 }, 321 } 322 323 for _, tt := range tests { 324 tt := tt 325 t.Run(tt.name, func(t *testing.T) { 326 t.Parallel() 327 328 ts, factory := memorytopo.NewServerAndFactory(tt.cells...) 329 if tt.topoDown { 330 factory.SetError(errors.New("topo down for testing")) 331 } 332 333 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 334 return NewVtctldServer(ts) 335 }) 336 _, err := vtctld.ApplyRoutingRules(ctx, tt.req) 337 if tt.shouldErr { 338 assert.Error(t, err) 339 return 340 } 341 342 require.NoError(t, err, "ApplyRoutingRules(%+v) failed", tt.req) 343 344 rr, err := ts.GetRoutingRules(ctx) 345 require.NoError(t, err, "failed to get routing rules from topo to compare") 346 utils.MustMatch(t, tt.expectedRules, rr) 347 }) 348 } 349 } 350 351 func TestApplyVSchema(t *testing.T) { 352 t.Parallel() 353 354 tests := []struct { 355 name string 356 req *vtctldatapb.ApplyVSchemaRequest 357 exp *vtctldatapb.ApplyVSchemaResponse 358 shouldErr bool 359 }{ 360 { 361 name: "normal", 362 req: &vtctldatapb.ApplyVSchemaRequest{ 363 Keyspace: "testkeyspace", 364 VSchema: &vschemapb.Keyspace{ 365 Sharded: false, 366 }, 367 }, 368 exp: &vtctldatapb.ApplyVSchemaResponse{ 369 VSchema: &vschemapb.Keyspace{ 370 Sharded: false, 371 }, 372 }, 373 shouldErr: false, 374 }, { 375 name: "skip rebuild", 376 req: &vtctldatapb.ApplyVSchemaRequest{ 377 Keyspace: "testkeyspace", 378 VSchema: &vschemapb.Keyspace{ 379 Sharded: false, 380 }, 381 SkipRebuild: true, 382 }, 383 exp: &vtctldatapb.ApplyVSchemaResponse{ 384 VSchema: &vschemapb.Keyspace{ 385 Sharded: false, 386 }, 387 }, 388 shouldErr: false, 389 }, { 390 name: "both", 391 req: &vtctldatapb.ApplyVSchemaRequest{ 392 Keyspace: "testkeyspace", 393 VSchema: &vschemapb.Keyspace{ 394 Sharded: false, 395 }, 396 Sql: "some vschema ddl here", 397 }, 398 shouldErr: true, 399 }, { 400 name: "neither", 401 req: &vtctldatapb.ApplyVSchemaRequest{ 402 Keyspace: "testkeyspace", 403 }, 404 shouldErr: true, 405 }, { 406 name: "dry run", 407 req: &vtctldatapb.ApplyVSchemaRequest{ 408 Keyspace: "testkeyspace", 409 VSchema: &vschemapb.Keyspace{ 410 Sharded: false, 411 }, 412 DryRun: true, 413 }, 414 exp: &vtctldatapb.ApplyVSchemaResponse{ 415 VSchema: &vschemapb.Keyspace{ 416 Sharded: false, 417 }, 418 }, 419 shouldErr: false, 420 }, 421 } 422 423 for _, tt := range tests { 424 tt := tt 425 t.Run(tt.name, func(t *testing.T) { 426 t.Parallel() 427 428 ctx := context.Background() 429 ts := memorytopo.NewServer("zone1") 430 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 431 return NewVtctldServer(ts) 432 }) 433 434 testutil.AddKeyspace(ctx, t, ts, &vtctldatapb.Keyspace{ 435 Name: tt.req.Keyspace, 436 Keyspace: &topodatapb.Keyspace{ 437 KeyspaceType: topodatapb.KeyspaceType_NORMAL, 438 }, 439 }) 440 441 origVSchema := &vschemapb.Keyspace{ 442 Sharded: true, 443 Vindexes: map[string]*vschemapb.Vindex{ 444 "v1": { 445 Type: "hash", 446 }, 447 }, 448 } 449 err := ts.SaveVSchema(ctx, tt.req.Keyspace, origVSchema) 450 require.NoError(t, err) 451 452 origSrvVSchema := &vschemapb.SrvVSchema{ 453 Keyspaces: map[string]*vschemapb.Keyspace{ 454 "testkeyspace": { 455 Sharded: true, 456 Vindexes: map[string]*vschemapb.Vindex{ 457 "v1": { 458 Type: "hash", 459 }, 460 }, 461 }, 462 }, 463 RoutingRules: &vschemapb.RoutingRules{ 464 Rules: []*vschemapb.RoutingRule{}, 465 }, 466 } 467 err = ts.UpdateSrvVSchema(ctx, "zone1", origSrvVSchema) 468 require.NoError(t, err) 469 470 res, err := vtctld.ApplyVSchema(ctx, tt.req) 471 if tt.shouldErr { 472 assert.Error(t, err) 473 return 474 } 475 476 assert.NoError(t, err) 477 utils.MustMatch(t, tt.exp, res) 478 479 if tt.req.DryRun { 480 actual, err := ts.GetVSchema(ctx, tt.req.Keyspace) 481 require.NoError(t, err) 482 utils.MustMatch(t, origVSchema, actual) 483 } 484 485 finalSrvVSchema, err := ts.GetSrvVSchema(ctx, "zone1") 486 require.NoError(t, err) 487 488 if tt.req.SkipRebuild || tt.req.DryRun { 489 utils.MustMatch(t, origSrvVSchema, finalSrvVSchema) 490 } else { 491 changedSrvVSchema := &vschemapb.SrvVSchema{ 492 Keyspaces: map[string]*vschemapb.Keyspace{ 493 "testkeyspace": { 494 Sharded: false, 495 }, 496 }, 497 RoutingRules: &vschemapb.RoutingRules{ 498 Rules: []*vschemapb.RoutingRule{}, 499 }, 500 ShardRoutingRules: &vschemapb.ShardRoutingRules{ 501 Rules: []*vschemapb.ShardRoutingRule{}, 502 }, 503 } 504 utils.MustMatch(t, changedSrvVSchema, finalSrvVSchema) 505 } 506 }) 507 } 508 } 509 510 func TestBackup(t *testing.T) { 511 ctx := context.Background() 512 tests := []struct { 513 name string 514 ts *topo.Server 515 tmc tmclient.TabletManagerClient 516 tablet *topodatapb.Tablet 517 req *vtctldatapb.BackupRequest 518 shouldErr bool 519 assertion func(t *testing.T, responses []*vtctldatapb.BackupResponse, err error) 520 }{ 521 { 522 name: "ok", 523 ts: memorytopo.NewServer("zone1"), 524 tmc: &testutil.TabletManagerClient{ 525 Backups: map[string]struct { 526 Events []*logutilpb.Event 527 EventInterval time.Duration 528 EventJitter time.Duration 529 ErrorAfter time.Duration 530 }{ 531 "zone1-0000000100": { 532 Events: []*logutilpb.Event{{}, {}, {}}, 533 }, 534 }, 535 SetReplicationSourceResults: map[string]error{ 536 "zone1-0000000100": nil, 537 }, 538 }, 539 tablet: &topodatapb.Tablet{ 540 Alias: &topodatapb.TabletAlias{ 541 Cell: "zone1", 542 Uid: 100, 543 }, 544 Type: topodatapb.TabletType_REPLICA, 545 Keyspace: "ks", 546 Shard: "-", 547 }, 548 req: &vtctldatapb.BackupRequest{ 549 TabletAlias: &topodatapb.TabletAlias{ 550 Cell: "zone1", 551 Uid: 100, 552 }, 553 }, 554 assertion: func(t *testing.T, responses []*vtctldatapb.BackupResponse, err error) { 555 assert.ErrorIs(t, err, io.EOF, "expected Recv loop to end with io.EOF") 556 assert.Equal(t, 3, len(responses), "expected 3 messages from backupclient stream") 557 }, 558 }, 559 { 560 name: "cannot backup primary", 561 ts: memorytopo.NewServer("zone1"), 562 tmc: &testutil.TabletManagerClient{ 563 Backups: map[string]struct { 564 Events []*logutilpb.Event 565 EventInterval time.Duration 566 EventJitter time.Duration 567 ErrorAfter time.Duration 568 }{ 569 "zone1-0000000100": { 570 Events: []*logutilpb.Event{{}, {}, {}}, 571 }, 572 }, 573 }, 574 tablet: &topodatapb.Tablet{ 575 Alias: &topodatapb.TabletAlias{ 576 Cell: "zone1", 577 Uid: 100, 578 }, 579 Type: topodatapb.TabletType_PRIMARY, 580 Keyspace: "ks", 581 Shard: "-", 582 }, 583 req: &vtctldatapb.BackupRequest{ 584 TabletAlias: &topodatapb.TabletAlias{ 585 Cell: "zone1", 586 Uid: 100, 587 }, 588 }, 589 assertion: func(t *testing.T, responses []*vtctldatapb.BackupResponse, err error) { 590 assert.NotErrorIs(t, err, io.EOF, "expected backupclient stream to close with non-EOF") 591 assert.Zero(t, len(responses), "expected no backupclient messages") 592 }, 593 }, 594 { 595 name: "allow-primary", 596 ts: memorytopo.NewServer("zone1"), 597 tmc: &testutil.TabletManagerClient{ 598 Backups: map[string]struct { 599 Events []*logutilpb.Event 600 EventInterval time.Duration 601 EventJitter time.Duration 602 ErrorAfter time.Duration 603 }{ 604 "zone1-0000000100": { 605 Events: []*logutilpb.Event{{}, {}, {}}, 606 }, 607 }, 608 }, 609 tablet: &topodatapb.Tablet{ 610 Alias: &topodatapb.TabletAlias{ 611 Cell: "zone1", 612 Uid: 100, 613 }, 614 Type: topodatapb.TabletType_PRIMARY, 615 Keyspace: "ks", 616 Shard: "-", 617 }, 618 req: &vtctldatapb.BackupRequest{ 619 TabletAlias: &topodatapb.TabletAlias{ 620 Cell: "zone1", 621 Uid: 100, 622 }, 623 AllowPrimary: true, 624 }, 625 assertion: func(t *testing.T, responses []*vtctldatapb.BackupResponse, err error) { 626 assert.ErrorIs(t, err, io.EOF, "expected Recv loop to end with io.EOF") 627 assert.Equal(t, 3, len(responses), "expected 3 messages from backupclient stream") 628 }, 629 }, 630 { 631 name: "no tablet", 632 ts: memorytopo.NewServer("zone1"), 633 tmc: &testutil.TabletManagerClient{ 634 Backups: map[string]struct { 635 Events []*logutilpb.Event 636 EventInterval time.Duration 637 EventJitter time.Duration 638 ErrorAfter time.Duration 639 }{ 640 "zone1-0000000100": { 641 Events: []*logutilpb.Event{{}, {}, {}}, 642 }, 643 }, 644 }, 645 tablet: &topodatapb.Tablet{ 646 Alias: &topodatapb.TabletAlias{ 647 Cell: "zone1", 648 Uid: 100, 649 }, 650 Type: topodatapb.TabletType_REPLICA, 651 Keyspace: "ks", 652 Shard: "-", 653 }, 654 req: &vtctldatapb.BackupRequest{ 655 TabletAlias: &topodatapb.TabletAlias{ 656 Cell: "zone1", 657 Uid: 404, 658 }, 659 }, 660 assertion: func(t *testing.T, responses []*vtctldatapb.BackupResponse, err error) { 661 assert.NotErrorIs(t, err, io.EOF, "expected backupclient stream to close with non-EOF") 662 assert.Zero(t, len(responses), "expected no backupclient messages") 663 }, 664 }, 665 { 666 name: "midstream error", 667 ts: memorytopo.NewServer("zone1"), 668 tmc: &testutil.TabletManagerClient{ 669 Backups: map[string]struct { 670 Events []*logutilpb.Event 671 EventInterval time.Duration 672 EventJitter time.Duration 673 ErrorAfter time.Duration 674 }{ 675 "zone1-0000000100": { 676 Events: []*logutilpb.Event{{}, {}, {}}, 677 EventInterval: 100 * time.Millisecond, 678 ErrorAfter: 20 * time.Millisecond, 679 }, 680 }, 681 }, 682 tablet: &topodatapb.Tablet{ 683 Alias: &topodatapb.TabletAlias{ 684 Cell: "zone1", 685 Uid: 100, 686 }, 687 Type: topodatapb.TabletType_REPLICA, 688 Keyspace: "ks", 689 Shard: "-", 690 }, 691 req: &vtctldatapb.BackupRequest{ 692 TabletAlias: &topodatapb.TabletAlias{ 693 Cell: "zone1", 694 Uid: 100, 695 }, 696 }, 697 assertion: func(t *testing.T, responses []*vtctldatapb.BackupResponse, err error) { 698 assert.NotErrorIs(t, err, io.EOF, "expected Recv loop to end with error other than io.EOF") 699 assert.Less(t, len(responses), 3, "expected fewer than 3 messages from backupclient stream") 700 }, 701 }, 702 } 703 704 for _, tt := range tests { 705 tt := tt 706 t.Run(tt.name, func(t *testing.T) { 707 if tt.tablet != nil { 708 testutil.AddTablet(ctx, t, tt.ts, tt.tablet, nil) 709 } 710 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 711 return NewVtctldServer(ts) 712 }) 713 client := localvtctldclient.New(vtctld) 714 stream, err := client.Backup(ctx, tt.req) 715 if tt.shouldErr { 716 assert.Error(t, err) 717 } else { 718 require.NoError(t, err) 719 } 720 721 responses, err := func() (responses []*vtctldatapb.BackupResponse, err error) { 722 for { 723 resp, err := stream.Recv() 724 if err != nil { 725 return responses, err 726 } 727 728 responses = append(responses, resp) 729 } 730 }() 731 732 if tt.assertion != nil { 733 func() { 734 t.Helper() 735 tt.assertion(t, responses, err) 736 }() 737 } 738 }) 739 } 740 } 741 742 func TestBackupShard(t *testing.T) { 743 ctx := context.Background() 744 tests := []struct { 745 name string 746 ts *topo.Server 747 tmc tmclient.TabletManagerClient 748 tablets []*topodatapb.Tablet 749 req *vtctldatapb.BackupShardRequest 750 shouldErr bool 751 assertion func(t *testing.T, responses []*vtctldatapb.BackupResponse, err error) 752 }{ 753 { 754 name: "ok", 755 ts: memorytopo.NewServer("zone1"), 756 tmc: &testutil.TabletManagerClient{ 757 Backups: map[string]struct { 758 Events []*logutilpb.Event 759 EventInterval time.Duration 760 EventJitter time.Duration 761 ErrorAfter time.Duration 762 }{ 763 "zone1-0000000100": { 764 Events: []*logutilpb.Event{{}, {}, {}}, 765 }, 766 }, 767 PrimaryPositionResults: map[string]struct { 768 Position string 769 Error error 770 }{ 771 "zone1-0000000200": { 772 Position: "some-position", 773 }, 774 }, 775 ReplicationStatusResults: map[string]struct { 776 Position *replicationdatapb.Status 777 Error error 778 }{ 779 "zone1-0000000100": { 780 Position: &replicationdatapb.Status{ 781 ReplicationLagSeconds: 0, 782 }, 783 }, 784 }, 785 SetReplicationSourceResults: map[string]error{ 786 "zone1-0000000100": nil, 787 }, 788 }, 789 tablets: []*topodatapb.Tablet{ 790 { 791 Alias: &topodatapb.TabletAlias{ 792 Cell: "zone1", 793 Uid: 100, 794 }, 795 Keyspace: "ks", 796 Shard: "-", 797 Type: topodatapb.TabletType_REPLICA, 798 }, 799 { 800 Alias: &topodatapb.TabletAlias{ 801 Cell: "zone1", 802 Uid: 200, 803 }, 804 Keyspace: "ks", 805 Shard: "-", 806 Type: topodatapb.TabletType_PRIMARY, 807 }, 808 }, 809 req: &vtctldatapb.BackupShardRequest{ 810 Keyspace: "ks", 811 Shard: "-", 812 }, 813 assertion: func(t *testing.T, responses []*vtctldatapb.BackupResponse, err error) { 814 assert.ErrorIs(t, err, io.EOF, "expected Recv loop to end with io.EOF") 815 assert.Equal(t, 3, len(responses), "expected 3 messages from backupclient stream") 816 }, 817 }, 818 { 819 name: "cannot backup primary", 820 ts: memorytopo.NewServer("zone1"), 821 tmc: &testutil.TabletManagerClient{ 822 Backups: map[string]struct { 823 Events []*logutilpb.Event 824 EventInterval time.Duration 825 EventJitter time.Duration 826 ErrorAfter time.Duration 827 }{ 828 "zone1-0000000100": { 829 Events: []*logutilpb.Event{{}, {}, {}}, 830 }, 831 }, 832 PrimaryPositionResults: map[string]struct { 833 Position string 834 Error error 835 }{ 836 "zone1-0000000100": { 837 Position: "some-position", 838 }, 839 }, 840 }, 841 tablets: []*topodatapb.Tablet{ 842 { 843 Alias: &topodatapb.TabletAlias{ 844 Cell: "zone1", 845 Uid: 100, 846 }, 847 Type: topodatapb.TabletType_PRIMARY, 848 Keyspace: "ks", 849 Shard: "-", 850 }, 851 }, 852 req: &vtctldatapb.BackupShardRequest{ 853 Keyspace: "ks", 854 Shard: "-", 855 }, 856 assertion: func(t *testing.T, responses []*vtctldatapb.BackupResponse, err error) { 857 assert.NotErrorIs(t, err, io.EOF, "expected backupclient stream to close with non-EOF") 858 assert.Zero(t, len(responses), "expected no backupclient messages") 859 }, 860 }, 861 { 862 name: "allow-primary", 863 ts: memorytopo.NewServer("zone1"), 864 tmc: &testutil.TabletManagerClient{ 865 Backups: map[string]struct { 866 Events []*logutilpb.Event 867 EventInterval time.Duration 868 EventJitter time.Duration 869 ErrorAfter time.Duration 870 }{ 871 "zone1-0000000100": { 872 Events: []*logutilpb.Event{{}, {}, {}}, 873 }, 874 }, 875 PrimaryPositionResults: map[string]struct { 876 Position string 877 Error error 878 }{ 879 "zone1-0000000100": { 880 Position: "some-position", 881 }, 882 }, 883 ReplicationStatusResults: map[string]struct { 884 Position *replicationdatapb.Status 885 Error error 886 }{ 887 "zone1-0000000101": { 888 Position: &replicationdatapb.Status{}, 889 }, 890 }, 891 }, 892 tablets: []*topodatapb.Tablet{ 893 { 894 Alias: &topodatapb.TabletAlias{ 895 Cell: "zone1", 896 Uid: 100, 897 }, 898 Type: topodatapb.TabletType_PRIMARY, 899 Keyspace: "ks", 900 Shard: "-", 901 }, 902 { 903 Alias: &topodatapb.TabletAlias{ 904 Cell: "zone1", 905 Uid: 101, 906 }, 907 Type: topodatapb.TabletType_BACKUP, 908 Keyspace: "ks", 909 Shard: "-", 910 }, 911 }, 912 req: &vtctldatapb.BackupShardRequest{ 913 Keyspace: "ks", 914 Shard: "-", 915 AllowPrimary: true, 916 }, 917 assertion: func(t *testing.T, responses []*vtctldatapb.BackupResponse, err error) { 918 assert.ErrorIs(t, err, io.EOF, "expected Recv loop to end with io.EOF") 919 assert.Equal(t, 3, len(responses), "expected 3 messages from backupclient stream") 920 }, 921 }, 922 { 923 name: "no available tablet", 924 ts: memorytopo.NewServer("zone1"), 925 tmc: &testutil.TabletManagerClient{ 926 Backups: map[string]struct { 927 Events []*logutilpb.Event 928 EventInterval time.Duration 929 EventJitter time.Duration 930 ErrorAfter time.Duration 931 }{ 932 "zone1-0000000100": { 933 Events: []*logutilpb.Event{{}, {}, {}}, 934 }, 935 }, 936 ReplicationStatusResults: map[string]struct { 937 Position *replicationdatapb.Status 938 Error error 939 }{ 940 "zone1-0000000100": { 941 Error: assert.AnError, 942 }, 943 "zone1-0000000101": { 944 Error: assert.AnError, 945 }, 946 }, 947 }, 948 tablets: []*topodatapb.Tablet{ 949 { 950 Alias: &topodatapb.TabletAlias{ 951 Cell: "zone1", 952 Uid: 100, 953 }, 954 Type: topodatapb.TabletType_REPLICA, 955 Keyspace: "ks", 956 Shard: "-", 957 }, 958 { 959 Alias: &topodatapb.TabletAlias{ 960 Cell: "zone1", 961 Uid: 101, 962 }, 963 Type: topodatapb.TabletType_REPLICA, 964 Keyspace: "ks", 965 Shard: "-", 966 }, 967 }, 968 req: &vtctldatapb.BackupShardRequest{}, 969 assertion: func(t *testing.T, responses []*vtctldatapb.BackupResponse, err error) { 970 assert.NotErrorIs(t, err, io.EOF, "expected backupclient stream to close with non-EOF") 971 assert.Zero(t, len(responses), "expected no backupclient messages") 972 }, 973 }, 974 } 975 976 for _, tt := range tests { 977 tt := tt 978 t.Run(tt.name, func(t *testing.T) { 979 testutil.AddTablets(ctx, t, tt.ts, 980 &testutil.AddTabletOptions{ 981 AlsoSetShardPrimary: true, 982 }, tt.tablets..., 983 ) 984 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 985 return NewVtctldServer(ts) 986 }) 987 client := localvtctldclient.New(vtctld) 988 stream, err := client.BackupShard(ctx, tt.req) 989 if tt.shouldErr { 990 assert.Error(t, err) 991 } else { 992 require.NoError(t, err) 993 } 994 995 responses, err := func() (responses []*vtctldatapb.BackupResponse, err error) { 996 for { 997 resp, err := stream.Recv() 998 if err != nil { 999 return responses, err 1000 } 1001 1002 responses = append(responses, resp) 1003 } 1004 }() 1005 1006 if tt.assertion != nil { 1007 func() { 1008 t.Helper() 1009 tt.assertion(t, responses, err) 1010 }() 1011 } 1012 }) 1013 } 1014 } 1015 1016 func TestChangeTabletType(t *testing.T) { 1017 t.Parallel() 1018 1019 tests := []struct { 1020 name string 1021 cells []string 1022 tablets []*topodatapb.Tablet 1023 req *vtctldatapb.ChangeTabletTypeRequest 1024 expected *vtctldatapb.ChangeTabletTypeResponse 1025 shouldErr bool 1026 }{ 1027 { 1028 name: "success", 1029 cells: []string{"zone1"}, 1030 tablets: []*topodatapb.Tablet{ 1031 { 1032 Alias: &topodatapb.TabletAlias{ 1033 Cell: "zone1", 1034 Uid: 100, 1035 }, 1036 Keyspace: "ks", 1037 Shard: "0", 1038 Type: topodatapb.TabletType_REPLICA, 1039 }, { 1040 Alias: &topodatapb.TabletAlias{ 1041 Cell: "zone1", 1042 Uid: 101, 1043 }, 1044 Keyspace: "ks", 1045 Shard: "0", 1046 Type: topodatapb.TabletType_PRIMARY, 1047 }, 1048 }, 1049 req: &vtctldatapb.ChangeTabletTypeRequest{ 1050 TabletAlias: &topodatapb.TabletAlias{ 1051 Cell: "zone1", 1052 Uid: 100, 1053 }, 1054 DbType: topodatapb.TabletType_RDONLY, 1055 }, 1056 expected: &vtctldatapb.ChangeTabletTypeResponse{ 1057 BeforeTablet: &topodatapb.Tablet{ 1058 Alias: &topodatapb.TabletAlias{ 1059 Cell: "zone1", 1060 Uid: 100, 1061 }, 1062 Keyspace: "ks", 1063 Shard: "0", 1064 Type: topodatapb.TabletType_REPLICA, 1065 }, 1066 AfterTablet: &topodatapb.Tablet{ 1067 Alias: &topodatapb.TabletAlias{ 1068 Cell: "zone1", 1069 Uid: 100, 1070 }, 1071 Keyspace: "ks", 1072 Shard: "0", 1073 Type: topodatapb.TabletType_RDONLY, 1074 }, 1075 WasDryRun: false, 1076 }, 1077 shouldErr: false, 1078 }, 1079 { 1080 name: "dry run", 1081 cells: []string{"zone1"}, 1082 tablets: []*topodatapb.Tablet{ 1083 { 1084 Alias: &topodatapb.TabletAlias{ 1085 Cell: "zone1", 1086 Uid: 100, 1087 }, 1088 Keyspace: "ks", 1089 Shard: "0", 1090 Type: topodatapb.TabletType_REPLICA, 1091 }, { 1092 Alias: &topodatapb.TabletAlias{ 1093 Cell: "zone1", 1094 Uid: 101, 1095 }, 1096 Keyspace: "ks", 1097 Shard: "0", 1098 Type: topodatapb.TabletType_PRIMARY, 1099 }, 1100 }, 1101 req: &vtctldatapb.ChangeTabletTypeRequest{ 1102 TabletAlias: &topodatapb.TabletAlias{ 1103 Cell: "zone1", 1104 Uid: 100, 1105 }, 1106 DbType: topodatapb.TabletType_RDONLY, 1107 DryRun: true, 1108 }, 1109 expected: &vtctldatapb.ChangeTabletTypeResponse{ 1110 BeforeTablet: &topodatapb.Tablet{ 1111 Alias: &topodatapb.TabletAlias{ 1112 Cell: "zone1", 1113 Uid: 100, 1114 }, 1115 Keyspace: "ks", 1116 Shard: "0", 1117 Type: topodatapb.TabletType_REPLICA, 1118 }, 1119 AfterTablet: &topodatapb.Tablet{ 1120 Alias: &topodatapb.TabletAlias{ 1121 Cell: "zone1", 1122 Uid: 100, 1123 }, 1124 Keyspace: "ks", 1125 Shard: "0", 1126 Type: topodatapb.TabletType_RDONLY, 1127 }, 1128 WasDryRun: true, 1129 }, 1130 shouldErr: false, 1131 }, 1132 { 1133 name: "tablet not found", 1134 cells: []string{"zone1"}, 1135 tablets: []*topodatapb.Tablet{ 1136 { 1137 Alias: &topodatapb.TabletAlias{ 1138 Cell: "zone1", 1139 Uid: 200, 1140 }, 1141 Keyspace: "ks", 1142 Shard: "0", 1143 Type: topodatapb.TabletType_REPLICA, 1144 }, { 1145 Alias: &topodatapb.TabletAlias{ 1146 Cell: "zone1", 1147 Uid: 101, 1148 }, 1149 Keyspace: "ks", 1150 Shard: "0", 1151 Type: topodatapb.TabletType_PRIMARY, 1152 }, 1153 }, 1154 req: &vtctldatapb.ChangeTabletTypeRequest{ 1155 TabletAlias: &topodatapb.TabletAlias{ 1156 Cell: "zone1", 1157 Uid: 100, 1158 }, 1159 DbType: topodatapb.TabletType_RDONLY, 1160 }, 1161 expected: nil, 1162 shouldErr: true, 1163 }, 1164 { 1165 name: "primary promotions not allowed", 1166 cells: []string{"zone1"}, 1167 tablets: []*topodatapb.Tablet{ 1168 { 1169 Alias: &topodatapb.TabletAlias{ 1170 Cell: "zone1", 1171 Uid: 100, 1172 }, 1173 Keyspace: "ks", 1174 Shard: "0", 1175 Type: topodatapb.TabletType_REPLICA, 1176 }, { 1177 Alias: &topodatapb.TabletAlias{ 1178 Cell: "zone1", 1179 Uid: 101, 1180 }, 1181 Keyspace: "ks", 1182 Shard: "0", 1183 Type: topodatapb.TabletType_PRIMARY, 1184 }, 1185 }, 1186 req: &vtctldatapb.ChangeTabletTypeRequest{ 1187 TabletAlias: &topodatapb.TabletAlias{ 1188 Cell: "zone1", 1189 Uid: 100, 1190 }, 1191 DbType: topodatapb.TabletType_PRIMARY, 1192 }, 1193 expected: nil, 1194 shouldErr: true, 1195 }, 1196 { 1197 name: "primary demotions not allowed", 1198 cells: []string{"zone1"}, 1199 tablets: []*topodatapb.Tablet{ 1200 { 1201 Alias: &topodatapb.TabletAlias{ 1202 Cell: "zone1", 1203 Uid: 100, 1204 }, 1205 Keyspace: "ks", 1206 Shard: "0", 1207 Type: topodatapb.TabletType_PRIMARY, 1208 }, 1209 }, 1210 req: &vtctldatapb.ChangeTabletTypeRequest{ 1211 TabletAlias: &topodatapb.TabletAlias{ 1212 Cell: "zone1", 1213 Uid: 100, 1214 }, 1215 DbType: topodatapb.TabletType_REPLICA, 1216 }, 1217 expected: nil, 1218 shouldErr: true, 1219 }, 1220 } 1221 1222 for _, tt := range tests { 1223 tt := tt 1224 1225 t.Run(tt.name, func(t *testing.T) { 1226 t.Parallel() 1227 1228 ctx := context.Background() 1229 ts := memorytopo.NewServer(tt.cells...) 1230 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &testutil.TabletManagerClient{ 1231 TopoServer: ts, 1232 }, func(ts *topo.Server) vtctlservicepb.VtctldServer { return NewVtctldServer(ts) }) 1233 1234 testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ 1235 AlsoSetShardPrimary: true, 1236 }, tt.tablets...) 1237 1238 resp, err := vtctld.ChangeTabletType(ctx, tt.req) 1239 if tt.shouldErr { 1240 assert.Error(t, err) 1241 return 1242 } 1243 1244 assert.NoError(t, err) 1245 utils.MustMatch(t, tt.expected, resp) 1246 1247 // If we are testing a dry-run, then the tablet in the actual 1248 // topo should match the BeforeTablet in the response. Otherwise, 1249 // the tablet in the actual topo should match the AfterTablet in 1250 // the response. 1251 expectedRealType := resp.AfterTablet.Type 1252 msg := "ChangeTabletType did not cause topo update" 1253 if tt.req.DryRun { 1254 expectedRealType = resp.BeforeTablet.Type 1255 msg = "dryrun type change resulted in real type change" 1256 } 1257 1258 tablet, err := ts.GetTablet(ctx, tt.req.TabletAlias) 1259 assert.NoError(t, err, 1260 "could not load tablet %s from topo after type change %v -> %v [dryrun=%t]", 1261 topoproto.TabletAliasString(tt.req.TabletAlias), 1262 resp.BeforeTablet.Type, 1263 resp.AfterTablet.Type, 1264 resp.WasDryRun, 1265 ) 1266 utils.MustMatch(t, expectedRealType, tablet.Type, msg) 1267 }) 1268 } 1269 1270 t.Run("tabletmanager failure", func(t *testing.T) { 1271 t.Parallel() 1272 1273 ctx := context.Background() 1274 ts := memorytopo.NewServer("zone1") 1275 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &testutil.TabletManagerClient{ 1276 TopoServer: nil, 1277 }, func(ts *topo.Server) vtctlservicepb.VtctldServer { return NewVtctldServer(ts) }) 1278 1279 testutil.AddTablet(ctx, t, ts, &topodatapb.Tablet{ 1280 Alias: &topodatapb.TabletAlias{ 1281 Cell: "zone1", 1282 Uid: 100, 1283 }, 1284 Keyspace: "ks", 1285 Shard: "0", 1286 Type: topodatapb.TabletType_REPLICA, 1287 }, nil) 1288 testutil.AddTablet(ctx, t, ts, &topodatapb.Tablet{ 1289 Alias: &topodatapb.TabletAlias{ 1290 Cell: "zone1", 1291 Uid: 101, 1292 }, 1293 Keyspace: "ks", 1294 Shard: "0", 1295 Type: topodatapb.TabletType_PRIMARY, 1296 }, &testutil.AddTabletOptions{ 1297 AlsoSetShardPrimary: true, 1298 }) 1299 1300 _, err := vtctld.ChangeTabletType(ctx, &vtctldatapb.ChangeTabletTypeRequest{ 1301 TabletAlias: &topodatapb.TabletAlias{ 1302 Cell: "zone1", 1303 Uid: 100, 1304 }, 1305 DbType: topodatapb.TabletType_RDONLY, 1306 }) 1307 assert.Error(t, err) 1308 }) 1309 } 1310 1311 func TestCreateKeyspace(t *testing.T) { 1312 t.Parallel() 1313 1314 cells := []string{"zone1", "zone2", "zone3"} 1315 tests := []struct { 1316 name string 1317 topo map[string]*topodatapb.Keyspace 1318 vschemas map[string]*vschemapb.Keyspace 1319 req *vtctldatapb.CreateKeyspaceRequest 1320 expected *vtctldatapb.CreateKeyspaceResponse 1321 shouldErr bool 1322 vschemaShouldExist bool 1323 expectedVSchema *vschemapb.Keyspace 1324 }{ 1325 { 1326 name: "normal keyspace", 1327 topo: nil, 1328 req: &vtctldatapb.CreateKeyspaceRequest{ 1329 Name: "testkeyspace", 1330 Type: topodatapb.KeyspaceType_NORMAL, 1331 }, 1332 expected: &vtctldatapb.CreateKeyspaceResponse{ 1333 Keyspace: &vtctldatapb.Keyspace{ 1334 Name: "testkeyspace", 1335 Keyspace: &topodatapb.Keyspace{ 1336 KeyspaceType: topodatapb.KeyspaceType_NORMAL, 1337 }, 1338 }, 1339 }, 1340 vschemaShouldExist: true, 1341 expectedVSchema: &vschemapb.Keyspace{ 1342 Sharded: false, 1343 }, 1344 shouldErr: false, 1345 }, 1346 { 1347 name: "snapshot keyspace", 1348 topo: map[string]*topodatapb.Keyspace{ 1349 "testkeyspace": { 1350 KeyspaceType: topodatapb.KeyspaceType_NORMAL, 1351 }, 1352 }, 1353 vschemas: map[string]*vschemapb.Keyspace{ 1354 "testkeyspace": { 1355 Sharded: true, 1356 Vindexes: map[string]*vschemapb.Vindex{ 1357 "h1": { 1358 Type: "hash", 1359 }, 1360 }, 1361 }, 1362 }, 1363 req: &vtctldatapb.CreateKeyspaceRequest{ 1364 Name: "testsnapshot", 1365 Type: topodatapb.KeyspaceType_SNAPSHOT, 1366 BaseKeyspace: "testkeyspace", 1367 SnapshotTime: &vttime.Time{ 1368 Seconds: 1, 1369 }, 1370 }, 1371 expected: &vtctldatapb.CreateKeyspaceResponse{ 1372 Keyspace: &vtctldatapb.Keyspace{ 1373 Name: "testsnapshot", 1374 Keyspace: &topodatapb.Keyspace{ 1375 KeyspaceType: topodatapb.KeyspaceType_SNAPSHOT, 1376 BaseKeyspace: "testkeyspace", 1377 SnapshotTime: &vttime.Time{ 1378 Seconds: 1, 1379 }, 1380 }, 1381 }, 1382 }, 1383 vschemaShouldExist: true, 1384 expectedVSchema: &vschemapb.Keyspace{ 1385 Sharded: true, 1386 Vindexes: map[string]*vschemapb.Vindex{ 1387 "h1": { 1388 Type: "hash", 1389 }, 1390 }, 1391 RequireExplicitRouting: true, 1392 }, 1393 shouldErr: false, 1394 }, 1395 { 1396 name: "snapshot keyspace with no base keyspace specified", 1397 topo: nil, 1398 req: &vtctldatapb.CreateKeyspaceRequest{ 1399 Name: "testsnapshot", 1400 Type: topodatapb.KeyspaceType_SNAPSHOT, 1401 SnapshotTime: &vttime.Time{}, 1402 }, 1403 expected: nil, 1404 shouldErr: true, 1405 }, 1406 { 1407 name: "snapshot keyspace with no snapshot time", 1408 topo: nil, 1409 req: &vtctldatapb.CreateKeyspaceRequest{ 1410 Name: "testsnapshot", 1411 Type: topodatapb.KeyspaceType_SNAPSHOT, 1412 BaseKeyspace: "testkeyspace", 1413 }, 1414 expected: nil, 1415 shouldErr: true, 1416 }, 1417 { 1418 name: "snapshot keyspace with nonexistent base keyspace", 1419 topo: nil, 1420 req: &vtctldatapb.CreateKeyspaceRequest{ 1421 Name: "testsnapshot", 1422 Type: topodatapb.KeyspaceType_SNAPSHOT, 1423 BaseKeyspace: "testkeyspace", 1424 SnapshotTime: &vttime.Time{Seconds: 100}, 1425 }, 1426 expected: &vtctldatapb.CreateKeyspaceResponse{ 1427 Keyspace: &vtctldatapb.Keyspace{ 1428 Name: "testsnapshot", 1429 Keyspace: &topodatapb.Keyspace{ 1430 KeyspaceType: topodatapb.KeyspaceType_SNAPSHOT, 1431 BaseKeyspace: "testkeyspace", 1432 SnapshotTime: &vttime.Time{Seconds: 100}, 1433 }, 1434 }, 1435 }, 1436 vschemaShouldExist: true, 1437 expectedVSchema: &vschemapb.Keyspace{ 1438 Sharded: false, 1439 RequireExplicitRouting: true, 1440 }, 1441 shouldErr: false, 1442 }, 1443 { 1444 name: "invalid keyspace type", 1445 topo: nil, 1446 req: &vtctldatapb.CreateKeyspaceRequest{ 1447 Name: "badkeyspacetype", 1448 Type: 10000000, 1449 }, 1450 expected: nil, 1451 shouldErr: true, 1452 }, 1453 { 1454 name: "keyspace exists/no force", 1455 topo: map[string]*topodatapb.Keyspace{ 1456 "testkeyspace": { 1457 KeyspaceType: topodatapb.KeyspaceType_NORMAL, 1458 }, 1459 }, 1460 req: &vtctldatapb.CreateKeyspaceRequest{ 1461 Name: "testkeyspace", 1462 Type: topodatapb.KeyspaceType_NORMAL, 1463 Force: false, 1464 }, 1465 expected: nil, 1466 shouldErr: true, 1467 }, 1468 { 1469 name: "keyspace exists/force", 1470 topo: map[string]*topodatapb.Keyspace{ 1471 "testkeyspace": { 1472 KeyspaceType: topodatapb.KeyspaceType_NORMAL, 1473 }, 1474 }, 1475 req: &vtctldatapb.CreateKeyspaceRequest{ 1476 Name: "testkeyspace", 1477 Type: topodatapb.KeyspaceType_NORMAL, 1478 Force: true, 1479 }, 1480 expected: &vtctldatapb.CreateKeyspaceResponse{ 1481 Keyspace: &vtctldatapb.Keyspace{ 1482 Name: "testkeyspace", 1483 Keyspace: &topodatapb.Keyspace{ 1484 KeyspaceType: topodatapb.KeyspaceType_NORMAL, 1485 }, 1486 }, 1487 }, 1488 vschemaShouldExist: true, 1489 expectedVSchema: &vschemapb.Keyspace{ 1490 Sharded: false, 1491 }, 1492 shouldErr: false, 1493 }, 1494 { 1495 name: "allow empty vschema", 1496 topo: nil, 1497 req: &vtctldatapb.CreateKeyspaceRequest{ 1498 Name: "testkeyspace", 1499 Type: topodatapb.KeyspaceType_NORMAL, 1500 AllowEmptyVSchema: true, 1501 }, 1502 expected: &vtctldatapb.CreateKeyspaceResponse{ 1503 Keyspace: &vtctldatapb.Keyspace{ 1504 Name: "testkeyspace", 1505 Keyspace: &topodatapb.Keyspace{ 1506 KeyspaceType: topodatapb.KeyspaceType_NORMAL, 1507 }, 1508 }, 1509 }, 1510 vschemaShouldExist: false, 1511 expectedVSchema: nil, 1512 shouldErr: false, 1513 }, { 1514 name: "keyspace with durability policy specified", 1515 topo: nil, 1516 req: &vtctldatapb.CreateKeyspaceRequest{ 1517 Name: "testkeyspace", 1518 Type: topodatapb.KeyspaceType_NORMAL, 1519 DurabilityPolicy: "semi_sync", 1520 }, 1521 expected: &vtctldatapb.CreateKeyspaceResponse{ 1522 Keyspace: &vtctldatapb.Keyspace{ 1523 Name: "testkeyspace", 1524 Keyspace: &topodatapb.Keyspace{ 1525 KeyspaceType: topodatapb.KeyspaceType_NORMAL, 1526 DurabilityPolicy: "semi_sync", 1527 }, 1528 }, 1529 }, 1530 vschemaShouldExist: true, 1531 expectedVSchema: &vschemapb.Keyspace{ 1532 Sharded: false, 1533 }, 1534 shouldErr: false, 1535 }, 1536 } 1537 1538 for _, tt := range tests { 1539 tt := tt 1540 1541 t.Run(tt.name, func(t *testing.T) { 1542 t.Parallel() 1543 1544 if tt.req == nil { 1545 t.Skip("test not yet implemented") 1546 } 1547 1548 ctx := context.Background() 1549 ts := memorytopo.NewServer(cells...) 1550 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 1551 return NewVtctldServer(ts) 1552 }) 1553 1554 for name, ks := range tt.topo { 1555 testutil.AddKeyspace(ctx, t, ts, &vtctldatapb.Keyspace{ 1556 Name: name, 1557 Keyspace: ks, 1558 }) 1559 } 1560 1561 for name, vs := range tt.vschemas { 1562 require.NoError(t, ts.SaveVSchema(ctx, name, vs), "error in SaveVSchema(%v, %+v)", name, vs) 1563 } 1564 1565 // Create the keyspace and make some assertions 1566 resp, err := vtctld.CreateKeyspace(ctx, tt.req) 1567 if tt.shouldErr { 1568 assert.Error(t, err) 1569 return 1570 } 1571 1572 assert.NoError(t, err) 1573 testutil.AssertKeyspacesEqual(t, tt.expected.Keyspace, resp.Keyspace, "%+v\n%+v\n", tt.expected.Keyspace, resp.Keyspace) 1574 1575 // Fetch the newly-created keyspace out of the topo and assert on it 1576 ks, err := ts.GetKeyspace(ctx, tt.req.Name) 1577 assert.NoError(t, err, "cannot get keyspace %v after creating", tt.req.Name) 1578 require.NotNil(t, ks.Keyspace) 1579 1580 actualKs := &vtctldatapb.Keyspace{ 1581 Name: tt.req.Name, 1582 Keyspace: ks.Keyspace, 1583 } 1584 testutil.AssertKeyspacesEqual( 1585 t, 1586 tt.expected.Keyspace, 1587 actualKs, 1588 "created keyspace %v does not match requested keyspace (name = %v) %v", 1589 actualKs, 1590 tt.expected.Keyspace, 1591 ) 1592 1593 // Finally, check the VSchema 1594 vs, err := ts.GetVSchema(ctx, tt.req.Name) 1595 if !tt.vschemaShouldExist { 1596 assert.True(t, topo.IsErrType(err, topo.NoNode), "vschema should not exist, but got other error = %v", err) 1597 return 1598 } 1599 assert.NoError(t, err) 1600 utils.MustMatch(t, tt.expectedVSchema, vs) 1601 }) 1602 } 1603 } 1604 1605 func TestCreateShard(t *testing.T) { 1606 t.Parallel() 1607 1608 tests := []struct { 1609 name string 1610 keyspaces []*vtctldatapb.Keyspace 1611 shards []*vtctldatapb.Shard 1612 topoErr error 1613 req *vtctldatapb.CreateShardRequest 1614 expected *vtctldatapb.CreateShardResponse 1615 shouldErr bool 1616 }{ 1617 { 1618 name: "success", 1619 keyspaces: []*vtctldatapb.Keyspace{ 1620 { 1621 Name: "testkeyspace", 1622 Keyspace: &topodatapb.Keyspace{}, 1623 }, 1624 }, 1625 shards: nil, 1626 topoErr: nil, 1627 req: &vtctldatapb.CreateShardRequest{ 1628 Keyspace: "testkeyspace", 1629 ShardName: "-", 1630 }, 1631 expected: &vtctldatapb.CreateShardResponse{ 1632 Keyspace: &vtctldatapb.Keyspace{ 1633 Name: "testkeyspace", 1634 Keyspace: &topodatapb.Keyspace{}, 1635 }, 1636 Shard: &vtctldatapb.Shard{ 1637 Keyspace: "testkeyspace", 1638 Name: "-", 1639 Shard: &topodatapb.Shard{ 1640 KeyRange: &topodatapb.KeyRange{}, 1641 IsPrimaryServing: true, 1642 }, 1643 }, 1644 ShardAlreadyExists: false, 1645 }, 1646 shouldErr: false, 1647 }, 1648 { 1649 name: "include parent", 1650 keyspaces: nil, 1651 shards: nil, 1652 topoErr: nil, 1653 req: &vtctldatapb.CreateShardRequest{ 1654 Keyspace: "testkeyspace", 1655 ShardName: "-", 1656 IncludeParent: true, 1657 }, 1658 expected: &vtctldatapb.CreateShardResponse{ 1659 Keyspace: &vtctldatapb.Keyspace{ 1660 Name: "testkeyspace", 1661 Keyspace: &topodatapb.Keyspace{}, 1662 }, 1663 Shard: &vtctldatapb.Shard{ 1664 Keyspace: "testkeyspace", 1665 Name: "-", 1666 Shard: &topodatapb.Shard{ 1667 KeyRange: &topodatapb.KeyRange{}, 1668 IsPrimaryServing: true, 1669 }, 1670 }, 1671 ShardAlreadyExists: false, 1672 }, 1673 shouldErr: false, 1674 }, 1675 { 1676 name: "keyspace does not exist", 1677 keyspaces: nil, 1678 shards: nil, 1679 topoErr: nil, 1680 req: &vtctldatapb.CreateShardRequest{ 1681 Keyspace: "testkeyspace", 1682 ShardName: "-", 1683 }, 1684 expected: nil, 1685 shouldErr: true, 1686 }, 1687 { 1688 name: "include parent/keyspace exists/no force", 1689 keyspaces: []*vtctldatapb.Keyspace{ 1690 { 1691 Name: "testkeyspace", 1692 Keyspace: &topodatapb.Keyspace{}, 1693 }, 1694 }, 1695 shards: nil, 1696 topoErr: nil, 1697 req: &vtctldatapb.CreateShardRequest{ 1698 Keyspace: "testkeyspace", 1699 ShardName: "-", 1700 IncludeParent: true, 1701 }, 1702 expected: nil, 1703 shouldErr: true, 1704 }, 1705 { 1706 name: "include parent/keyspace exists/force", 1707 keyspaces: []*vtctldatapb.Keyspace{ 1708 { 1709 Name: "testkeyspace", 1710 Keyspace: &topodatapb.Keyspace{}, 1711 }, 1712 }, 1713 shards: nil, 1714 topoErr: nil, 1715 req: &vtctldatapb.CreateShardRequest{ 1716 Keyspace: "testkeyspace", 1717 ShardName: "-", 1718 IncludeParent: true, 1719 Force: true, 1720 }, 1721 expected: &vtctldatapb.CreateShardResponse{ 1722 Keyspace: &vtctldatapb.Keyspace{ 1723 Name: "testkeyspace", 1724 Keyspace: &topodatapb.Keyspace{}, 1725 }, 1726 Shard: &vtctldatapb.Shard{ 1727 Keyspace: "testkeyspace", 1728 Name: "-", 1729 Shard: &topodatapb.Shard{ 1730 KeyRange: &topodatapb.KeyRange{}, 1731 IsPrimaryServing: true, 1732 }, 1733 }, 1734 ShardAlreadyExists: false, 1735 }, 1736 shouldErr: false, 1737 }, 1738 { 1739 name: "shard exists/no force", 1740 keyspaces: []*vtctldatapb.Keyspace{ 1741 { 1742 Name: "testkeyspace", 1743 Keyspace: &topodatapb.Keyspace{}, 1744 }, 1745 }, 1746 shards: []*vtctldatapb.Shard{ 1747 { 1748 Keyspace: "testkeyspace", 1749 Name: "-", 1750 }, 1751 }, 1752 topoErr: nil, 1753 req: &vtctldatapb.CreateShardRequest{ 1754 Keyspace: "testkeyspace", 1755 ShardName: "-", 1756 }, 1757 expected: nil, 1758 shouldErr: true, 1759 }, 1760 { 1761 name: "shard exists/force", 1762 keyspaces: []*vtctldatapb.Keyspace{ 1763 { 1764 Name: "testkeyspace", 1765 Keyspace: &topodatapb.Keyspace{}, 1766 }, 1767 }, 1768 shards: []*vtctldatapb.Shard{ 1769 { 1770 Keyspace: "testkeyspace", 1771 Name: "-", 1772 }, 1773 }, 1774 topoErr: nil, 1775 req: &vtctldatapb.CreateShardRequest{ 1776 Keyspace: "testkeyspace", 1777 ShardName: "-", 1778 Force: true, 1779 }, 1780 expected: &vtctldatapb.CreateShardResponse{ 1781 Keyspace: &vtctldatapb.Keyspace{ 1782 Name: "testkeyspace", 1783 Keyspace: &topodatapb.Keyspace{}, 1784 }, 1785 Shard: &vtctldatapb.Shard{ 1786 Keyspace: "testkeyspace", 1787 Name: "-", 1788 Shard: &topodatapb.Shard{ 1789 KeyRange: &topodatapb.KeyRange{}, 1790 IsPrimaryServing: true, 1791 }, 1792 }, 1793 ShardAlreadyExists: true, 1794 }, 1795 shouldErr: false, 1796 }, 1797 { 1798 name: "topo is down", 1799 keyspaces: []*vtctldatapb.Keyspace{ 1800 { 1801 Name: "testkeyspace", 1802 Keyspace: &topodatapb.Keyspace{}, 1803 }, 1804 }, 1805 shards: nil, 1806 topoErr: assert.AnError, 1807 req: &vtctldatapb.CreateShardRequest{ 1808 Keyspace: "testkeyspace", 1809 ShardName: "-", 1810 }, 1811 expected: nil, 1812 shouldErr: true, 1813 }, 1814 } 1815 1816 for _, tt := range tests { 1817 tt := tt 1818 1819 t.Run(tt.name, func(t *testing.T) { 1820 t.Parallel() 1821 if tt.req == nil { 1822 t.Skip("focusing on other tests") 1823 } 1824 1825 ctx := context.Background() 1826 ts, topofactory := memorytopo.NewServerAndFactory("zone1") 1827 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 1828 return NewVtctldServer(ts) 1829 }) 1830 1831 for _, ks := range tt.keyspaces { 1832 testutil.AddKeyspace(ctx, t, ts, ks) 1833 } 1834 1835 testutil.AddShards(ctx, t, ts, tt.shards...) 1836 1837 if tt.topoErr != nil { 1838 topofactory.SetError(tt.topoErr) 1839 } 1840 1841 resp, err := vtctld.CreateShard(ctx, tt.req) 1842 if tt.shouldErr { 1843 assert.Error(t, err) 1844 return 1845 } 1846 1847 assert.NoError(t, err) 1848 utils.MustMatch(t, tt.expected, resp) 1849 }) 1850 } 1851 } 1852 1853 func TestDeleteCellInfo(t *testing.T) { 1854 t.Parallel() 1855 1856 ctx := context.Background() 1857 tests := []struct { 1858 name string 1859 ts *topo.Server 1860 req *vtctldatapb.DeleteCellInfoRequest 1861 shouldErr bool 1862 }{ 1863 { 1864 ts: memorytopo.NewServer("zone1", "zone2"), 1865 req: &vtctldatapb.DeleteCellInfoRequest{ 1866 Name: "zone2", 1867 }, 1868 }, 1869 { 1870 name: "cell does not exist", 1871 ts: memorytopo.NewServer("zone1"), 1872 req: &vtctldatapb.DeleteCellInfoRequest{ 1873 Name: "zone2", 1874 }, 1875 shouldErr: true, 1876 }, 1877 } 1878 1879 for _, tt := range tests { 1880 tt := tt 1881 t.Run(tt.name, func(t *testing.T) { 1882 t.Parallel() 1883 1884 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 1885 return NewVtctldServer(ts) 1886 }) 1887 _, err := vtctld.DeleteCellInfo(ctx, tt.req) 1888 if tt.shouldErr { 1889 assert.Error(t, err) 1890 return 1891 } 1892 1893 require.NoError(t, err) 1894 ci, err := tt.ts.GetCellInfo(ctx, tt.req.Name, true) 1895 assert.True(t, topo.IsErrType(err, topo.NoNode), "expected cell %s to no longer exist; found %+v", tt.req.Name, ci) 1896 }) 1897 } 1898 } 1899 1900 func TestDeleteCellsAlias(t *testing.T) { 1901 t.Parallel() 1902 1903 ctx := context.Background() 1904 tests := []struct { 1905 name string 1906 ts *topo.Server 1907 setup func(ts *topo.Server) error 1908 req *vtctldatapb.DeleteCellsAliasRequest 1909 shouldErr bool 1910 }{ 1911 { 1912 ts: memorytopo.NewServer("zone1", "zone2"), 1913 setup: func(ts *topo.Server) error { 1914 return ts.CreateCellsAlias(ctx, "zone", &topodatapb.CellsAlias{ 1915 Cells: []string{"zone1", "zone2"}, 1916 }) 1917 }, 1918 req: &vtctldatapb.DeleteCellsAliasRequest{ 1919 Name: "zone", 1920 }, 1921 }, 1922 { 1923 name: "alias does not exist", 1924 ts: memorytopo.NewServer("zone1", "zone2"), 1925 setup: func(ts *topo.Server) error { 1926 return ts.CreateCellsAlias(ctx, "zone_a", &topodatapb.CellsAlias{ 1927 Cells: []string{"zone1", "zone2"}, 1928 }) 1929 }, 1930 req: &vtctldatapb.DeleteCellsAliasRequest{ 1931 Name: "zone_b", 1932 }, 1933 shouldErr: true, 1934 }, 1935 } 1936 1937 for _, tt := range tests { 1938 tt := tt 1939 t.Run(tt.name, func(t *testing.T) { 1940 t.Parallel() 1941 1942 if tt.setup != nil { 1943 err := tt.setup(tt.ts) 1944 require.NoError(t, err, "test setup failed") 1945 } 1946 1947 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 1948 return NewVtctldServer(ts) 1949 }) 1950 _, err := vtctld.DeleteCellsAlias(ctx, tt.req) 1951 if tt.shouldErr { 1952 assert.Error(t, err) 1953 return 1954 } 1955 1956 require.NoError(t, err) 1957 ca, err := tt.ts.GetCellsAlias(ctx, tt.req.Name, true) 1958 assert.True(t, topo.IsErrType(err, topo.NoNode), "expected cell alias %s to no longer exist; found %+v", tt.req.Name, ca) 1959 }) 1960 } 1961 } 1962 1963 func TestDeleteKeyspace(t *testing.T) { 1964 t.Parallel() 1965 1966 type testcase struct { 1967 name string 1968 keyspaces []*vtctldatapb.Keyspace 1969 shards []*vtctldatapb.Shard 1970 srvKeyspaces map[string]map[string]*topodatapb.SrvKeyspace 1971 before func(t *testing.T, ts *topo.Server, tt testcase) func() 1972 topoErr error 1973 req *vtctldatapb.DeleteKeyspaceRequest 1974 expected *vtctldatapb.DeleteKeyspaceResponse 1975 expectedRemainingKeyspaces []string 1976 expectedRemainingShards map[string][]string 1977 shouldErr bool 1978 } 1979 tests := []testcase{ 1980 { 1981 name: "success", 1982 keyspaces: []*vtctldatapb.Keyspace{ 1983 { 1984 Name: "testkeyspace", 1985 Keyspace: &topodatapb.Keyspace{}, 1986 }, 1987 }, 1988 shards: nil, 1989 srvKeyspaces: nil, 1990 topoErr: nil, 1991 req: &vtctldatapb.DeleteKeyspaceRequest{ 1992 Keyspace: "testkeyspace", 1993 }, 1994 expected: &vtctldatapb.DeleteKeyspaceResponse{}, 1995 expectedRemainingKeyspaces: []string{}, 1996 expectedRemainingShards: map[string][]string{}, 1997 shouldErr: false, 1998 }, 1999 { 2000 name: "keyspace does not exist", 2001 keyspaces: []*vtctldatapb.Keyspace{ 2002 { 2003 Name: "otherkeyspace", 2004 Keyspace: &topodatapb.Keyspace{}, 2005 }, 2006 }, 2007 shards: nil, 2008 srvKeyspaces: nil, 2009 topoErr: nil, 2010 req: &vtctldatapb.DeleteKeyspaceRequest{ 2011 Keyspace: "testkeyspace", 2012 }, 2013 expected: nil, 2014 expectedRemainingKeyspaces: []string{"otherkeyspace"}, 2015 expectedRemainingShards: map[string][]string{ 2016 "otherkeyspace": nil, 2017 }, 2018 shouldErr: true, 2019 }, 2020 { 2021 name: "keyspace has shards/Recursive=false", 2022 keyspaces: []*vtctldatapb.Keyspace{ 2023 { 2024 Name: "testkeyspace", 2025 Keyspace: &topodatapb.Keyspace{}, 2026 }, 2027 }, 2028 shards: []*vtctldatapb.Shard{ 2029 { 2030 Keyspace: "testkeyspace", 2031 Name: "-80", 2032 }, 2033 { 2034 Keyspace: "testkeyspace", 2035 Name: "80-", 2036 }, 2037 }, 2038 srvKeyspaces: nil, 2039 topoErr: nil, 2040 req: &vtctldatapb.DeleteKeyspaceRequest{ 2041 Keyspace: "testkeyspace", 2042 }, 2043 expected: nil, 2044 expectedRemainingKeyspaces: []string{"testkeyspace"}, 2045 expectedRemainingShards: map[string][]string{ 2046 "testkeyspace": {"-80", "80-"}, 2047 }, 2048 shouldErr: true, 2049 }, 2050 { 2051 name: "keyspace has shards/Recursive=true", 2052 keyspaces: []*vtctldatapb.Keyspace{ 2053 { 2054 Name: "testkeyspace", 2055 Keyspace: &topodatapb.Keyspace{}, 2056 }, 2057 }, 2058 shards: []*vtctldatapb.Shard{ 2059 { 2060 Keyspace: "testkeyspace", 2061 Name: "-80", 2062 }, 2063 { 2064 Keyspace: "testkeyspace", 2065 Name: "80-", 2066 }, 2067 }, 2068 srvKeyspaces: nil, 2069 topoErr: nil, 2070 req: &vtctldatapb.DeleteKeyspaceRequest{ 2071 Keyspace: "testkeyspace", 2072 Recursive: true, 2073 }, 2074 expected: &vtctldatapb.DeleteKeyspaceResponse{}, 2075 expectedRemainingKeyspaces: []string{}, 2076 expectedRemainingShards: map[string][]string{}, 2077 shouldErr: false, 2078 }, 2079 // Not sure how to force this case because we always pass 2080 // (Recursive=true, EvenIfServing=true) so anything short of "topo 2081 // server is down" won't fail, and "topo server is down" will cause us 2082 // to error before we even reach this point in the code, so, ¯\_(ツ)_/¯. 2083 // { 2084 // name: "recursive/cannot delete shard", 2085 // }, 2086 { 2087 name: "topo error", 2088 keyspaces: []*vtctldatapb.Keyspace{ 2089 { 2090 Name: "testkeyspace", 2091 Keyspace: &topodatapb.Keyspace{}, 2092 }, 2093 }, 2094 shards: nil, 2095 srvKeyspaces: nil, 2096 topoErr: assert.AnError, 2097 req: &vtctldatapb.DeleteKeyspaceRequest{ 2098 Keyspace: "testkeyspace", 2099 }, 2100 expected: nil, 2101 expectedRemainingKeyspaces: []string{"testkeyspace"}, 2102 expectedRemainingShards: map[string][]string{ 2103 "testkeyspace": nil, 2104 }, 2105 shouldErr: true, 2106 }, 2107 { 2108 name: "keyspace is locked", 2109 keyspaces: []*vtctldatapb.Keyspace{ 2110 { 2111 Name: "testkeyspace", 2112 Keyspace: &topodatapb.Keyspace{}, 2113 }, 2114 }, 2115 shards: nil, 2116 srvKeyspaces: nil, 2117 topoErr: nil, 2118 before: func(t *testing.T, ts *topo.Server, tt testcase) func() { 2119 _, unlock, err := ts.LockKeyspace(context.Background(), tt.req.Keyspace, "test.DeleteKeyspace") 2120 require.NoError(t, err, "failed to lock keyspace %s before test", tt.req.Keyspace) 2121 return func() { 2122 unlock(&err) 2123 if !topo.IsErrType(err, topo.NoNode) { 2124 assert.NoError(t, err, "error while unlocking keyspace %s after test", tt.req.Keyspace) 2125 } 2126 } 2127 }, 2128 req: &vtctldatapb.DeleteKeyspaceRequest{ 2129 Keyspace: "testkeyspace", 2130 }, 2131 expected: nil, 2132 expectedRemainingKeyspaces: []string{"testkeyspace"}, 2133 expectedRemainingShards: map[string][]string{ 2134 "testkeyspace": nil, 2135 }, 2136 shouldErr: true, 2137 }, 2138 { 2139 name: "keyspace is locked with force", 2140 keyspaces: []*vtctldatapb.Keyspace{ 2141 { 2142 Name: "testkeyspace", 2143 Keyspace: &topodatapb.Keyspace{}, 2144 }, 2145 }, 2146 shards: nil, 2147 srvKeyspaces: nil, 2148 topoErr: nil, 2149 before: func(t *testing.T, ts *topo.Server, tt testcase) func() { 2150 _, unlock, err := ts.LockKeyspace(context.Background(), tt.req.Keyspace, "test.DeleteKeyspace") 2151 require.NoError(t, err, "failed to lock keyspace %s before test", tt.req.Keyspace) 2152 return func() { 2153 unlock(&err) 2154 if !topo.IsErrType(err, topo.NoNode) { 2155 assert.NoError(t, err, "error while unlocking keyspace %s after test", tt.req.Keyspace) 2156 } 2157 } 2158 }, 2159 req: &vtctldatapb.DeleteKeyspaceRequest{ 2160 Keyspace: "testkeyspace", 2161 Force: true, 2162 }, 2163 expected: &vtctldatapb.DeleteKeyspaceResponse{}, 2164 expectedRemainingKeyspaces: nil, 2165 expectedRemainingShards: nil, 2166 shouldErr: false, 2167 }, 2168 } 2169 2170 for _, tt := range tests { 2171 tt := tt 2172 2173 t.Run(tt.name, func(t *testing.T) { 2174 t.Parallel() 2175 2176 cells := []string{"zone1", "zone2", "zone3"} 2177 2178 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50) 2179 defer cancel() 2180 2181 ts, topofactory := memorytopo.NewServerAndFactory(cells...) 2182 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 2183 return NewVtctldServer(ts) 2184 }) 2185 2186 testutil.AddKeyspaces(ctx, t, ts, tt.keyspaces...) 2187 testutil.AddShards(ctx, t, ts, tt.shards...) 2188 testutil.UpdateSrvKeyspaces(ctx, t, ts, tt.srvKeyspaces) 2189 2190 if tt.topoErr != nil { 2191 topofactory.SetError(tt.topoErr) 2192 } 2193 2194 defer func() { 2195 if tt.expectedRemainingKeyspaces == nil { 2196 return 2197 } 2198 2199 topofactory.SetError(nil) 2200 2201 keyspaces, err := ts.GetKeyspaces(ctx) 2202 require.NoError(t, err, "cannot get keyspaces names after DeleteKeyspace call") 2203 assert.ElementsMatch(t, tt.expectedRemainingKeyspaces, keyspaces) 2204 2205 if tt.expectedRemainingShards == nil { 2206 return 2207 } 2208 2209 remainingShards := make(map[string][]string, len(keyspaces)) 2210 2211 for _, ks := range keyspaces { 2212 shards, err := ts.GetShardNames(ctx, ks) 2213 require.NoError(t, err, "cannot get shard names for keyspace %s", ks) 2214 2215 remainingShards[ks] = shards 2216 } 2217 2218 utils.MustMatch(t, tt.expectedRemainingShards, remainingShards) 2219 }() 2220 2221 if tt.before != nil { 2222 if after := tt.before(t, ts, tt); after != nil { 2223 defer after() 2224 } 2225 } 2226 2227 resp, err := vtctld.DeleteKeyspace(ctx, tt.req) 2228 if tt.shouldErr { 2229 assert.Error(t, err) 2230 2231 return 2232 } 2233 2234 assert.NoError(t, err) 2235 utils.MustMatch(t, tt.expected, resp) 2236 }) 2237 } 2238 } 2239 2240 func TestDeleteShards(t *testing.T) { 2241 t.Parallel() 2242 2243 type testcase struct { 2244 name string 2245 shards []*vtctldatapb.Shard 2246 tablets []*topodatapb.Tablet 2247 replicationGraphs []*topo.ShardReplicationInfo 2248 srvKeyspaces map[string]map[string]*topodatapb.SrvKeyspace 2249 topoErr error 2250 before func(t *testing.T, ts *topo.Server, tt testcase) func() 2251 req *vtctldatapb.DeleteShardsRequest 2252 expected *vtctldatapb.DeleteShardsResponse 2253 expectedRemainingShards []*vtctldatapb.Shard 2254 shouldErr bool 2255 } 2256 tests := []testcase{ 2257 { 2258 name: "success", 2259 shards: []*vtctldatapb.Shard{ 2260 { 2261 Keyspace: "testkeyspace", 2262 Name: "-", 2263 }, 2264 }, 2265 tablets: nil, 2266 topoErr: nil, 2267 req: &vtctldatapb.DeleteShardsRequest{ 2268 Shards: []*vtctldatapb.Shard{ 2269 { 2270 Keyspace: "testkeyspace", 2271 Name: "-", 2272 }, 2273 }, 2274 }, 2275 expected: &vtctldatapb.DeleteShardsResponse{}, 2276 expectedRemainingShards: []*vtctldatapb.Shard{}, 2277 shouldErr: false, 2278 }, 2279 { 2280 name: "shard not found", 2281 shards: nil, 2282 tablets: nil, 2283 topoErr: nil, 2284 req: &vtctldatapb.DeleteShardsRequest{ 2285 Shards: []*vtctldatapb.Shard{ 2286 { 2287 Keyspace: "testkeyspace", 2288 Name: "-", 2289 }, 2290 }, 2291 }, 2292 expected: nil, 2293 shouldErr: true, 2294 }, 2295 { 2296 name: "multiple shards", 2297 shards: []*vtctldatapb.Shard{ 2298 { 2299 Keyspace: "testkeyspace", 2300 Name: "-", 2301 }, 2302 { 2303 Keyspace: "otherkeyspace", 2304 Name: "-80", 2305 }, 2306 { 2307 Keyspace: "otherkeyspace", 2308 Name: "80-", 2309 }, 2310 }, 2311 tablets: nil, 2312 topoErr: nil, 2313 req: &vtctldatapb.DeleteShardsRequest{ 2314 Shards: []*vtctldatapb.Shard{ 2315 { 2316 Keyspace: "testkeyspace", 2317 Name: "-", 2318 }, 2319 { 2320 Keyspace: "otherkeyspace", 2321 Name: "-80", 2322 }, 2323 }, 2324 }, 2325 expected: &vtctldatapb.DeleteShardsResponse{}, 2326 expectedRemainingShards: []*vtctldatapb.Shard{ 2327 { 2328 Keyspace: "otherkeyspace", 2329 Name: "80-", 2330 }, 2331 }, 2332 shouldErr: false, 2333 }, 2334 { 2335 name: "topo is down", 2336 shards: []*vtctldatapb.Shard{ 2337 { 2338 Keyspace: "testkeyspace", 2339 Name: "-", 2340 }, 2341 }, 2342 tablets: nil, 2343 topoErr: assert.AnError, 2344 req: &vtctldatapb.DeleteShardsRequest{ 2345 Shards: []*vtctldatapb.Shard{ 2346 { 2347 Keyspace: "testkeyspace", 2348 Name: "-", 2349 }, 2350 }, 2351 }, 2352 expected: nil, 2353 expectedRemainingShards: []*vtctldatapb.Shard{ 2354 { 2355 Keyspace: "testkeyspace", 2356 Name: "-", 2357 }, 2358 }, 2359 shouldErr: true, 2360 }, 2361 { 2362 name: "shard is serving/EvenIfServing=false", 2363 shards: []*vtctldatapb.Shard{ 2364 { 2365 Keyspace: "testkeyspace", 2366 Name: "-", 2367 }, 2368 }, 2369 tablets: nil, 2370 srvKeyspaces: map[string]map[string]*topodatapb.SrvKeyspace{ 2371 "zone1": { 2372 "testkeyspace": &topodatapb.SrvKeyspace{ 2373 Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ 2374 { 2375 ServedType: topodatapb.TabletType_PRIMARY, 2376 ShardReferences: []*topodatapb.ShardReference{ 2377 { 2378 Name: "-", 2379 KeyRange: &topodatapb.KeyRange{}, 2380 }, 2381 }, 2382 }, 2383 }, 2384 }, 2385 }, 2386 }, 2387 topoErr: nil, 2388 req: &vtctldatapb.DeleteShardsRequest{ 2389 Shards: []*vtctldatapb.Shard{ 2390 { 2391 Keyspace: "testkeyspace", 2392 Name: "-", 2393 }, 2394 }, 2395 }, 2396 expected: nil, 2397 expectedRemainingShards: []*vtctldatapb.Shard{ 2398 { 2399 Keyspace: "testkeyspace", 2400 Name: "-", 2401 }, 2402 }, 2403 shouldErr: true, 2404 }, 2405 { 2406 name: "shard is serving/EvenIfServing=true", 2407 shards: []*vtctldatapb.Shard{ 2408 { 2409 Keyspace: "testkeyspace", 2410 Name: "-", 2411 }, 2412 }, 2413 tablets: nil, 2414 srvKeyspaces: map[string]map[string]*topodatapb.SrvKeyspace{ 2415 "zone1": { 2416 "testkeyspace": &topodatapb.SrvKeyspace{ 2417 Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ 2418 { 2419 ServedType: topodatapb.TabletType_PRIMARY, 2420 ShardReferences: []*topodatapb.ShardReference{ 2421 { 2422 Name: "-", 2423 KeyRange: &topodatapb.KeyRange{}, 2424 }, 2425 }, 2426 }, 2427 }, 2428 }, 2429 }, 2430 }, 2431 topoErr: nil, 2432 req: &vtctldatapb.DeleteShardsRequest{ 2433 Shards: []*vtctldatapb.Shard{ 2434 { 2435 Keyspace: "testkeyspace", 2436 Name: "-", 2437 }, 2438 }, 2439 EvenIfServing: true, 2440 }, 2441 expected: &vtctldatapb.DeleteShardsResponse{}, 2442 expectedRemainingShards: []*vtctldatapb.Shard{}, 2443 shouldErr: false, 2444 }, 2445 { 2446 name: "ShardReplication in topo", 2447 shards: []*vtctldatapb.Shard{ 2448 { 2449 Keyspace: "testkeyspace", 2450 Name: "-", 2451 }, 2452 }, 2453 tablets: nil, 2454 replicationGraphs: []*topo.ShardReplicationInfo{ 2455 topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ 2456 Nodes: []*topodatapb.ShardReplication_Node{ 2457 { 2458 TabletAlias: &topodatapb.TabletAlias{ 2459 Cell: "zone1", 2460 Uid: 100, 2461 }, 2462 }, 2463 }, 2464 }, "zone1", "testkeyspace", "-"), 2465 topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ 2466 Nodes: []*topodatapb.ShardReplication_Node{ 2467 { 2468 TabletAlias: &topodatapb.TabletAlias{ 2469 Cell: "zone2", 2470 Uid: 200, 2471 }, 2472 }, 2473 }, 2474 }, "zone2", "testkeyspace", "-"), 2475 topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ 2476 Nodes: []*topodatapb.ShardReplication_Node{ 2477 { 2478 TabletAlias: &topodatapb.TabletAlias{ 2479 Cell: "zone3", 2480 Uid: 300, 2481 }, 2482 }, 2483 }, 2484 }, "zone3", "testkeyspace", "-"), 2485 }, 2486 topoErr: nil, 2487 req: &vtctldatapb.DeleteShardsRequest{ 2488 Shards: []*vtctldatapb.Shard{ 2489 { 2490 Keyspace: "testkeyspace", 2491 Name: "-", 2492 }, 2493 }, 2494 }, 2495 expected: &vtctldatapb.DeleteShardsResponse{}, 2496 expectedRemainingShards: []*vtctldatapb.Shard{}, 2497 shouldErr: false, 2498 }, 2499 { 2500 name: "shard has tablets/Recursive=false", 2501 shards: []*vtctldatapb.Shard{ 2502 { 2503 Keyspace: "testkeyspace", 2504 Name: "-", 2505 }, 2506 }, 2507 tablets: []*topodatapb.Tablet{ 2508 { 2509 Alias: &topodatapb.TabletAlias{ 2510 Cell: "zone1", 2511 Uid: 100, 2512 }, 2513 Keyspace: "testkeyspace", 2514 Shard: "-", 2515 }, 2516 }, 2517 topoErr: nil, 2518 req: &vtctldatapb.DeleteShardsRequest{ 2519 Shards: []*vtctldatapb.Shard{ 2520 { 2521 Keyspace: "testkeyspace", 2522 Name: "-", 2523 }, 2524 }, 2525 }, 2526 expected: nil, 2527 expectedRemainingShards: []*vtctldatapb.Shard{ 2528 { 2529 Keyspace: "testkeyspace", 2530 Name: "-", 2531 }, 2532 }, 2533 shouldErr: true, 2534 }, 2535 { 2536 name: "shard has tablets/Recursive=true", 2537 shards: []*vtctldatapb.Shard{ 2538 { 2539 Keyspace: "testkeyspace", 2540 Name: "-", 2541 }, 2542 }, 2543 tablets: []*topodatapb.Tablet{ 2544 { 2545 Alias: &topodatapb.TabletAlias{ 2546 Cell: "zone1", 2547 Uid: 100, 2548 }, 2549 Keyspace: "testkeyspace", 2550 Shard: "-", 2551 }, 2552 }, 2553 topoErr: nil, 2554 req: &vtctldatapb.DeleteShardsRequest{ 2555 Shards: []*vtctldatapb.Shard{ 2556 { 2557 Keyspace: "testkeyspace", 2558 Name: "-", 2559 }, 2560 }, 2561 Recursive: true, 2562 }, 2563 expected: &vtctldatapb.DeleteShardsResponse{}, 2564 expectedRemainingShards: []*vtctldatapb.Shard{}, 2565 shouldErr: false, 2566 }, 2567 { 2568 name: "tablets in topo belonging to other shard", 2569 shards: []*vtctldatapb.Shard{ 2570 { 2571 Keyspace: "testkeyspace", 2572 Name: "-80", 2573 }, 2574 { 2575 Keyspace: "testkeyspace", 2576 Name: "80-", 2577 }, 2578 }, 2579 tablets: []*topodatapb.Tablet{ 2580 { 2581 Alias: &topodatapb.TabletAlias{ 2582 Cell: "zone1", 2583 Uid: 100, 2584 }, 2585 Keyspace: "testkeyspace", 2586 Shard: "80-", 2587 }, 2588 }, 2589 topoErr: nil, 2590 req: &vtctldatapb.DeleteShardsRequest{ 2591 Shards: []*vtctldatapb.Shard{ 2592 { 2593 Keyspace: "testkeyspace", 2594 Name: "-80", 2595 }, 2596 }, 2597 }, 2598 expected: &vtctldatapb.DeleteShardsResponse{}, 2599 expectedRemainingShards: []*vtctldatapb.Shard{ 2600 { 2601 Keyspace: "testkeyspace", 2602 Name: "80-", 2603 }, 2604 }, 2605 shouldErr: false, 2606 }, 2607 { 2608 name: "shard is locked", 2609 shards: []*vtctldatapb.Shard{ 2610 { 2611 Keyspace: "testkeyspace", 2612 Name: "-", 2613 }, 2614 }, 2615 tablets: nil, 2616 topoErr: nil, 2617 before: func(t *testing.T, ts *topo.Server, tt testcase) func() { 2618 shard := tt.req.Shards[0] 2619 _, unlock, err := ts.LockShard(context.Background(), shard.Keyspace, shard.Name, "test.DeleteShard") 2620 require.NoError(t, err, "failed to lock shard %s/%s before test", shard.Keyspace, shard.Name) 2621 return func() { 2622 unlock(&err) 2623 if !topo.IsErrType(err, topo.NoNode) { 2624 assert.NoError(t, err, "error while unlocking shard %s/%s after test", shard.Keyspace, shard.Name) 2625 } 2626 } 2627 }, 2628 req: &vtctldatapb.DeleteShardsRequest{ 2629 Shards: []*vtctldatapb.Shard{ 2630 { 2631 Keyspace: "testkeyspace", 2632 Name: "-", 2633 }, 2634 }, 2635 }, 2636 expected: nil, 2637 expectedRemainingShards: []*vtctldatapb.Shard{ 2638 { 2639 Keyspace: "testkeyspace", 2640 Name: "-", 2641 }, 2642 }, 2643 shouldErr: true, 2644 }, 2645 { 2646 name: "shard is locked with force", 2647 shards: []*vtctldatapb.Shard{ 2648 { 2649 Keyspace: "testkeyspace", 2650 Name: "-", 2651 }, 2652 }, 2653 tablets: nil, 2654 topoErr: nil, 2655 before: func(t *testing.T, ts *topo.Server, tt testcase) func() { 2656 shard := tt.req.Shards[0] 2657 _, unlock, err := ts.LockShard(context.Background(), shard.Keyspace, shard.Name, "test.DeleteShard") 2658 require.NoError(t, err, "failed to lock shard %s/%s before test", shard.Keyspace, shard.Name) 2659 return func() { 2660 unlock(&err) 2661 if !topo.IsErrType(err, topo.NoNode) { 2662 assert.NoError(t, err, "error while unlocking shard %s/%s after test", shard.Keyspace, shard.Name) 2663 } 2664 } 2665 }, 2666 req: &vtctldatapb.DeleteShardsRequest{ 2667 Shards: []*vtctldatapb.Shard{ 2668 { 2669 Keyspace: "testkeyspace", 2670 Name: "-", 2671 }, 2672 }, 2673 Force: true, 2674 }, 2675 expected: &vtctldatapb.DeleteShardsResponse{}, 2676 expectedRemainingShards: []*vtctldatapb.Shard{}, 2677 shouldErr: false, 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 cells := []string{"zone1", "zone2", "zone3"} 2688 2689 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50) 2690 defer cancel() 2691 2692 ts, topofactory := memorytopo.NewServerAndFactory(cells...) 2693 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 2694 return NewVtctldServer(ts) 2695 }) 2696 2697 testutil.AddShards(ctx, t, ts, tt.shards...) 2698 testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) 2699 testutil.SetupReplicationGraphs(ctx, t, ts, tt.replicationGraphs...) 2700 testutil.UpdateSrvKeyspaces(ctx, t, ts, tt.srvKeyspaces) 2701 2702 if tt.topoErr != nil { 2703 topofactory.SetError(tt.topoErr) 2704 } 2705 2706 if tt.expectedRemainingShards != nil { 2707 defer func() { 2708 topofactory.SetError(nil) 2709 2710 actualShards := []*vtctldatapb.Shard{} 2711 2712 keyspaces, err := ts.GetKeyspaces(ctx) 2713 require.NoError(t, err, "cannot get keyspace names to check remaining shards") 2714 2715 for _, ks := range keyspaces { 2716 shards, err := ts.GetShardNames(ctx, ks) 2717 require.NoError(t, err, "cannot get shard names for keyspace %s", ks) 2718 2719 for _, shard := range shards { 2720 actualShards = append(actualShards, &vtctldatapb.Shard{ 2721 Keyspace: ks, 2722 Name: shard, 2723 }) 2724 } 2725 } 2726 2727 assert.ElementsMatch(t, tt.expectedRemainingShards, actualShards) 2728 }() 2729 } 2730 2731 if tt.before != nil { 2732 if after := tt.before(t, ts, tt); after != nil { 2733 defer after() 2734 } 2735 } 2736 2737 resp, err := vtctld.DeleteShards(ctx, tt.req) 2738 if tt.shouldErr { 2739 assert.Error(t, err) 2740 2741 return 2742 } 2743 2744 assert.NoError(t, err) 2745 utils.MustMatch(t, tt.expected, resp) 2746 }) 2747 } 2748 } 2749 2750 func TestDeleteSrvKeyspace(t *testing.T) { 2751 t.Parallel() 2752 2753 ctx := context.Background() 2754 tests := []struct { 2755 name string 2756 vschemas map[string]*vschemapb.SrvVSchema 2757 req *vtctldatapb.DeleteSrvVSchemaRequest 2758 shouldErr bool 2759 }{ 2760 { 2761 name: "success", 2762 vschemas: map[string]*vschemapb.SrvVSchema{ 2763 "zone1": { 2764 Keyspaces: map[string]*vschemapb.Keyspace{ 2765 "ks1": {}, 2766 "ks2": {}, 2767 }, 2768 RoutingRules: &vschemapb.RoutingRules{Rules: []*vschemapb.RoutingRule{}}, 2769 }, 2770 "zone2": { 2771 Keyspaces: map[string]*vschemapb.Keyspace{ 2772 "ks3": {}, 2773 }, 2774 RoutingRules: &vschemapb.RoutingRules{Rules: []*vschemapb.RoutingRule{}}, 2775 }, 2776 }, 2777 req: &vtctldatapb.DeleteSrvVSchemaRequest{ 2778 Cell: "zone2", 2779 }, 2780 }, 2781 { 2782 name: "cell not found", 2783 vschemas: map[string]*vschemapb.SrvVSchema{ 2784 "zone1": { 2785 Keyspaces: map[string]*vschemapb.Keyspace{ 2786 "ks1": {}, 2787 "ks2": {}, 2788 }, 2789 RoutingRules: &vschemapb.RoutingRules{Rules: []*vschemapb.RoutingRule{}}, 2790 }, 2791 "zone2": { 2792 Keyspaces: map[string]*vschemapb.Keyspace{ 2793 "ks3": {}, 2794 }, 2795 RoutingRules: &vschemapb.RoutingRules{Rules: []*vschemapb.RoutingRule{}}, 2796 }, 2797 }, 2798 req: &vtctldatapb.DeleteSrvVSchemaRequest{ 2799 Cell: "zone404", 2800 }, 2801 shouldErr: true, 2802 }, 2803 { 2804 name: "empty cell argument", 2805 req: &vtctldatapb.DeleteSrvVSchemaRequest{}, 2806 shouldErr: true, 2807 }, 2808 } 2809 2810 for _, tt := range tests { 2811 tt := tt 2812 2813 t.Run(tt.name, func(t *testing.T) { 2814 t.Parallel() 2815 2816 cells := make([]string, 0, len(tt.vschemas)) 2817 finalVSchemas := make(map[string]*vschemapb.SrvVSchema, len(tt.vschemas)) // the set of vschemas that should be left after the Delete 2818 for cell, vschema := range tt.vschemas { 2819 cells = append(cells, cell) 2820 2821 if cell == tt.req.Cell { 2822 vschema = nil 2823 } 2824 2825 finalVSchemas[cell] = vschema 2826 } 2827 2828 ts := memorytopo.NewServer(cells...) 2829 for cell, vschema := range tt.vschemas { 2830 err := ts.UpdateSrvVSchema(ctx, cell, vschema) 2831 require.NoError(t, err, "failed to update SrvVSchema in cell = %v, vschema = %+v", cell, vschema) 2832 } 2833 2834 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 2835 return NewVtctldServer(ts) 2836 }) 2837 _, err := vtctld.DeleteSrvVSchema(ctx, tt.req) 2838 if tt.shouldErr { 2839 assert.Error(t, err) 2840 return 2841 } 2842 2843 assert.NoError(t, err) 2844 2845 resp, err := vtctld.GetSrvVSchemas(ctx, &vtctldatapb.GetSrvVSchemasRequest{}) 2846 require.NoError(t, err, "GetSrvVSchemas error") 2847 utils.MustMatch(t, resp.SrvVSchemas, finalVSchemas) 2848 }) 2849 } 2850 } 2851 2852 func TestDeleteTablets(t *testing.T) { 2853 t.Parallel() 2854 2855 tests := []struct { 2856 name string 2857 tablets []*topodatapb.Tablet 2858 shardFieldUpdates map[string]func(*topo.ShardInfo) error 2859 lockedShards []*vtctldatapb.Shard 2860 topoError error 2861 req *vtctldatapb.DeleteTabletsRequest 2862 expected *vtctldatapb.DeleteTabletsResponse 2863 expectedRemainingTablets []*topodatapb.Tablet 2864 shouldErr bool 2865 }{ 2866 { 2867 name: "single replica", 2868 tablets: []*topodatapb.Tablet{ 2869 { 2870 Alias: &topodatapb.TabletAlias{ 2871 Cell: "zone1", 2872 Uid: 100, 2873 }, 2874 Type: topodatapb.TabletType_REPLICA, 2875 Keyspace: "testkeyspace", 2876 Shard: "-", 2877 }, 2878 }, 2879 lockedShards: nil, 2880 topoError: nil, 2881 req: &vtctldatapb.DeleteTabletsRequest{ 2882 TabletAliases: []*topodatapb.TabletAlias{ 2883 { 2884 Cell: "zone1", 2885 Uid: 100, 2886 }, 2887 }, 2888 }, 2889 expected: &vtctldatapb.DeleteTabletsResponse{}, 2890 expectedRemainingTablets: []*topodatapb.Tablet{}, 2891 shouldErr: false, 2892 }, 2893 { 2894 name: "single primary/no AllowPrimary", 2895 tablets: []*topodatapb.Tablet{ 2896 { 2897 Alias: &topodatapb.TabletAlias{ 2898 Cell: "zone1", 2899 Uid: 100, 2900 }, 2901 Type: topodatapb.TabletType_PRIMARY, 2902 Keyspace: "testkeyspace", 2903 Shard: "-", 2904 PrimaryTermStartTime: &vttime.Time{ 2905 Seconds: 100, 2906 Nanoseconds: 10, 2907 }, 2908 }, 2909 }, 2910 lockedShards: nil, 2911 topoError: nil, 2912 req: &vtctldatapb.DeleteTabletsRequest{ 2913 TabletAliases: []*topodatapb.TabletAlias{ 2914 { 2915 Cell: "zone1", 2916 Uid: 100, 2917 }, 2918 }, 2919 }, 2920 expected: nil, 2921 shouldErr: true, 2922 }, 2923 { 2924 name: "single primary/with AllowPrimary", 2925 tablets: []*topodatapb.Tablet{ 2926 { 2927 Alias: &topodatapb.TabletAlias{ 2928 Cell: "zone1", 2929 Uid: 100, 2930 }, 2931 Type: topodatapb.TabletType_PRIMARY, 2932 Keyspace: "testkeyspace", 2933 Shard: "-", 2934 PrimaryTermStartTime: &vttime.Time{ 2935 Seconds: 100, 2936 Nanoseconds: 10, 2937 }, 2938 }, 2939 }, 2940 lockedShards: nil, 2941 topoError: nil, 2942 req: &vtctldatapb.DeleteTabletsRequest{ 2943 TabletAliases: []*topodatapb.TabletAlias{ 2944 { 2945 Cell: "zone1", 2946 Uid: 100, 2947 }, 2948 }, 2949 AllowPrimary: true, 2950 }, 2951 expected: &vtctldatapb.DeleteTabletsResponse{}, 2952 expectedRemainingTablets: []*topodatapb.Tablet{}, 2953 shouldErr: false, 2954 }, 2955 { 2956 name: "multiple tablets", 2957 tablets: []*topodatapb.Tablet{ 2958 { 2959 Alias: &topodatapb.TabletAlias{ 2960 Cell: "zone1", 2961 Uid: 100, 2962 }, 2963 Type: topodatapb.TabletType_REPLICA, 2964 Keyspace: "testkeyspace", 2965 Shard: "-", 2966 }, 2967 { 2968 Alias: &topodatapb.TabletAlias{ 2969 Cell: "zone1", 2970 Uid: 101, 2971 }, 2972 Type: topodatapb.TabletType_REPLICA, 2973 Keyspace: "testkeyspace", 2974 Shard: "-", 2975 }, 2976 { 2977 Alias: &topodatapb.TabletAlias{ 2978 Cell: "zone1", 2979 Uid: 102, 2980 }, 2981 Type: topodatapb.TabletType_REPLICA, 2982 Keyspace: "testkeyspace", 2983 Shard: "-", 2984 }, 2985 }, 2986 lockedShards: nil, 2987 topoError: nil, 2988 req: &vtctldatapb.DeleteTabletsRequest{ 2989 TabletAliases: []*topodatapb.TabletAlias{ 2990 { 2991 Cell: "zone1", 2992 Uid: 100, 2993 }, 2994 { 2995 Cell: "zone1", 2996 Uid: 102, 2997 }, 2998 }, 2999 }, 3000 expected: &vtctldatapb.DeleteTabletsResponse{}, 3001 expectedRemainingTablets: []*topodatapb.Tablet{ 3002 { 3003 Alias: &topodatapb.TabletAlias{ 3004 Cell: "zone1", 3005 Uid: 101, 3006 }, 3007 Type: topodatapb.TabletType_REPLICA, 3008 Keyspace: "testkeyspace", 3009 Shard: "-", 3010 }, 3011 }, 3012 shouldErr: false, 3013 }, 3014 { 3015 name: "stale primary record", 3016 tablets: []*topodatapb.Tablet{ 3017 { 3018 // The stale primary we're going to delete. 3019 Alias: &topodatapb.TabletAlias{ 3020 Cell: "zone1", 3021 Uid: 100, 3022 }, 3023 Type: topodatapb.TabletType_PRIMARY, 3024 Keyspace: "testkeyspace", 3025 Shard: "-", 3026 PrimaryTermStartTime: &vttime.Time{ 3027 Seconds: 100, 3028 Nanoseconds: 10, 3029 }, 3030 }, 3031 { 3032 // The real shard primary, which we'll update in the shard 3033 // record below. 3034 Alias: &topodatapb.TabletAlias{ 3035 Cell: "zone1", 3036 Uid: 101, 3037 }, 3038 Type: topodatapb.TabletType_PRIMARY, 3039 Keyspace: "testkeyspace", 3040 Shard: "-", 3041 PrimaryTermStartTime: &vttime.Time{ 3042 Seconds: 1001, 3043 Nanoseconds: 101, 3044 }, 3045 }, 3046 }, 3047 shardFieldUpdates: map[string]func(*topo.ShardInfo) error{ 3048 "testkeyspace/-": func(si *topo.ShardInfo) error { 3049 si.PrimaryAlias = &topodatapb.TabletAlias{ 3050 Cell: "zone1", 3051 Uid: 101, 3052 } 3053 si.PrimaryTermStartTime = &vttime.Time{ 3054 Seconds: 1001, 3055 Nanoseconds: 101, 3056 } 3057 3058 return nil 3059 }, 3060 }, 3061 lockedShards: nil, 3062 topoError: nil, 3063 req: &vtctldatapb.DeleteTabletsRequest{ 3064 TabletAliases: []*topodatapb.TabletAlias{ 3065 { 3066 Cell: "zone1", 3067 Uid: 100, 3068 }, 3069 }, 3070 }, 3071 expected: &vtctldatapb.DeleteTabletsResponse{}, 3072 expectedRemainingTablets: []*topodatapb.Tablet{ 3073 { 3074 // The true shard primary still exists (phew!) 3075 Alias: &topodatapb.TabletAlias{ 3076 Cell: "zone1", 3077 Uid: 101, 3078 }, 3079 Type: topodatapb.TabletType_PRIMARY, 3080 Keyspace: "testkeyspace", 3081 Shard: "-", 3082 PrimaryTermStartTime: &vttime.Time{ 3083 Seconds: 1001, 3084 Nanoseconds: 101, 3085 }, 3086 }, 3087 }, 3088 shouldErr: false, 3089 }, 3090 { 3091 name: "tablet not found", 3092 tablets: []*topodatapb.Tablet{ 3093 { 3094 Alias: &topodatapb.TabletAlias{ 3095 Cell: "zone1", 3096 Uid: 100, 3097 }, 3098 Type: topodatapb.TabletType_REPLICA, 3099 Keyspace: "testkeyspace", 3100 Shard: "-", 3101 }, 3102 }, 3103 lockedShards: nil, 3104 topoError: nil, 3105 req: &vtctldatapb.DeleteTabletsRequest{ 3106 TabletAliases: []*topodatapb.TabletAlias{ 3107 { 3108 Cell: "zone1", 3109 Uid: 200, 3110 }, 3111 }, 3112 }, 3113 expected: nil, 3114 expectedRemainingTablets: []*topodatapb.Tablet{ 3115 { 3116 Alias: &topodatapb.TabletAlias{ 3117 Cell: "zone1", 3118 Uid: 100, 3119 }, 3120 Type: topodatapb.TabletType_REPLICA, 3121 Keyspace: "testkeyspace", 3122 Shard: "-", 3123 }, 3124 }, 3125 shouldErr: true, 3126 }, 3127 { 3128 name: "shard is locked", 3129 tablets: []*topodatapb.Tablet{ 3130 { 3131 Alias: &topodatapb.TabletAlias{ 3132 Cell: "zone1", 3133 Uid: 100, 3134 }, 3135 Type: topodatapb.TabletType_PRIMARY, 3136 Keyspace: "testkeyspace", 3137 Shard: "-", 3138 PrimaryTermStartTime: &vttime.Time{ 3139 Seconds: 100, 3140 Nanoseconds: 10, 3141 }, 3142 }, 3143 }, 3144 lockedShards: []*vtctldatapb.Shard{ 3145 { 3146 Keyspace: "testkeyspace", 3147 Name: "-", 3148 }, 3149 }, 3150 topoError: nil, 3151 req: &vtctldatapb.DeleteTabletsRequest{ 3152 TabletAliases: []*topodatapb.TabletAlias{ 3153 { 3154 Cell: "zone1", 3155 Uid: 100, 3156 }, 3157 }, 3158 AllowPrimary: true, 3159 }, 3160 expected: nil, 3161 expectedRemainingTablets: []*topodatapb.Tablet{ 3162 { 3163 Alias: &topodatapb.TabletAlias{ 3164 Cell: "zone1", 3165 Uid: 100, 3166 }, 3167 Type: topodatapb.TabletType_PRIMARY, 3168 Keyspace: "testkeyspace", 3169 Shard: "-", 3170 PrimaryTermStartTime: &vttime.Time{ 3171 Seconds: 100, 3172 Nanoseconds: 10, 3173 }, 3174 }, 3175 }, 3176 shouldErr: true, 3177 }, 3178 { 3179 name: "another shard is locked", 3180 tablets: []*topodatapb.Tablet{ 3181 { 3182 Alias: &topodatapb.TabletAlias{ 3183 Cell: "zone1", 3184 Uid: 100, 3185 }, 3186 Type: topodatapb.TabletType_PRIMARY, 3187 Keyspace: "testkeyspace", 3188 Shard: "-80", 3189 PrimaryTermStartTime: &vttime.Time{ 3190 Seconds: 100, 3191 Nanoseconds: 10, 3192 }, 3193 }, 3194 { 3195 Alias: &topodatapb.TabletAlias{ 3196 Cell: "zone1", 3197 Uid: 200, 3198 }, 3199 Type: topodatapb.TabletType_PRIMARY, 3200 Keyspace: "testkeyspace", 3201 Shard: "80-", 3202 PrimaryTermStartTime: &vttime.Time{ 3203 Seconds: 200, 3204 Nanoseconds: 20, 3205 }, 3206 }, 3207 }, 3208 lockedShards: []*vtctldatapb.Shard{ 3209 { 3210 Keyspace: "testkeyspace", 3211 Name: "80-", 3212 }, 3213 }, 3214 topoError: nil, 3215 req: &vtctldatapb.DeleteTabletsRequest{ 3216 TabletAliases: []*topodatapb.TabletAlias{ 3217 { 3218 // testkeyspace/-80 3219 Cell: "zone1", 3220 Uid: 100, 3221 }, 3222 }, 3223 AllowPrimary: true, 3224 }, 3225 expected: &vtctldatapb.DeleteTabletsResponse{}, 3226 expectedRemainingTablets: []*topodatapb.Tablet{ 3227 { 3228 Alias: &topodatapb.TabletAlias{ 3229 Cell: "zone1", 3230 Uid: 200, 3231 }, 3232 Type: topodatapb.TabletType_PRIMARY, 3233 Keyspace: "testkeyspace", 3234 Shard: "80-", 3235 PrimaryTermStartTime: &vttime.Time{ 3236 Seconds: 200, 3237 Nanoseconds: 20, 3238 }, 3239 }, 3240 }, 3241 shouldErr: false, 3242 }, 3243 { 3244 name: "topo server is down", 3245 tablets: []*topodatapb.Tablet{ 3246 { 3247 Alias: &topodatapb.TabletAlias{ 3248 Cell: "zone1", 3249 Uid: 100, 3250 }, 3251 Type: topodatapb.TabletType_REPLICA, 3252 Keyspace: "testkeyspace", 3253 Shard: "-", 3254 }, 3255 }, 3256 lockedShards: nil, 3257 topoError: assert.AnError, 3258 req: &vtctldatapb.DeleteTabletsRequest{ 3259 TabletAliases: []*topodatapb.TabletAlias{ 3260 { 3261 Cell: "zone1", 3262 Uid: 200, 3263 }, 3264 }, 3265 }, 3266 expected: nil, 3267 expectedRemainingTablets: []*topodatapb.Tablet{ 3268 { 3269 Alias: &topodatapb.TabletAlias{ 3270 Cell: "zone1", 3271 Uid: 100, 3272 }, 3273 Type: topodatapb.TabletType_REPLICA, 3274 Keyspace: "testkeyspace", 3275 Shard: "-", 3276 }, 3277 }, 3278 shouldErr: true, 3279 }, 3280 } 3281 3282 for _, tt := range tests { 3283 tt := tt 3284 3285 t.Run(tt.name, func(t *testing.T) { 3286 t.Parallel() 3287 3288 if tt.req == nil { 3289 t.Skip("focusing on other tests") 3290 } 3291 3292 ctx := context.Background() 3293 ts, topofactory := memorytopo.NewServerAndFactory("zone1") 3294 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 3295 return NewVtctldServer(ts) 3296 }) 3297 3298 // Setup tablets and shards 3299 testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) 3300 3301 for key, updateFn := range tt.shardFieldUpdates { 3302 ks, shard, err := topoproto.ParseKeyspaceShard(key) 3303 require.NoError(t, err, "bad keyspace/shard provided in shardFieldUpdates: %s", key) 3304 3305 _, err = ts.UpdateShardFields(ctx, ks, shard, updateFn) 3306 require.NoError(t, err, "failed to update shard fields for %s", key) 3307 } 3308 3309 // Set locks 3310 for _, shard := range tt.lockedShards { 3311 lctx, unlock, lerr := ts.LockShard(ctx, shard.Keyspace, shard.Name, "testing locked shard") 3312 require.NoError(t, lerr, "cannot lock shard %s/%s", shard.Keyspace, shard.Name) 3313 // unlock at the end of the test, we don't care about this error 3314 // value anymore 3315 defer unlock(&lerr) 3316 3317 // we do, however, care that the lock context gets propogated 3318 // both to additional calls to lock, and to the actual RPC call. 3319 ctx = lctx 3320 } 3321 3322 // Set errors 3323 if tt.topoError != nil { 3324 topofactory.SetError(tt.topoError) 3325 } 3326 3327 checkRemainingTablets := func() { 3328 topofactory.SetError(nil) 3329 3330 resp, err := vtctld.GetTablets(ctx, &vtctldatapb.GetTabletsRequest{}) 3331 assert.NoError(t, err, "cannot look up tablets from topo after issuing DeleteTablets request") 3332 testutil.AssertSameTablets(t, tt.expectedRemainingTablets, resp.Tablets) 3333 } 3334 3335 // Run the test 3336 resp, err := vtctld.DeleteTablets(ctx, tt.req) 3337 if tt.shouldErr { 3338 assert.Error(t, err) 3339 3340 if tt.expectedRemainingTablets != nil { 3341 checkRemainingTablets() 3342 } 3343 3344 return 3345 } 3346 3347 assert.NoError(t, err) 3348 utils.MustMatch(t, tt.expected, resp) 3349 checkRemainingTablets() 3350 }) 3351 } 3352 } 3353 3354 func TestEmergencyReparentShard(t *testing.T) { 3355 t.Parallel() 3356 3357 tests := []struct { 3358 name string 3359 ts *topo.Server 3360 tmc tmclient.TabletManagerClient 3361 tablets []*topodatapb.Tablet 3362 3363 req *vtctldatapb.EmergencyReparentShardRequest 3364 expected *vtctldatapb.EmergencyReparentShardResponse 3365 expectEventsToOccur bool 3366 shouldErr bool 3367 }{ 3368 { 3369 name: "successful reparent", 3370 ts: memorytopo.NewServer("zone1"), 3371 tablets: []*topodatapb.Tablet{ 3372 { 3373 Alias: &topodatapb.TabletAlias{ 3374 Cell: "zone1", 3375 Uid: 100, 3376 }, 3377 Type: topodatapb.TabletType_PRIMARY, 3378 PrimaryTermStartTime: &vttime.Time{ 3379 Seconds: 100, 3380 }, 3381 Keyspace: "testkeyspace", 3382 Shard: "-", 3383 }, 3384 { 3385 Alias: &topodatapb.TabletAlias{ 3386 Cell: "zone1", 3387 Uid: 200, 3388 }, 3389 Type: topodatapb.TabletType_REPLICA, 3390 Keyspace: "testkeyspace", 3391 Shard: "-", 3392 }, 3393 { 3394 Alias: &topodatapb.TabletAlias{ 3395 Cell: "zone1", 3396 Uid: 101, 3397 }, 3398 Type: topodatapb.TabletType_RDONLY, 3399 Keyspace: "testkeyspace", 3400 Shard: "-", 3401 }, 3402 }, 3403 tmc: &testutil.TabletManagerClient{ 3404 DemotePrimaryResults: map[string]struct { 3405 Status *replicationdatapb.PrimaryStatus 3406 Error error 3407 }{ 3408 "zone1-0000000100": { 3409 Status: &replicationdatapb.PrimaryStatus{ 3410 Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5", 3411 }, 3412 }, 3413 }, 3414 PopulateReparentJournalResults: map[string]error{ 3415 "zone1-0000000200": nil, 3416 }, 3417 PromoteReplicaResults: map[string]struct { 3418 Result string 3419 Error error 3420 }{ 3421 "zone1-0000000200": {}, 3422 }, 3423 PrimaryPositionResults: map[string]struct { 3424 Position string 3425 Error error 3426 }{ 3427 "zone1-0000000200": {}, 3428 }, 3429 SetReplicationSourceResults: map[string]error{ 3430 "zone1-0000000100": nil, 3431 "zone1-0000000101": nil, 3432 }, 3433 StopReplicationAndGetStatusResults: map[string]struct { 3434 StopStatus *replicationdatapb.StopReplicationStatus 3435 Error error 3436 }{ 3437 "zone1-0000000100": { 3438 Error: mysql.ErrNotReplica, 3439 }, 3440 "zone1-0000000101": { 3441 Error: assert.AnError, 3442 }, 3443 "zone1-0000000200": { 3444 StopStatus: &replicationdatapb.StopReplicationStatus{ 3445 Before: &replicationdatapb.Status{IoState: int32(mysql.ReplicationStateRunning), SqlState: int32(mysql.ReplicationStateRunning)}, 3446 After: &replicationdatapb.Status{ 3447 SourceUuid: "3E11FA47-71CA-11E1-9E33-C80AA9429562", 3448 RelayLogPosition: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5", 3449 Position: "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5", 3450 }, 3451 }, 3452 }, 3453 }, 3454 WaitForPositionResults: map[string]map[string]error{ 3455 "zone1-0000000100": { 3456 "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5": nil, 3457 }, 3458 "zone1-0000000200": { 3459 "MySQL56/3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5": nil, 3460 }, 3461 }, 3462 }, 3463 req: &vtctldatapb.EmergencyReparentShardRequest{ 3464 Keyspace: "testkeyspace", 3465 Shard: "-", 3466 NewPrimary: &topodatapb.TabletAlias{ 3467 Cell: "zone1", 3468 Uid: 200, 3469 }, 3470 WaitReplicasTimeout: protoutil.DurationToProto(time.Millisecond * 10), 3471 }, 3472 expected: &vtctldatapb.EmergencyReparentShardResponse{ 3473 Keyspace: "testkeyspace", 3474 Shard: "-", 3475 PromotedPrimary: &topodatapb.TabletAlias{ 3476 Cell: "zone1", 3477 Uid: 200, 3478 }, 3479 }, 3480 expectEventsToOccur: true, 3481 shouldErr: false, 3482 }, 3483 { 3484 // Note: this is testing the error-handling done in 3485 // (*VtctldServer).EmergencyReparentShard, not the logic of an ERS. 3486 // That logic is tested in reparentutil, and not here. Therefore, 3487 // the simplest way to trigger a failure is to attempt an ERS on a 3488 // shard that does not exist. 3489 name: "failed reparent", 3490 ts: memorytopo.NewServer("zone1"), 3491 tablets: nil, 3492 3493 req: &vtctldatapb.EmergencyReparentShardRequest{ 3494 Keyspace: "testkeyspace", 3495 Shard: "-", 3496 }, 3497 expectEventsToOccur: false, 3498 shouldErr: true, 3499 }, 3500 { 3501 name: "invalid WaitReplicasTimeout", 3502 req: &vtctldatapb.EmergencyReparentShardRequest{ 3503 WaitReplicasTimeout: &vttime.Duration{ 3504 Seconds: -1, 3505 Nanos: 1, 3506 }, 3507 }, 3508 shouldErr: true, 3509 }, 3510 } 3511 3512 ctx := context.Background() 3513 3514 for _, tt := range tests { 3515 tt := tt 3516 3517 t.Run(tt.name, func(t *testing.T) { 3518 t.Parallel() 3519 3520 testutil.AddTablets(ctx, t, tt.ts, &testutil.AddTabletOptions{ 3521 AlsoSetShardPrimary: true, 3522 ForceSetShardPrimary: true, 3523 SkipShardCreation: false, 3524 }, tt.tablets...) 3525 3526 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 3527 return NewVtctldServer(ts) 3528 }) 3529 resp, err := vtctld.EmergencyReparentShard(ctx, tt.req) 3530 3531 // We defer this because we want to check in both error and non- 3532 // error cases, but after the main set of assertions for those 3533 // cases. 3534 defer func() { 3535 if !tt.expectEventsToOccur { 3536 testutil.AssertNoLogutilEventsOccurred(t, resp, "expected no events to occur during ERS") 3537 3538 return 3539 } 3540 3541 testutil.AssertLogutilEventsOccurred(t, resp, "expected events to occur during ERS") 3542 }() 3543 3544 if tt.shouldErr { 3545 assert.Error(t, err) 3546 3547 return 3548 } 3549 3550 assert.NoError(t, err) 3551 testutil.AssertEmergencyReparentShardResponsesEqual(t, tt.expected, resp) 3552 }) 3553 } 3554 } 3555 3556 func TestExecuteFetchAsApp(t *testing.T) { 3557 t.Parallel() 3558 3559 tests := []struct { 3560 name string 3561 tablet *topodatapb.Tablet 3562 tmc *testutil.TabletManagerClient 3563 req *vtctldatapb.ExecuteFetchAsAppRequest 3564 expected *vtctldatapb.ExecuteFetchAsAppResponse 3565 shouldErr bool 3566 }{ 3567 { 3568 name: "ok", 3569 tablet: &topodatapb.Tablet{ 3570 Alias: &topodatapb.TabletAlias{ 3571 Cell: "zone1", 3572 Uid: 100, 3573 }, 3574 }, 3575 tmc: &testutil.TabletManagerClient{ 3576 ExecuteFetchAsAppResults: map[string]struct { 3577 Response *querypb.QueryResult 3578 Error error 3579 }{ 3580 "zone1-0000000100": { 3581 Response: &querypb.QueryResult{ 3582 InsertId: 100, 3583 }, 3584 }, 3585 }, 3586 }, 3587 req: &vtctldatapb.ExecuteFetchAsAppRequest{ 3588 TabletAlias: &topodatapb.TabletAlias{ 3589 Cell: "zone1", 3590 Uid: 100, 3591 }, 3592 Query: "select 1;", 3593 }, 3594 expected: &vtctldatapb.ExecuteFetchAsAppResponse{ 3595 Result: &querypb.QueryResult{ 3596 InsertId: 100, 3597 }, 3598 }, 3599 }, 3600 { 3601 name: "tablet not found", 3602 tablet: &topodatapb.Tablet{ 3603 Alias: &topodatapb.TabletAlias{ 3604 Cell: "zone1", 3605 Uid: 100, 3606 }, 3607 }, 3608 tmc: &testutil.TabletManagerClient{ 3609 ExecuteFetchAsAppResults: map[string]struct { 3610 Response *querypb.QueryResult 3611 Error error 3612 }{ 3613 "zone1-0000000100": { 3614 Response: &querypb.QueryResult{ 3615 InsertId: 100, 3616 }, 3617 }, 3618 }, 3619 }, 3620 req: &vtctldatapb.ExecuteFetchAsAppRequest{ 3621 TabletAlias: &topodatapb.TabletAlias{ 3622 Cell: "zone1", 3623 Uid: 404, 3624 }, 3625 Query: "select 1;", 3626 }, 3627 shouldErr: true, 3628 }, 3629 { 3630 name: "query error", 3631 tablet: &topodatapb.Tablet{ 3632 Alias: &topodatapb.TabletAlias{ 3633 Cell: "zone1", 3634 Uid: 100, 3635 }, 3636 }, 3637 tmc: &testutil.TabletManagerClient{ 3638 ExecuteFetchAsAppResults: map[string]struct { 3639 Response *querypb.QueryResult 3640 Error error 3641 }{ 3642 "zone1-0000000100": { 3643 Error: assert.AnError, 3644 }, 3645 }, 3646 }, 3647 req: &vtctldatapb.ExecuteFetchAsAppRequest{ 3648 TabletAlias: &topodatapb.TabletAlias{ 3649 Cell: "zone1", 3650 Uid: 100, 3651 }, 3652 Query: "select 1;", 3653 }, 3654 shouldErr: true, 3655 }, 3656 } 3657 3658 for _, tt := range tests { 3659 tt := tt 3660 t.Run(tt.name, func(t *testing.T) { 3661 t.Parallel() 3662 3663 ctx := context.Background() 3664 ts := memorytopo.NewServer("zone1") 3665 testutil.AddTablet(ctx, t, ts, tt.tablet, nil) 3666 3667 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 3668 return NewVtctldServer(ts) 3669 }) 3670 resp, err := vtctld.ExecuteFetchAsApp(ctx, tt.req) 3671 if tt.shouldErr { 3672 assert.Error(t, err) 3673 return 3674 } 3675 3676 require.NoError(t, err) 3677 utils.MustMatch(t, tt.expected, resp) 3678 }) 3679 } 3680 } 3681 3682 func TestExecuteFetchAsDBA(t *testing.T) { 3683 t.Parallel() 3684 3685 tests := []struct { 3686 name string 3687 tablet *topodatapb.Tablet 3688 tmc *testutil.TabletManagerClient 3689 req *vtctldatapb.ExecuteFetchAsDBARequest 3690 expected *vtctldatapb.ExecuteFetchAsDBAResponse 3691 shouldErr bool 3692 }{ 3693 { 3694 name: "ok", 3695 tablet: &topodatapb.Tablet{ 3696 Alias: &topodatapb.TabletAlias{ 3697 Cell: "zone1", 3698 Uid: 100, 3699 }, 3700 }, 3701 tmc: &testutil.TabletManagerClient{ 3702 ExecuteFetchAsDbaResults: map[string]struct { 3703 Response *querypb.QueryResult 3704 Error error 3705 }{ 3706 "zone1-0000000100": { 3707 Response: &querypb.QueryResult{ 3708 InsertId: 100, 3709 }, 3710 }, 3711 }, 3712 }, 3713 req: &vtctldatapb.ExecuteFetchAsDBARequest{ 3714 TabletAlias: &topodatapb.TabletAlias{ 3715 Cell: "zone1", 3716 Uid: 100, 3717 }, 3718 Query: "select 1;", 3719 }, 3720 expected: &vtctldatapb.ExecuteFetchAsDBAResponse{ 3721 Result: &querypb.QueryResult{ 3722 InsertId: 100, 3723 }, 3724 }, 3725 }, 3726 { 3727 name: "tablet not found", 3728 tablet: &topodatapb.Tablet{ 3729 Alias: &topodatapb.TabletAlias{ 3730 Cell: "zone1", 3731 Uid: 100, 3732 }, 3733 }, 3734 tmc: &testutil.TabletManagerClient{ 3735 ExecuteFetchAsDbaResults: map[string]struct { 3736 Response *querypb.QueryResult 3737 Error error 3738 }{ 3739 "zone1-0000000100": { 3740 Response: &querypb.QueryResult{ 3741 InsertId: 100, 3742 }, 3743 }, 3744 }, 3745 }, 3746 req: &vtctldatapb.ExecuteFetchAsDBARequest{ 3747 TabletAlias: &topodatapb.TabletAlias{ 3748 Cell: "zone1", 3749 Uid: 404, 3750 }, 3751 Query: "select 1;", 3752 }, 3753 shouldErr: true, 3754 }, 3755 { 3756 name: "query error", 3757 tablet: &topodatapb.Tablet{ 3758 Alias: &topodatapb.TabletAlias{ 3759 Cell: "zone1", 3760 Uid: 100, 3761 }, 3762 }, 3763 tmc: &testutil.TabletManagerClient{ 3764 ExecuteFetchAsDbaResults: map[string]struct { 3765 Response *querypb.QueryResult 3766 Error error 3767 }{ 3768 "zone1-0000000100": { 3769 Error: assert.AnError, 3770 }, 3771 }, 3772 }, 3773 req: &vtctldatapb.ExecuteFetchAsDBARequest{ 3774 TabletAlias: &topodatapb.TabletAlias{ 3775 Cell: "zone1", 3776 Uid: 100, 3777 }, 3778 Query: "select 1;", 3779 }, 3780 shouldErr: true, 3781 }, 3782 } 3783 3784 for _, tt := range tests { 3785 tt := tt 3786 t.Run(tt.name, func(t *testing.T) { 3787 t.Parallel() 3788 3789 ctx := context.Background() 3790 ts := memorytopo.NewServer("zone1") 3791 testutil.AddTablet(ctx, t, ts, tt.tablet, nil) 3792 3793 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 3794 return NewVtctldServer(ts) 3795 }) 3796 resp, err := vtctld.ExecuteFetchAsDBA(ctx, tt.req) 3797 if tt.shouldErr { 3798 assert.Error(t, err) 3799 return 3800 } 3801 3802 require.NoError(t, err) 3803 utils.MustMatch(t, tt.expected, resp) 3804 }) 3805 } 3806 } 3807 3808 func TestExecuteHook(t *testing.T) { 3809 t.Parallel() 3810 3811 tests := []struct { 3812 name string 3813 ts *topo.Server 3814 tmc tmclient.TabletManagerClient 3815 tablets []*topodatapb.Tablet 3816 req *vtctldatapb.ExecuteHookRequest 3817 shouldErr bool 3818 }{ 3819 { 3820 name: "ok", 3821 ts: memorytopo.NewServer("zone1"), 3822 tmc: &testutil.TabletManagerClient{ 3823 ExecuteHookResults: map[string]struct { 3824 Response *hk.HookResult 3825 Error error 3826 }{ 3827 "zone1-0000000100": { 3828 Response: &hk.HookResult{}, 3829 }, 3830 }, 3831 }, 3832 tablets: []*topodatapb.Tablet{ 3833 { 3834 Alias: &topodatapb.TabletAlias{ 3835 Cell: "zone1", 3836 Uid: 100, 3837 }, 3838 }, 3839 }, 3840 req: &vtctldatapb.ExecuteHookRequest{ 3841 TabletAlias: &topodatapb.TabletAlias{ 3842 Cell: "zone1", 3843 Uid: 100, 3844 }, 3845 TabletHookRequest: &tabletmanagerdatapb.ExecuteHookRequest{}, 3846 }, 3847 }, 3848 { 3849 name: "nil hook request", 3850 ts: memorytopo.NewServer("zone1"), 3851 tmc: &testutil.TabletManagerClient{ 3852 ExecuteHookResults: map[string]struct { 3853 Response *hk.HookResult 3854 Error error 3855 }{ 3856 "zone1-0000000100": { 3857 Response: &hk.HookResult{}, 3858 }, 3859 }, 3860 }, 3861 tablets: []*topodatapb.Tablet{ 3862 { 3863 Alias: &topodatapb.TabletAlias{ 3864 Cell: "zone1", 3865 Uid: 100, 3866 }, 3867 }, 3868 }, 3869 req: &vtctldatapb.ExecuteHookRequest{ 3870 TabletAlias: &topodatapb.TabletAlias{ 3871 Cell: "zone1", 3872 Uid: 100, 3873 }, 3874 TabletHookRequest: nil, 3875 }, 3876 shouldErr: true, 3877 }, 3878 { 3879 name: "hook with slash", 3880 ts: memorytopo.NewServer("zone1"), 3881 tmc: &testutil.TabletManagerClient{ 3882 ExecuteHookResults: map[string]struct { 3883 Response *hk.HookResult 3884 Error error 3885 }{ 3886 "zone1-0000000100": { 3887 Response: &hk.HookResult{}, 3888 }, 3889 }, 3890 }, 3891 tablets: []*topodatapb.Tablet{ 3892 { 3893 Alias: &topodatapb.TabletAlias{ 3894 Cell: "zone1", 3895 Uid: 100, 3896 }, 3897 }, 3898 }, 3899 req: &vtctldatapb.ExecuteHookRequest{ 3900 TabletAlias: &topodatapb.TabletAlias{ 3901 Cell: "zone1", 3902 Uid: 100, 3903 }, 3904 TabletHookRequest: &tabletmanagerdatapb.ExecuteHookRequest{ 3905 Name: "hooks/cannot/contain/slashes", 3906 }, 3907 }, 3908 shouldErr: true, 3909 }, 3910 { 3911 name: "no such tablet", 3912 ts: memorytopo.NewServer("zone1"), 3913 tmc: &testutil.TabletManagerClient{ 3914 ExecuteHookResults: map[string]struct { 3915 Response *hk.HookResult 3916 Error error 3917 }{ 3918 "zone1-0000000100": { 3919 Response: &hk.HookResult{}, 3920 }, 3921 }, 3922 }, 3923 tablets: []*topodatapb.Tablet{ 3924 { 3925 Alias: &topodatapb.TabletAlias{ 3926 Cell: "zone1", 3927 Uid: 100, 3928 }, 3929 }, 3930 }, 3931 req: &vtctldatapb.ExecuteHookRequest{ 3932 TabletAlias: &topodatapb.TabletAlias{ 3933 Cell: "zone1", 3934 Uid: 404, 3935 }, 3936 TabletHookRequest: &tabletmanagerdatapb.ExecuteHookRequest{}, 3937 }, 3938 shouldErr: true, 3939 }, 3940 { 3941 name: "tablet hook failure", 3942 ts: memorytopo.NewServer("zone1"), 3943 tmc: &testutil.TabletManagerClient{ 3944 ExecuteHookResults: map[string]struct { 3945 Response *hk.HookResult 3946 Error error 3947 }{ 3948 "zone1-0000000100": { 3949 Error: assert.AnError, 3950 }, 3951 }, 3952 }, 3953 tablets: []*topodatapb.Tablet{ 3954 { 3955 Alias: &topodatapb.TabletAlias{ 3956 Cell: "zone1", 3957 Uid: 100, 3958 }, 3959 }, 3960 }, 3961 req: &vtctldatapb.ExecuteHookRequest{ 3962 TabletAlias: &topodatapb.TabletAlias{ 3963 Cell: "zone1", 3964 Uid: 100, 3965 }, 3966 TabletHookRequest: &tabletmanagerdatapb.ExecuteHookRequest{}, 3967 }, 3968 shouldErr: true, 3969 }, 3970 } 3971 3972 ctx := context.Background() 3973 for _, tt := range tests { 3974 tt := tt 3975 t.Run(tt.name, func(t *testing.T) { 3976 t.Parallel() 3977 3978 testutil.AddTablets(ctx, t, tt.ts, nil, tt.tablets...) 3979 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 3980 return NewVtctldServer(ts) 3981 }) 3982 3983 _, err := vtctld.ExecuteHook(ctx, tt.req) 3984 if tt.shouldErr { 3985 assert.Error(t, err) 3986 return 3987 } 3988 3989 assert.NoError(t, err) 3990 }) 3991 } 3992 } 3993 3994 func TestFindAllShardsInKeyspace(t *testing.T) { 3995 t.Parallel() 3996 3997 ctx := context.Background() 3998 ts := memorytopo.NewServer("cell1") 3999 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 4000 return NewVtctldServer(ts) 4001 }) 4002 4003 ks := &vtctldatapb.Keyspace{ 4004 Name: "testkeyspace", 4005 Keyspace: &topodatapb.Keyspace{}, 4006 } 4007 testutil.AddKeyspace(ctx, t, ts, ks) 4008 4009 si1, err := ts.GetOrCreateShard(ctx, ks.Name, "-80") 4010 require.NoError(t, err) 4011 si2, err := ts.GetOrCreateShard(ctx, ks.Name, "80-") 4012 require.NoError(t, err) 4013 4014 resp, err := vtctld.FindAllShardsInKeyspace(ctx, &vtctldatapb.FindAllShardsInKeyspaceRequest{Keyspace: ks.Name}) 4015 assert.NoError(t, err) 4016 assert.NotNil(t, resp) 4017 4018 expected := map[string]*vtctldatapb.Shard{ 4019 "-80": { 4020 Keyspace: ks.Name, 4021 Name: "-80", 4022 Shard: si1.Shard, 4023 }, 4024 "80-": { 4025 Keyspace: ks.Name, 4026 Name: "80-", 4027 Shard: si2.Shard, 4028 }, 4029 } 4030 4031 utils.MustMatch(t, expected, resp.Shards) 4032 4033 _, err = vtctld.FindAllShardsInKeyspace(ctx, &vtctldatapb.FindAllShardsInKeyspaceRequest{Keyspace: "nothing"}) 4034 assert.Error(t, err) 4035 } 4036 4037 func TestGetBackups(t *testing.T) { 4038 ctx := context.Background() 4039 ts := memorytopo.NewServer() 4040 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 4041 return NewVtctldServer(ts) 4042 }) 4043 4044 testutil.BackupStorage.Backups = map[string][]string{ 4045 "testkeyspace/-": {"backup1", "backup2"}, 4046 } 4047 4048 expected := &vtctldatapb.GetBackupsResponse{ 4049 Backups: []*mysqlctlpb.BackupInfo{ 4050 { 4051 Directory: "testkeyspace/-", 4052 Name: "backup1", 4053 Keyspace: "testkeyspace", 4054 Shard: "-", 4055 }, 4056 { 4057 Directory: "testkeyspace/-", 4058 Name: "backup2", 4059 Keyspace: "testkeyspace", 4060 Shard: "-", 4061 }, 4062 }, 4063 } 4064 4065 resp, err := vtctld.GetBackups(ctx, &vtctldatapb.GetBackupsRequest{ 4066 Keyspace: "testkeyspace", 4067 Shard: "-", 4068 }) 4069 assert.NoError(t, err) 4070 utils.MustMatch(t, expected, resp) 4071 4072 t.Run("no backupstorage", func(t *testing.T) { 4073 backupstorage.BackupStorageImplementation = "doesnotexist" 4074 defer func() { backupstorage.BackupStorageImplementation = testutil.BackupStorageImplementation }() 4075 4076 _, err := vtctld.GetBackups(ctx, &vtctldatapb.GetBackupsRequest{ 4077 Keyspace: "testkeyspace", 4078 Shard: "-", 4079 }) 4080 assert.Error(t, err) 4081 }) 4082 4083 t.Run("listbackups error", func(t *testing.T) { 4084 testutil.BackupStorage.ListBackupsError = assert.AnError 4085 defer func() { testutil.BackupStorage.ListBackupsError = nil }() 4086 4087 _, err := vtctld.GetBackups(ctx, &vtctldatapb.GetBackupsRequest{ 4088 Keyspace: "testkeyspace", 4089 Shard: "-", 4090 }) 4091 assert.Error(t, err) 4092 }) 4093 4094 t.Run("parsing times and aliases", func(t *testing.T) { 4095 testutil.BackupStorage.Backups["ks2/-80"] = []string{ 4096 "2021-06-11.123456.zone1-101", 4097 } 4098 4099 resp, err := vtctld.GetBackups(ctx, &vtctldatapb.GetBackupsRequest{ 4100 Keyspace: "ks2", 4101 Shard: "-80", 4102 }) 4103 require.NoError(t, err) 4104 expected := &vtctldatapb.GetBackupsResponse{ 4105 Backups: []*mysqlctlpb.BackupInfo{ 4106 { 4107 Directory: "ks2/-80", 4108 Name: "2021-06-11.123456.zone1-101", 4109 Keyspace: "ks2", 4110 Shard: "-80", 4111 Time: protoutil.TimeToProto(time.Date(2021, time.June, 11, 12, 34, 56, 0, time.UTC)), 4112 TabletAlias: &topodatapb.TabletAlias{ 4113 Cell: "zone1", 4114 Uid: 101, 4115 }, 4116 }, 4117 }, 4118 } 4119 utils.MustMatch(t, expected, resp) 4120 }) 4121 4122 t.Run("limiting", func(t *testing.T) { 4123 unlimited, err := vtctld.GetBackups(ctx, &vtctldatapb.GetBackupsRequest{ 4124 Keyspace: "testkeyspace", 4125 Shard: "-", 4126 }) 4127 require.NoError(t, err) 4128 4129 limited, err := vtctld.GetBackups(ctx, &vtctldatapb.GetBackupsRequest{ 4130 Keyspace: "testkeyspace", 4131 Shard: "-", 4132 Limit: 1, 4133 }) 4134 require.NoError(t, err) 4135 4136 assert.Equal(t, len(limited.Backups), 1, "expected limited backups to have length 1") 4137 assert.Less(t, len(limited.Backups), len(unlimited.Backups), "expected limited backups to be less than unlimited") 4138 utils.MustMatch(t, limited.Backups[0], unlimited.Backups[len(unlimited.Backups)-1], "expected limiting to keep N most recent") 4139 }) 4140 } 4141 4142 func TestGetKeyspace(t *testing.T) { 4143 t.Parallel() 4144 4145 ctx := context.Background() 4146 ts := memorytopo.NewServer("cell1") 4147 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 4148 return NewVtctldServer(ts) 4149 }) 4150 4151 expected := &vtctldatapb.GetKeyspaceResponse{ 4152 Keyspace: &vtctldatapb.Keyspace{ 4153 Name: "testkeyspace", 4154 Keyspace: &topodatapb.Keyspace{}, 4155 }, 4156 } 4157 testutil.AddKeyspace(ctx, t, ts, expected.Keyspace) 4158 4159 ks, err := vtctld.GetKeyspace(ctx, &vtctldatapb.GetKeyspaceRequest{Keyspace: expected.Keyspace.Name}) 4160 assert.NoError(t, err) 4161 utils.MustMatch(t, expected, ks) 4162 4163 _, err = vtctld.GetKeyspace(ctx, &vtctldatapb.GetKeyspaceRequest{Keyspace: "notfound"}) 4164 assert.Error(t, err) 4165 } 4166 4167 func TestGetCellInfoNames(t *testing.T) { 4168 t.Parallel() 4169 4170 ctx := context.Background() 4171 ts := memorytopo.NewServer("cell1", "cell2", "cell3") 4172 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 4173 return NewVtctldServer(ts) 4174 }) 4175 4176 resp, err := vtctld.GetCellInfoNames(ctx, &vtctldatapb.GetCellInfoNamesRequest{}) 4177 assert.NoError(t, err) 4178 assert.ElementsMatch(t, []string{"cell1", "cell2", "cell3"}, resp.Names) 4179 4180 ts = memorytopo.NewServer() 4181 vtctld = testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 4182 return NewVtctldServer(ts) 4183 }) 4184 4185 resp, err = vtctld.GetCellInfoNames(ctx, &vtctldatapb.GetCellInfoNamesRequest{}) 4186 assert.NoError(t, err) 4187 assert.Empty(t, resp.Names) 4188 4189 ts, topofactory := memorytopo.NewServerAndFactory("cell1") 4190 vtctld = testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 4191 return NewVtctldServer(ts) 4192 }) 4193 4194 topofactory.SetError(assert.AnError) 4195 _, err = vtctld.GetCellInfoNames(ctx, &vtctldatapb.GetCellInfoNamesRequest{}) 4196 assert.Error(t, err) 4197 } 4198 4199 func TestGetCellInfo(t *testing.T) { 4200 t.Parallel() 4201 4202 ctx := context.Background() 4203 ts := memorytopo.NewServer() 4204 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 4205 return NewVtctldServer(ts) 4206 }) 4207 4208 expected := &topodatapb.CellInfo{ 4209 ServerAddress: "example.com", 4210 Root: "vitess", 4211 } 4212 input := proto.Clone(expected).(*topodatapb.CellInfo) 4213 require.NoError(t, ts.CreateCellInfo(ctx, "cell1", input)) 4214 4215 resp, err := vtctld.GetCellInfo(ctx, &vtctldatapb.GetCellInfoRequest{Cell: "cell1"}) 4216 assert.NoError(t, err) 4217 utils.MustMatch(t, expected, resp.CellInfo) 4218 4219 _, err = vtctld.GetCellInfo(ctx, &vtctldatapb.GetCellInfoRequest{Cell: "does_not_exist"}) 4220 assert.Error(t, err) 4221 4222 _, err = vtctld.GetCellInfo(ctx, &vtctldatapb.GetCellInfoRequest{}) 4223 assert.Error(t, err) 4224 } 4225 4226 func TestGetCellsAliases(t *testing.T) { 4227 t.Parallel() 4228 4229 ctx := context.Background() 4230 ts := memorytopo.NewServer("c11", "c12", "c13", "c21", "c22") 4231 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 4232 return NewVtctldServer(ts) 4233 }) 4234 4235 alias1 := &topodatapb.CellsAlias{ 4236 Cells: []string{"c11", "c12", "c13"}, 4237 } 4238 alias2 := &topodatapb.CellsAlias{ 4239 Cells: []string{"c21", "c22"}, 4240 } 4241 4242 for i, alias := range []*topodatapb.CellsAlias{alias1, alias2} { 4243 input := proto.Clone(alias).(*topodatapb.CellsAlias) 4244 name := fmt.Sprintf("a%d", i+1) 4245 require.NoError(t, ts.CreateCellsAlias(ctx, name, input), "cannot create cells alias %d (idx = %d) = %+v", i+1, i, input) 4246 } 4247 4248 expected := map[string]*topodatapb.CellsAlias{ 4249 "a1": alias1, 4250 "a2": alias2, 4251 } 4252 4253 resp, err := vtctld.GetCellsAliases(ctx, &vtctldatapb.GetCellsAliasesRequest{}) 4254 assert.NoError(t, err) 4255 utils.MustMatch(t, expected, resp.Aliases) 4256 4257 ts, topofactory := memorytopo.NewServerAndFactory() 4258 vtctld = testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 4259 return NewVtctldServer(ts) 4260 }) 4261 4262 topofactory.SetError(assert.AnError) 4263 _, err = vtctld.GetCellsAliases(ctx, &vtctldatapb.GetCellsAliasesRequest{}) 4264 assert.Error(t, err) 4265 } 4266 4267 func TestGetFullStatus(t *testing.T) { 4268 t.Parallel() 4269 4270 tests := []struct { 4271 name string 4272 cells []string 4273 tablets []*topodatapb.Tablet 4274 req *vtctldatapb.GetFullStatusRequest 4275 serverUUID string 4276 shouldErr bool 4277 }{ 4278 { 4279 name: "success", 4280 cells: []string{"zone1"}, 4281 tablets: []*topodatapb.Tablet{ 4282 { 4283 Alias: &topodatapb.TabletAlias{ 4284 Cell: "zone1", 4285 Uid: 100, 4286 }, 4287 Keyspace: "ks", 4288 Shard: "0", 4289 Type: topodatapb.TabletType_REPLICA, 4290 }, 4291 }, 4292 req: &vtctldatapb.GetFullStatusRequest{ 4293 TabletAlias: &topodatapb.TabletAlias{ 4294 Cell: "zone1", 4295 Uid: 100, 4296 }, 4297 }, 4298 serverUUID: "abcd", 4299 shouldErr: false, 4300 }, 4301 { 4302 name: "tablet not found", 4303 cells: []string{"zone1"}, 4304 tablets: []*topodatapb.Tablet{ 4305 { 4306 Alias: &topodatapb.TabletAlias{ 4307 Cell: "zone1", 4308 Uid: 200, 4309 }, 4310 Keyspace: "ks", 4311 Shard: "0", 4312 Type: topodatapb.TabletType_REPLICA, 4313 }, 4314 }, 4315 req: &vtctldatapb.GetFullStatusRequest{ 4316 TabletAlias: &topodatapb.TabletAlias{ 4317 Cell: "zone1", 4318 Uid: 100, 4319 }, 4320 }, 4321 shouldErr: true, 4322 }, 4323 } 4324 4325 for _, tt := range tests { 4326 tt := tt 4327 4328 t.Run(tt.name, func(t *testing.T) { 4329 t.Parallel() 4330 4331 ctx := context.Background() 4332 ts := memorytopo.NewServer(tt.cells...) 4333 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &testutil.TabletManagerClient{ 4334 TopoServer: ts, 4335 FullStatusResult: &replicationdatapb.FullStatus{ 4336 ServerUuid: tt.serverUUID, 4337 }, 4338 }, func(ts *topo.Server) vtctlservicepb.VtctldServer { return NewVtctldServer(ts) }) 4339 4340 testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ 4341 AlsoSetShardPrimary: true, 4342 }, tt.tablets...) 4343 4344 resp, err := vtctld.GetFullStatus(ctx, tt.req) 4345 if tt.shouldErr { 4346 assert.Error(t, err) 4347 return 4348 } 4349 4350 assert.NoError(t, err) 4351 utils.MustMatch(t, tt.serverUUID, resp.Status.ServerUuid) 4352 }) 4353 } 4354 } 4355 4356 func TestGetKeyspaces(t *testing.T) { 4357 t.Parallel() 4358 4359 ctx := context.Background() 4360 ts, topofactory := memorytopo.NewServerAndFactory("cell1") 4361 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 4362 return NewVtctldServer(ts) 4363 }) 4364 4365 resp, err := vtctld.GetKeyspaces(ctx, &vtctldatapb.GetKeyspacesRequest{}) 4366 assert.NoError(t, err) 4367 assert.Empty(t, resp.Keyspaces) 4368 4369 expected := []*vtctldatapb.Keyspace{ 4370 { 4371 Name: "ks1", 4372 Keyspace: &topodatapb.Keyspace{}, 4373 }, 4374 { 4375 Name: "ks2", 4376 Keyspace: &topodatapb.Keyspace{}, 4377 }, 4378 { 4379 Name: "ks3", 4380 Keyspace: &topodatapb.Keyspace{}, 4381 }, 4382 } 4383 for _, ks := range expected { 4384 testutil.AddKeyspace(ctx, t, ts, ks) 4385 } 4386 4387 resp, err = vtctld.GetKeyspaces(ctx, &vtctldatapb.GetKeyspacesRequest{}) 4388 assert.NoError(t, err) 4389 utils.MustMatch(t, expected, resp.Keyspaces) 4390 4391 topofactory.SetError(errors.New("error from toposerver")) 4392 4393 _, err = vtctld.GetKeyspaces(ctx, &vtctldatapb.GetKeyspacesRequest{}) 4394 assert.Error(t, err) 4395 } 4396 4397 func TestGetPermissions(t *testing.T) { 4398 t.Parallel() 4399 4400 ctx := context.Background() 4401 var testGetPermissionsReply = &tabletmanagerdatapb.Permissions{ 4402 UserPermissions: []*tabletmanagerdatapb.UserPermission{ 4403 { 4404 Host: "host1", 4405 User: "user1", 4406 Privileges: map[string]string{ 4407 "create": "yes", 4408 "delete": "no", 4409 }, 4410 }, 4411 }, 4412 DbPermissions: []*tabletmanagerdatapb.DbPermission{ 4413 { 4414 Host: "host2", 4415 Db: "db1", 4416 User: "user2", 4417 Privileges: map[string]string{ 4418 "create": "no", 4419 "delete": "yes", 4420 }, 4421 }, 4422 }, 4423 } 4424 tests := []struct { 4425 name string 4426 tablets []*topodatapb.Tablet 4427 tmc testutil.TabletManagerClient 4428 req *vtctldatapb.GetPermissionsRequest 4429 shouldErr bool 4430 }{ 4431 { 4432 name: "ok", 4433 tablets: []*topodatapb.Tablet{ 4434 { 4435 Alias: &topodatapb.TabletAlias{ 4436 Cell: "zone1", 4437 Uid: 100, 4438 }, 4439 }, 4440 }, 4441 tmc: testutil.TabletManagerClient{ 4442 GetPermissionsResults: map[string]struct { 4443 Permissions *tabletmanagerdatapb.Permissions 4444 Error error 4445 }{ 4446 "zone1-0000000100": { 4447 Permissions: testGetPermissionsReply, 4448 Error: nil, 4449 }, 4450 }, 4451 }, 4452 req: &vtctldatapb.GetPermissionsRequest{ 4453 TabletAlias: &topodatapb.TabletAlias{ 4454 Cell: "zone1", 4455 Uid: 100, 4456 }, 4457 }, 4458 }, 4459 { 4460 name: "no tablet", 4461 tablets: []*topodatapb.Tablet{ 4462 { 4463 Alias: &topodatapb.TabletAlias{ 4464 Cell: "zone1", 4465 Uid: 404, 4466 }, 4467 }, 4468 }, 4469 tmc: testutil.TabletManagerClient{ 4470 GetPermissionsResults: map[string]struct { 4471 Permissions *tabletmanagerdatapb.Permissions 4472 Error error 4473 }{ 4474 "zone1-0000000100": { 4475 Permissions: testGetPermissionsReply, 4476 Error: nil, 4477 }, 4478 }, 4479 }, 4480 req: &vtctldatapb.GetPermissionsRequest{ 4481 TabletAlias: &topodatapb.TabletAlias{ 4482 Cell: "zone1", 4483 Uid: 100, 4484 }, 4485 }, 4486 shouldErr: true, 4487 }, 4488 { 4489 name: "tmc call failed", 4490 tablets: []*topodatapb.Tablet{ 4491 { 4492 Alias: &topodatapb.TabletAlias{ 4493 Cell: "zone1", 4494 Uid: 100, 4495 }, 4496 }, 4497 }, 4498 tmc: testutil.TabletManagerClient{ 4499 GetPermissionsResults: map[string]struct { 4500 Permissions *tabletmanagerdatapb.Permissions 4501 Error error 4502 }{ 4503 "zone1-0000000100": { 4504 Permissions: testGetPermissionsReply, 4505 Error: assert.AnError, 4506 }, 4507 }, 4508 }, 4509 req: &vtctldatapb.GetPermissionsRequest{ 4510 TabletAlias: &topodatapb.TabletAlias{ 4511 Cell: "zone1", 4512 Uid: 100, 4513 }, 4514 }, 4515 shouldErr: true, 4516 }, 4517 } 4518 4519 for _, tt := range tests { 4520 tt := tt 4521 t.Run(tt.name, func(t *testing.T) { 4522 t.Parallel() 4523 4524 ts := memorytopo.NewServer("zone1") 4525 testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) 4526 4527 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 4528 return NewVtctldServer(ts) 4529 }) 4530 resp, err := vtctld.GetPermissions(ctx, tt.req) 4531 if tt.shouldErr { 4532 assert.Error(t, err) 4533 return 4534 } 4535 // we should expect same user and DB permissions as assigned 4536 assert.Equal(t, resp.Permissions.DbPermissions[0].Host, "host2") 4537 assert.Equal(t, resp.Permissions.UserPermissions[0].Host, "host1") 4538 4539 require.NoError(t, err) 4540 }) 4541 } 4542 } 4543 4544 func TestGetRoutingRules(t *testing.T) { 4545 t.Parallel() 4546 4547 ctx := context.Background() 4548 tests := []struct { 4549 name string 4550 topoDown bool 4551 rrIn *vschemapb.RoutingRules 4552 expected *vschemapb.RoutingRules 4553 shouldErr bool 4554 }{ 4555 { 4556 name: "success", 4557 rrIn: &vschemapb.RoutingRules{ 4558 Rules: []*vschemapb.RoutingRule{ 4559 { 4560 FromTable: "t1", 4561 ToTables: []string{"t2", "t3"}, 4562 }, 4563 }, 4564 }, 4565 expected: &vschemapb.RoutingRules{ 4566 Rules: []*vschemapb.RoutingRule{ 4567 { 4568 FromTable: "t1", 4569 ToTables: []string{"t2", "t3"}, 4570 }, 4571 }, 4572 }, 4573 }, 4574 { 4575 name: "empty routing rules", 4576 rrIn: nil, 4577 expected: &vschemapb.RoutingRules{}, 4578 }, 4579 { 4580 name: "topo error", 4581 topoDown: true, 4582 shouldErr: true, 4583 }, 4584 } 4585 4586 for _, tt := range tests { 4587 tt := tt 4588 t.Run(tt.name, func(t *testing.T) { 4589 t.Parallel() 4590 4591 ts, factory := memorytopo.NewServerAndFactory() 4592 if tt.rrIn != nil { 4593 err := ts.SaveRoutingRules(ctx, tt.rrIn) 4594 require.NoError(t, err, "could not save routing rules: %+v", tt.rrIn) 4595 } 4596 4597 if tt.topoDown { 4598 factory.SetError(errors.New("topo down for testing")) 4599 } 4600 4601 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 4602 return NewVtctldServer(ts) 4603 }) 4604 resp, err := vtctld.GetRoutingRules(ctx, &vtctldatapb.GetRoutingRulesRequest{}) 4605 if tt.shouldErr { 4606 assert.Error(t, err) 4607 return 4608 } 4609 4610 require.NoError(t, err) 4611 utils.MustMatch(t, resp.RoutingRules, tt.expected) 4612 }) 4613 } 4614 } 4615 4616 func TestGetSchema(t *testing.T) { 4617 ctx := context.Background() 4618 ts := memorytopo.NewServer("zone1") 4619 tmc := testutil.TabletManagerClient{ 4620 GetSchemaResults: map[string]struct { 4621 Schema *tabletmanagerdatapb.SchemaDefinition 4622 Error error 4623 }{}, 4624 } 4625 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 4626 return NewVtctldServer(ts) 4627 }) 4628 4629 validAlias := &topodatapb.TabletAlias{ 4630 Cell: "zone1", 4631 Uid: 100, 4632 } 4633 testutil.AddTablet(ctx, t, ts, &topodatapb.Tablet{ 4634 Alias: validAlias, 4635 }, nil) 4636 otherAlias := &topodatapb.TabletAlias{ 4637 Cell: "zone1", 4638 Uid: 101, 4639 } 4640 testutil.AddTablet(ctx, t, ts, &topodatapb.Tablet{ 4641 Alias: otherAlias, 4642 }, nil) 4643 4644 // we need to run this on each test case or they will pollute each other 4645 setupSchema := func() { 4646 tmc.GetSchemaResults[topoproto.TabletAliasString(validAlias)] = struct { 4647 Schema *tabletmanagerdatapb.SchemaDefinition 4648 Error error 4649 }{ 4650 Schema: &tabletmanagerdatapb.SchemaDefinition{ 4651 DatabaseSchema: "CREATE DATABASE vt_testkeyspace", 4652 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 4653 { 4654 Name: "t1", 4655 Schema: `CREATE TABLE t1 ( 4656 id int(11) not null, 4657 PRIMARY KEY (id) 4658 );`, 4659 Type: "BASE", 4660 Columns: []string{"id"}, 4661 DataLength: 100, 4662 RowCount: 50, 4663 Fields: []*querypb.Field{ 4664 { 4665 Name: "id", 4666 Type: querypb.Type_INT32, 4667 }, 4668 }, 4669 }, 4670 }, 4671 }, 4672 Error: nil, 4673 } 4674 } 4675 4676 tests := []*struct { 4677 name string 4678 req *vtctldatapb.GetSchemaRequest 4679 expected *vtctldatapb.GetSchemaResponse 4680 shouldErr bool 4681 }{ 4682 { 4683 name: "normal path", 4684 req: &vtctldatapb.GetSchemaRequest{ 4685 TabletAlias: validAlias, 4686 }, 4687 expected: &vtctldatapb.GetSchemaResponse{ 4688 Schema: &tabletmanagerdatapb.SchemaDefinition{ 4689 DatabaseSchema: "CREATE DATABASE vt_testkeyspace", 4690 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 4691 { 4692 Name: "t1", 4693 Schema: `CREATE TABLE t1 ( 4694 id int(11) not null, 4695 PRIMARY KEY (id) 4696 );`, 4697 Type: "BASE", 4698 Columns: []string{"id"}, 4699 DataLength: 100, 4700 RowCount: 50, 4701 Fields: []*querypb.Field{ 4702 { 4703 Name: "id", 4704 Type: querypb.Type_INT32, 4705 }, 4706 }, 4707 }, 4708 }, 4709 }, 4710 }, 4711 shouldErr: false, 4712 }, 4713 { 4714 name: "table names only", 4715 req: &vtctldatapb.GetSchemaRequest{ 4716 TabletAlias: validAlias, 4717 TableNamesOnly: true, 4718 }, 4719 expected: &vtctldatapb.GetSchemaResponse{ 4720 Schema: &tabletmanagerdatapb.SchemaDefinition{ 4721 DatabaseSchema: "CREATE DATABASE vt_testkeyspace", 4722 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 4723 { 4724 Name: "t1", 4725 }, 4726 }, 4727 }, 4728 }, 4729 shouldErr: false, 4730 }, 4731 { 4732 name: "table sizes only", 4733 req: &vtctldatapb.GetSchemaRequest{ 4734 TabletAlias: validAlias, 4735 TableSizesOnly: true, 4736 }, 4737 expected: &vtctldatapb.GetSchemaResponse{ 4738 Schema: &tabletmanagerdatapb.SchemaDefinition{ 4739 DatabaseSchema: "CREATE DATABASE vt_testkeyspace", 4740 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 4741 { 4742 Name: "t1", 4743 Type: "BASE", 4744 DataLength: 100, 4745 RowCount: 50, 4746 }, 4747 }, 4748 }, 4749 }, 4750 shouldErr: false, 4751 }, 4752 { 4753 name: "table names take precedence over table sizes", 4754 req: &vtctldatapb.GetSchemaRequest{ 4755 TabletAlias: validAlias, 4756 TableNamesOnly: true, 4757 TableSizesOnly: true, 4758 }, 4759 expected: &vtctldatapb.GetSchemaResponse{ 4760 Schema: &tabletmanagerdatapb.SchemaDefinition{ 4761 DatabaseSchema: "CREATE DATABASE vt_testkeyspace", 4762 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 4763 { 4764 Name: "t1", 4765 }, 4766 }, 4767 }, 4768 }, 4769 shouldErr: false, 4770 }, 4771 // error cases 4772 { 4773 name: "no tablet", 4774 req: &vtctldatapb.GetSchemaRequest{ 4775 TabletAlias: &topodatapb.TabletAlias{ 4776 Cell: "notfound", 4777 Uid: 100, 4778 }, 4779 }, 4780 expected: nil, 4781 shouldErr: true, 4782 }, 4783 { 4784 name: "no schema", 4785 req: &vtctldatapb.GetSchemaRequest{ 4786 TabletAlias: otherAlias, 4787 }, 4788 expected: nil, 4789 shouldErr: true, 4790 }, 4791 } 4792 4793 for _, tt := range tests { 4794 t.Run(tt.name, func(t *testing.T) { 4795 setupSchema() 4796 4797 resp, err := vtctld.GetSchema(ctx, tt.req) 4798 if tt.shouldErr { 4799 assert.Error(t, err) 4800 return 4801 } 4802 4803 assert.NoError(t, err) 4804 utils.MustMatch(t, tt.expected, resp) 4805 }) 4806 } 4807 } 4808 4809 func TestGetShard(t *testing.T) { 4810 t.Parallel() 4811 4812 tests := []struct { 4813 name string 4814 topo []*vtctldatapb.Shard 4815 topoError error 4816 req *vtctldatapb.GetShardRequest 4817 expected *vtctldatapb.GetShardResponse 4818 shouldErr bool 4819 }{ 4820 { 4821 name: "success", 4822 topo: []*vtctldatapb.Shard{ 4823 { 4824 Keyspace: "testkeyspace", 4825 Name: "-", 4826 }, 4827 }, 4828 topoError: nil, 4829 req: &vtctldatapb.GetShardRequest{ 4830 Keyspace: "testkeyspace", 4831 ShardName: "-", 4832 }, 4833 expected: &vtctldatapb.GetShardResponse{ 4834 Shard: &vtctldatapb.Shard{ 4835 Keyspace: "testkeyspace", 4836 Name: "-", 4837 Shard: &topodatapb.Shard{ 4838 KeyRange: &topodatapb.KeyRange{}, 4839 IsPrimaryServing: true, 4840 }, 4841 }, 4842 }, 4843 shouldErr: false, 4844 }, 4845 { 4846 name: "shard not found", 4847 topo: nil, 4848 topoError: nil, 4849 req: &vtctldatapb.GetShardRequest{ 4850 Keyspace: "testkeyspace", 4851 ShardName: "-", 4852 }, 4853 shouldErr: true, 4854 }, 4855 { 4856 name: "unavailable topo server", 4857 topo: []*vtctldatapb.Shard{ 4858 { 4859 Keyspace: "testkeyspace", 4860 Name: "-", 4861 }, 4862 }, 4863 topoError: assert.AnError, 4864 req: &vtctldatapb.GetShardRequest{}, 4865 shouldErr: true, 4866 }, 4867 } 4868 4869 for _, tt := range tests { 4870 tt := tt 4871 4872 t.Run(tt.name, func(t *testing.T) { 4873 t.Parallel() 4874 4875 cells := []string{"zone1", "zone2", "zone3"} 4876 4877 ctx := context.Background() 4878 ts, topofactory := memorytopo.NewServerAndFactory(cells...) 4879 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 4880 return NewVtctldServer(ts) 4881 }) 4882 4883 testutil.AddShards(ctx, t, ts, tt.topo...) 4884 4885 if tt.topoError != nil { 4886 topofactory.SetError(tt.topoError) 4887 } 4888 4889 resp, err := vtctld.GetShard(ctx, tt.req) 4890 if tt.shouldErr { 4891 assert.Error(t, err) 4892 return 4893 } 4894 4895 utils.MustMatch(t, tt.expected, resp) 4896 }) 4897 } 4898 } 4899 4900 func TestGetSrvKeyspaceNames(t *testing.T) { 4901 t.Parallel() 4902 4903 ctx := context.Background() 4904 tests := []struct { 4905 name string 4906 srvKeyspacesByCell map[string]map[string]*topodatapb.SrvKeyspace 4907 topoError error 4908 req *vtctldatapb.GetSrvKeyspaceNamesRequest 4909 expected *vtctldatapb.GetSrvKeyspaceNamesResponse 4910 shouldErr bool 4911 }{ 4912 { 4913 name: "success", 4914 srvKeyspacesByCell: map[string]map[string]*topodatapb.SrvKeyspace{ 4915 "zone1": { 4916 "ks1": {}, 4917 "ks2": {}, 4918 }, 4919 "zone2": { 4920 "ks1": {}, 4921 }, 4922 }, 4923 req: &vtctldatapb.GetSrvKeyspaceNamesRequest{}, 4924 expected: &vtctldatapb.GetSrvKeyspaceNamesResponse{ 4925 Names: map[string]*vtctldatapb.GetSrvKeyspaceNamesResponse_NameList{ 4926 "zone1": { 4927 Names: []string{"ks1", "ks2"}, 4928 }, 4929 "zone2": { 4930 Names: []string{"ks1"}, 4931 }, 4932 }, 4933 }, 4934 }, 4935 { 4936 name: "cell filtering", 4937 srvKeyspacesByCell: map[string]map[string]*topodatapb.SrvKeyspace{ 4938 "zone1": { 4939 "ks1": {}, 4940 "ks2": {}, 4941 }, 4942 "zone2": { 4943 "ks1": {}, 4944 }, 4945 }, 4946 req: &vtctldatapb.GetSrvKeyspaceNamesRequest{ 4947 Cells: []string{"zone2"}, 4948 }, 4949 expected: &vtctldatapb.GetSrvKeyspaceNamesResponse{ 4950 Names: map[string]*vtctldatapb.GetSrvKeyspaceNamesResponse_NameList{ 4951 "zone2": { 4952 Names: []string{"ks1"}, 4953 }, 4954 }, 4955 }, 4956 }, 4957 { 4958 name: "all cells topo down", 4959 srvKeyspacesByCell: map[string]map[string]*topodatapb.SrvKeyspace{ 4960 "zone1": { 4961 "ks1": {}, 4962 "ks2": {}, 4963 }, 4964 "zone2": { 4965 "ks1": {}, 4966 }, 4967 }, 4968 req: &vtctldatapb.GetSrvKeyspaceNamesRequest{}, 4969 topoError: errors.New("topo down for testing"), 4970 shouldErr: true, 4971 }, 4972 { 4973 name: "cell filtering topo down", 4974 srvKeyspacesByCell: map[string]map[string]*topodatapb.SrvKeyspace{ 4975 "zone1": { 4976 "ks1": {}, 4977 "ks2": {}, 4978 }, 4979 "zone2": { 4980 "ks1": {}, 4981 }, 4982 }, 4983 req: &vtctldatapb.GetSrvKeyspaceNamesRequest{ 4984 Cells: []string{"zone2"}, 4985 }, 4986 topoError: errors.New("topo down for testing"), 4987 shouldErr: true, 4988 }, 4989 } 4990 4991 for _, tt := range tests { 4992 tt := tt 4993 4994 t.Run(tt.name, func(t *testing.T) { 4995 t.Parallel() 4996 4997 cells := make([]string, 0, len(tt.srvKeyspacesByCell)) 4998 for cell := range tt.srvKeyspacesByCell { 4999 cells = append(cells, cell) 5000 } 5001 5002 ts, factory := memorytopo.NewServerAndFactory(cells...) 5003 5004 for cell, srvKeyspaces := range tt.srvKeyspacesByCell { 5005 for ks, srvks := range srvKeyspaces { 5006 err := ts.UpdateSrvKeyspace(ctx, cell, ks, srvks) 5007 require.NoError(t, err, "UpdateSrvKeyspace(%s, %s, %+v) failed", cell, ks, srvks) 5008 } 5009 } 5010 5011 if tt.topoError != nil { 5012 factory.SetError(tt.topoError) 5013 } 5014 5015 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 5016 return NewVtctldServer(ts) 5017 }) 5018 resp, err := vtctld.GetSrvKeyspaceNames(ctx, tt.req) 5019 if tt.shouldErr { 5020 assert.Error(t, err) 5021 return 5022 } 5023 5024 require.NoError(t, err) 5025 for _, names := range resp.Names { 5026 sort.Strings(names.Names) 5027 } 5028 for _, names := range tt.expected.Names { 5029 sort.Strings(names.Names) 5030 } 5031 utils.MustMatch(t, tt.expected, resp) 5032 }) 5033 } 5034 } 5035 5036 func TestGetSrvKeyspaces(t *testing.T) { 5037 t.Parallel() 5038 5039 tests := []struct { 5040 name string 5041 cells []string 5042 srvKeyspaces []*testutil.SrvKeyspace 5043 topoErr error 5044 req *vtctldatapb.GetSrvKeyspacesRequest 5045 expected *vtctldatapb.GetSrvKeyspacesResponse 5046 shouldErr bool 5047 }{ 5048 { 5049 name: "success", 5050 cells: []string{"zone1", "zone2"}, 5051 srvKeyspaces: []*testutil.SrvKeyspace{ 5052 { 5053 Cell: "zone1", 5054 Keyspace: "testkeyspace", 5055 SrvKeyspace: &topodatapb.SrvKeyspace{}, 5056 }, 5057 { 5058 Cell: "zone2", 5059 Keyspace: "testkeyspace", 5060 SrvKeyspace: &topodatapb.SrvKeyspace{}, 5061 }, 5062 }, 5063 req: &vtctldatapb.GetSrvKeyspacesRequest{ 5064 Keyspace: "testkeyspace", 5065 }, 5066 expected: &vtctldatapb.GetSrvKeyspacesResponse{ 5067 SrvKeyspaces: map[string]*topodatapb.SrvKeyspace{ 5068 "zone1": {}, 5069 "zone2": {}, 5070 }, 5071 }, 5072 shouldErr: false, 5073 }, 5074 { 5075 name: "filtering by cell", 5076 cells: []string{"zone1", "zone2"}, 5077 srvKeyspaces: []*testutil.SrvKeyspace{ 5078 { 5079 Cell: "zone1", 5080 Keyspace: "testkeyspace", 5081 SrvKeyspace: &topodatapb.SrvKeyspace{}, 5082 }, 5083 { 5084 Cell: "zone2", 5085 Keyspace: "testkeyspace", 5086 SrvKeyspace: &topodatapb.SrvKeyspace{}, 5087 }, 5088 }, 5089 req: &vtctldatapb.GetSrvKeyspacesRequest{ 5090 Keyspace: "testkeyspace", 5091 Cells: []string{"zone1"}, 5092 }, 5093 expected: &vtctldatapb.GetSrvKeyspacesResponse{ 5094 SrvKeyspaces: map[string]*topodatapb.SrvKeyspace{ 5095 "zone1": {}, 5096 }, 5097 }, 5098 shouldErr: false, 5099 }, 5100 { 5101 name: "no srvkeyspace for single cell", 5102 cells: []string{"zone1", "zone2"}, 5103 srvKeyspaces: []*testutil.SrvKeyspace{ 5104 { 5105 Cell: "zone1", 5106 Keyspace: "testkeyspace", 5107 SrvKeyspace: &topodatapb.SrvKeyspace{}, 5108 }, 5109 }, 5110 req: &vtctldatapb.GetSrvKeyspacesRequest{ 5111 Keyspace: "testkeyspace", 5112 }, 5113 expected: &vtctldatapb.GetSrvKeyspacesResponse{ 5114 SrvKeyspaces: map[string]*topodatapb.SrvKeyspace{ 5115 "zone1": {}, 5116 "zone2": nil, 5117 }, 5118 }, 5119 shouldErr: false, 5120 }, 5121 { 5122 name: "error getting cell names", 5123 cells: []string{"zone1"}, 5124 srvKeyspaces: []*testutil.SrvKeyspace{ 5125 { 5126 Cell: "zone1", 5127 Keyspace: "testkeyspace", 5128 SrvKeyspace: &topodatapb.SrvKeyspace{}, 5129 }, 5130 }, 5131 topoErr: assert.AnError, 5132 req: &vtctldatapb.GetSrvKeyspacesRequest{ 5133 Keyspace: "testkeyspace", 5134 }, 5135 shouldErr: true, 5136 }, 5137 { 5138 name: "error getting srvkeyspace", 5139 cells: []string{"zone1"}, 5140 srvKeyspaces: []*testutil.SrvKeyspace{ 5141 { 5142 Cell: "zone1", 5143 Keyspace: "testkeyspace", 5144 SrvKeyspace: &topodatapb.SrvKeyspace{}, 5145 }, 5146 }, 5147 topoErr: assert.AnError, 5148 req: &vtctldatapb.GetSrvKeyspacesRequest{ 5149 Keyspace: "testkeyspace", 5150 Cells: []string{"zone1"}, 5151 }, 5152 shouldErr: true, 5153 }, 5154 } 5155 5156 ctx := context.Background() 5157 5158 for _, tt := range tests { 5159 tt := tt 5160 5161 t.Run(tt.name, func(t *testing.T) { 5162 t.Parallel() 5163 5164 if tt.req == nil { 5165 t.SkipNow() 5166 } 5167 5168 ts, topofactory := memorytopo.NewServerAndFactory(tt.cells...) 5169 5170 testutil.AddSrvKeyspaces(t, ts, tt.srvKeyspaces...) 5171 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 5172 return NewVtctldServer(ts) 5173 }) 5174 5175 if tt.topoErr != nil { 5176 topofactory.SetError(tt.topoErr) 5177 } 5178 5179 resp, err := vtctld.GetSrvKeyspaces(ctx, tt.req) 5180 if tt.shouldErr { 5181 assert.Error(t, err) 5182 5183 return 5184 } 5185 5186 assert.NoError(t, err) 5187 utils.MustMatch(t, tt.expected, resp) 5188 }) 5189 } 5190 } 5191 5192 func TestGetSrvVSchema(t *testing.T) { 5193 t.Parallel() 5194 5195 ctx := context.Background() 5196 ts, topofactory := memorytopo.NewServerAndFactory("zone1", "zone2") 5197 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 5198 return NewVtctldServer(ts) 5199 }) 5200 5201 zone1SrvVSchema := &vschemapb.SrvVSchema{ 5202 Keyspaces: map[string]*vschemapb.Keyspace{ 5203 "testkeyspace": { 5204 Sharded: true, 5205 RequireExplicitRouting: false, 5206 }, 5207 }, 5208 RoutingRules: &vschemapb.RoutingRules{ 5209 Rules: []*vschemapb.RoutingRule{}, 5210 }, 5211 } 5212 zone2SrvVSchema := &vschemapb.SrvVSchema{ 5213 Keyspaces: map[string]*vschemapb.Keyspace{ 5214 "testkeyspace": { 5215 Sharded: true, 5216 RequireExplicitRouting: false, 5217 }, 5218 "unsharded": { 5219 Sharded: false, 5220 RequireExplicitRouting: false, 5221 }, 5222 }, 5223 RoutingRules: &vschemapb.RoutingRules{ 5224 Rules: []*vschemapb.RoutingRule{}, 5225 }, 5226 } 5227 5228 err := ts.UpdateSrvVSchema(ctx, "zone1", zone1SrvVSchema) 5229 require.NoError(t, err, "cannot add zone1 srv vschema") 5230 err = ts.UpdateSrvVSchema(ctx, "zone2", zone2SrvVSchema) 5231 require.NoError(t, err, "cannot add zone2 srv vschema") 5232 5233 expected := &vschemapb.SrvVSchema{ // have to copy our structs because of proto marshal artifacts 5234 Keyspaces: map[string]*vschemapb.Keyspace{ 5235 "testkeyspace": { 5236 Sharded: true, 5237 RequireExplicitRouting: false, 5238 }, 5239 }, 5240 RoutingRules: &vschemapb.RoutingRules{ 5241 Rules: []*vschemapb.RoutingRule{}, 5242 }, 5243 } 5244 resp, err := vtctld.GetSrvVSchema(ctx, &vtctldatapb.GetSrvVSchemaRequest{Cell: "zone1"}) 5245 assert.NoError(t, err) 5246 utils.MustMatch(t, expected.Keyspaces, resp.SrvVSchema.Keyspaces, "GetSrvVSchema(zone1) mismatch") 5247 assert.ElementsMatch(t, expected.RoutingRules.Rules, resp.SrvVSchema.RoutingRules.Rules, "GetSrvVSchema(zone1) rules mismatch") 5248 5249 expected = &vschemapb.SrvVSchema{ // have to copy our structs because of proto marshal artifacts 5250 Keyspaces: map[string]*vschemapb.Keyspace{ 5251 "testkeyspace": { 5252 Sharded: true, 5253 RequireExplicitRouting: false, 5254 }, 5255 "unsharded": { 5256 Sharded: false, 5257 RequireExplicitRouting: false, 5258 }, 5259 }, 5260 RoutingRules: &vschemapb.RoutingRules{ 5261 Rules: []*vschemapb.RoutingRule{}, 5262 }, 5263 } 5264 resp, err = vtctld.GetSrvVSchema(ctx, &vtctldatapb.GetSrvVSchemaRequest{Cell: "zone2"}) 5265 assert.NoError(t, err) 5266 utils.MustMatch(t, expected.Keyspaces, resp.SrvVSchema.Keyspaces, "GetSrvVSchema(zone2) mismatch") 5267 assert.ElementsMatch(t, expected.RoutingRules.Rules, resp.SrvVSchema.RoutingRules.Rules, "GetSrvVSchema(zone2) rules mismatch") 5268 5269 resp, err = vtctld.GetSrvVSchema(ctx, &vtctldatapb.GetSrvVSchemaRequest{Cell: "dne"}) 5270 assert.Error(t, err, "GetSrvVSchema(dne)") 5271 assert.Nil(t, resp, "GetSrvVSchema(dne)") 5272 5273 topofactory.SetError(assert.AnError) 5274 _, err = vtctld.GetSrvVSchema(ctx, &vtctldatapb.GetSrvVSchemaRequest{Cell: "zone1"}) 5275 assert.Error(t, err) 5276 } 5277 5278 func TestGetSrvVSchemas(t *testing.T) { 5279 t.Parallel() 5280 5281 tests := []struct { 5282 name string 5283 req *vtctldatapb.GetSrvVSchemasRequest 5284 expected *vtctldatapb.GetSrvVSchemasResponse 5285 topoErr error 5286 shouldErr bool 5287 }{ 5288 { 5289 name: "success", 5290 req: &vtctldatapb.GetSrvVSchemasRequest{}, 5291 expected: &vtctldatapb.GetSrvVSchemasResponse{ 5292 SrvVSchemas: map[string]*vschemapb.SrvVSchema{ 5293 "zone1": { 5294 Keyspaces: map[string]*vschemapb.Keyspace{ 5295 "testkeyspace": { 5296 Sharded: true, 5297 RequireExplicitRouting: false, 5298 }, 5299 }, 5300 RoutingRules: &vschemapb.RoutingRules{ 5301 Rules: []*vschemapb.RoutingRule{}, 5302 }, 5303 }, 5304 "zone2": { 5305 Keyspaces: map[string]*vschemapb.Keyspace{ 5306 "testkeyspace": { 5307 Sharded: true, 5308 RequireExplicitRouting: false, 5309 }, 5310 "unsharded": { 5311 Sharded: false, 5312 RequireExplicitRouting: false, 5313 }, 5314 }, 5315 RoutingRules: &vschemapb.RoutingRules{ 5316 Rules: []*vschemapb.RoutingRule{}, 5317 }, 5318 }, 5319 "zone3": {}, 5320 }, 5321 }, 5322 }, 5323 { 5324 name: "filtering by cell", 5325 req: &vtctldatapb.GetSrvVSchemasRequest{ 5326 Cells: []string{"zone2"}, 5327 }, 5328 expected: &vtctldatapb.GetSrvVSchemasResponse{ 5329 SrvVSchemas: map[string]*vschemapb.SrvVSchema{ 5330 "zone2": { 5331 Keyspaces: map[string]*vschemapb.Keyspace{ 5332 "testkeyspace": { 5333 Sharded: true, 5334 RequireExplicitRouting: false, 5335 }, 5336 "unsharded": { 5337 Sharded: false, 5338 RequireExplicitRouting: false, 5339 }, 5340 }, 5341 RoutingRules: &vschemapb.RoutingRules{ 5342 Rules: []*vschemapb.RoutingRule{}, 5343 }, 5344 }, 5345 }, 5346 }, 5347 }, 5348 { 5349 name: "no SrvVSchema for single cell", 5350 req: &vtctldatapb.GetSrvVSchemasRequest{ 5351 Cells: []string{"zone3"}, 5352 }, 5353 expected: &vtctldatapb.GetSrvVSchemasResponse{ 5354 SrvVSchemas: map[string]*vschemapb.SrvVSchema{ 5355 "zone3": {}, 5356 }, 5357 }, 5358 }, 5359 { 5360 name: "topology error", 5361 req: &vtctldatapb.GetSrvVSchemasRequest{ 5362 Cells: []string{"zone2"}, 5363 }, 5364 topoErr: assert.AnError, 5365 shouldErr: true, 5366 }, 5367 { 5368 name: "cell doesn't exist", 5369 req: &vtctldatapb.GetSrvVSchemasRequest{ 5370 Cells: []string{"doesnt-exist"}, 5371 }, 5372 expected: &vtctldatapb.GetSrvVSchemasResponse{ 5373 SrvVSchemas: map[string]*vschemapb.SrvVSchema{}, 5374 }, 5375 }, 5376 { 5377 name: "one of many cells doesn't exist", 5378 req: &vtctldatapb.GetSrvVSchemasRequest{ 5379 Cells: []string{"zone1", "doesnt-exist"}, 5380 }, 5381 expected: &vtctldatapb.GetSrvVSchemasResponse{ 5382 SrvVSchemas: map[string]*vschemapb.SrvVSchema{ 5383 "zone1": { 5384 Keyspaces: map[string]*vschemapb.Keyspace{ 5385 "testkeyspace": { 5386 Sharded: true, 5387 RequireExplicitRouting: false, 5388 }, 5389 }, 5390 RoutingRules: &vschemapb.RoutingRules{ 5391 Rules: []*vschemapb.RoutingRule{}, 5392 }, 5393 }, 5394 }, 5395 }, 5396 }, 5397 } 5398 5399 for _, tt := range tests { 5400 tt := tt 5401 5402 t.Run(tt.name, func(t *testing.T) { 5403 t.Parallel() 5404 5405 ctx := context.Background() 5406 ts, topofactory := memorytopo.NewServerAndFactory("zone1", "zone2", "zone3") 5407 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 5408 return NewVtctldServer(ts) 5409 }) 5410 5411 zone1SrvVSchema := &vschemapb.SrvVSchema{ 5412 Keyspaces: map[string]*vschemapb.Keyspace{ 5413 "testkeyspace": { 5414 Sharded: true, 5415 RequireExplicitRouting: false, 5416 }, 5417 }, 5418 RoutingRules: &vschemapb.RoutingRules{ 5419 Rules: []*vschemapb.RoutingRule{}, 5420 }, 5421 } 5422 5423 zone2SrvVSchema := &vschemapb.SrvVSchema{ 5424 Keyspaces: map[string]*vschemapb.Keyspace{ 5425 "testkeyspace": { 5426 Sharded: true, 5427 RequireExplicitRouting: false, 5428 }, 5429 "unsharded": { 5430 Sharded: false, 5431 RequireExplicitRouting: false, 5432 }, 5433 }, 5434 RoutingRules: &vschemapb.RoutingRules{ 5435 Rules: []*vschemapb.RoutingRule{}, 5436 }, 5437 } 5438 5439 err := ts.UpdateSrvVSchema(ctx, "zone1", zone1SrvVSchema) 5440 require.NoError(t, err, "cannot add zone1 srv vschema") 5441 err = ts.UpdateSrvVSchema(ctx, "zone2", zone2SrvVSchema) 5442 require.NoError(t, err, "cannot add zone2 srv vschema") 5443 5444 if tt.topoErr != nil { 5445 topofactory.SetError(tt.topoErr) 5446 } 5447 5448 resp, err := vtctld.GetSrvVSchemas(ctx, tt.req) 5449 5450 if tt.shouldErr { 5451 assert.Error(t, err) 5452 return 5453 } 5454 5455 assert.NoError(t, err) 5456 utils.MustMatch(t, tt.expected, resp) 5457 }) 5458 } 5459 } 5460 5461 func TestGetTablet(t *testing.T) { 5462 t.Parallel() 5463 5464 ctx := context.Background() 5465 ts := memorytopo.NewServer("cell1") 5466 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 5467 return NewVtctldServer(ts) 5468 }) 5469 5470 tablet := &topodatapb.Tablet{ 5471 Alias: &topodatapb.TabletAlias{ 5472 Cell: "cell1", 5473 Uid: 100, 5474 }, 5475 Hostname: "localhost", 5476 Keyspace: "testkeyspace", 5477 Shard: "-", 5478 Type: topodatapb.TabletType_REPLICA, 5479 } 5480 5481 testutil.AddTablet(ctx, t, ts, tablet, nil) 5482 5483 resp, err := vtctld.GetTablet(ctx, &vtctldatapb.GetTabletRequest{ 5484 TabletAlias: &topodatapb.TabletAlias{ 5485 Cell: "cell1", 5486 Uid: 100, 5487 }, 5488 }) 5489 assert.NoError(t, err) 5490 utils.MustMatch(t, resp.Tablet, tablet) 5491 5492 // not found 5493 _, err = vtctld.GetTablet(ctx, &vtctldatapb.GetTabletRequest{ 5494 TabletAlias: &topodatapb.TabletAlias{ 5495 Cell: "cell1", 5496 Uid: 101, 5497 }, 5498 }) 5499 assert.Error(t, err) 5500 } 5501 5502 func TestGetTablets(t *testing.T) { 5503 t.Parallel() 5504 5505 tests := []struct { 5506 name string 5507 cells []string 5508 tablets []*topodatapb.Tablet 5509 req *vtctldatapb.GetTabletsRequest 5510 expected []*topodatapb.Tablet 5511 shouldErr bool 5512 }{ 5513 { 5514 name: "no tablets", 5515 cells: []string{"cell1"}, 5516 tablets: []*topodatapb.Tablet{}, 5517 req: &vtctldatapb.GetTabletsRequest{}, 5518 expected: []*topodatapb.Tablet{}, 5519 shouldErr: false, 5520 }, 5521 { 5522 name: "keyspace and shard filter", 5523 cells: []string{"cell1"}, 5524 tablets: []*topodatapb.Tablet{ 5525 { 5526 Alias: &topodatapb.TabletAlias{ 5527 Cell: "cell1", 5528 Uid: 100, 5529 }, 5530 Keyspace: "ks1", 5531 Shard: "-80", 5532 }, 5533 { 5534 Alias: &topodatapb.TabletAlias{ 5535 Cell: "cell1", 5536 Uid: 101, 5537 }, 5538 Keyspace: "ks1", 5539 Shard: "80-", 5540 }, 5541 { 5542 Alias: &topodatapb.TabletAlias{ 5543 Cell: "cell1", 5544 Uid: 102, 5545 }, 5546 Keyspace: "ks2", 5547 Shard: "-", 5548 }, 5549 }, 5550 req: &vtctldatapb.GetTabletsRequest{ 5551 Keyspace: "ks1", 5552 Shard: "80-", 5553 }, 5554 expected: []*topodatapb.Tablet{ 5555 { 5556 Alias: &topodatapb.TabletAlias{ 5557 Cell: "cell1", 5558 Uid: 101, 5559 }, 5560 Keyspace: "ks1", 5561 Shard: "80-", 5562 }, 5563 }, 5564 shouldErr: false, 5565 }, 5566 { 5567 name: "keyspace filter", 5568 cells: []string{"cell1"}, 5569 tablets: []*topodatapb.Tablet{ 5570 { 5571 Alias: &topodatapb.TabletAlias{ 5572 Cell: "cell1", 5573 Uid: 100, 5574 }, 5575 Keyspace: "ks1", 5576 }, 5577 { 5578 Alias: &topodatapb.TabletAlias{ 5579 Cell: "cell1", 5580 Uid: 101, 5581 }, 5582 Keyspace: "ks1", 5583 }, 5584 { 5585 Alias: &topodatapb.TabletAlias{ 5586 Cell: "cell1", 5587 Uid: 102, 5588 }, 5589 Keyspace: "otherkeyspace", 5590 }, 5591 }, 5592 req: &vtctldatapb.GetTabletsRequest{ 5593 Keyspace: "ks1", 5594 }, 5595 expected: []*topodatapb.Tablet{ 5596 { 5597 Alias: &topodatapb.TabletAlias{ 5598 Cell: "cell1", 5599 Uid: 100, 5600 }, 5601 Keyspace: "ks1", 5602 }, 5603 { 5604 Alias: &topodatapb.TabletAlias{ 5605 Cell: "cell1", 5606 Uid: 101, 5607 }, 5608 Keyspace: "ks1", 5609 }, 5610 }, 5611 shouldErr: false, 5612 }, 5613 { 5614 name: "keyspace and shard filter - stale primary", 5615 cells: []string{"cell1"}, 5616 tablets: []*topodatapb.Tablet{ 5617 { 5618 Alias: &topodatapb.TabletAlias{ 5619 Cell: "cell1", 5620 Uid: 100, 5621 }, 5622 Keyspace: "ks1", 5623 Shard: "-80", 5624 }, 5625 { 5626 Alias: &topodatapb.TabletAlias{ 5627 Cell: "cell1", 5628 Uid: 101, 5629 }, 5630 Keyspace: "ks1", 5631 Shard: "80-", 5632 }, 5633 { 5634 Alias: &topodatapb.TabletAlias{ 5635 Cell: "cell1", 5636 Uid: 102, 5637 }, 5638 Keyspace: "ks2", 5639 Shard: "-", 5640 Type: topodatapb.TabletType_PRIMARY, 5641 PrimaryTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)), 5642 }, 5643 { 5644 Alias: &topodatapb.TabletAlias{ 5645 Cell: "cell1", 5646 Uid: 103, 5647 }, 5648 Keyspace: "ks2", 5649 Shard: "-", 5650 Hostname: "stale.primary", 5651 Type: topodatapb.TabletType_PRIMARY, 5652 PrimaryTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 14, 4, 5, 0, time.UTC)), 5653 }, 5654 }, 5655 req: &vtctldatapb.GetTabletsRequest{ 5656 Keyspace: "ks2", 5657 Shard: "-", 5658 }, 5659 expected: []*topodatapb.Tablet{ 5660 { 5661 Alias: &topodatapb.TabletAlias{ 5662 Cell: "cell1", 5663 Uid: 102, 5664 }, 5665 Keyspace: "ks2", 5666 Shard: "-", 5667 Type: topodatapb.TabletType_PRIMARY, 5668 PrimaryTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)), 5669 }, 5670 { 5671 Alias: &topodatapb.TabletAlias{ 5672 Cell: "cell1", 5673 Uid: 103, 5674 }, 5675 Keyspace: "ks2", 5676 Shard: "-", 5677 Hostname: "stale.primary", 5678 Type: topodatapb.TabletType_UNKNOWN, 5679 PrimaryTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 14, 4, 5, 0, time.UTC)), 5680 }, 5681 }, 5682 shouldErr: false, 5683 }, 5684 { 5685 name: "stale primary", 5686 cells: []string{"cell1"}, 5687 tablets: []*topodatapb.Tablet{ 5688 { 5689 Alias: &topodatapb.TabletAlias{ 5690 Cell: "cell1", 5691 Uid: 100, 5692 }, 5693 Keyspace: "ks1", 5694 Shard: "-", 5695 Hostname: "slightly less stale", 5696 Type: topodatapb.TabletType_PRIMARY, 5697 PrimaryTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)), 5698 }, 5699 { 5700 Alias: &topodatapb.TabletAlias{ 5701 Cell: "cell1", 5702 Uid: 101, 5703 }, 5704 Hostname: "stale primary", 5705 Keyspace: "ks1", 5706 Shard: "-", 5707 Type: topodatapb.TabletType_PRIMARY, 5708 PrimaryTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 14, 4, 5, 0, time.UTC)), 5709 }, 5710 { 5711 Alias: &topodatapb.TabletAlias{ 5712 Cell: "cell1", 5713 Uid: 103, 5714 }, 5715 Hostname: "true primary", 5716 Keyspace: "ks1", 5717 Shard: "-", 5718 Type: topodatapb.TabletType_PRIMARY, 5719 PrimaryTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 16, 4, 5, 0, time.UTC)), 5720 }, 5721 }, 5722 req: &vtctldatapb.GetTabletsRequest{}, 5723 expected: []*topodatapb.Tablet{ 5724 { 5725 Alias: &topodatapb.TabletAlias{ 5726 Cell: "cell1", 5727 Uid: 100, 5728 }, 5729 Keyspace: "ks1", 5730 Shard: "-", 5731 Hostname: "slightly less stale", 5732 Type: topodatapb.TabletType_UNKNOWN, 5733 PrimaryTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 15, 4, 5, 0, time.UTC)), 5734 }, 5735 { 5736 Alias: &topodatapb.TabletAlias{ 5737 Cell: "cell1", 5738 Uid: 101, 5739 }, 5740 Hostname: "stale primary", 5741 Keyspace: "ks1", 5742 Shard: "-", 5743 Type: topodatapb.TabletType_UNKNOWN, 5744 PrimaryTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 14, 4, 5, 0, time.UTC)), 5745 }, 5746 { 5747 Alias: &topodatapb.TabletAlias{ 5748 Cell: "cell1", 5749 Uid: 103, 5750 }, 5751 Hostname: "true primary", 5752 Keyspace: "ks1", 5753 Shard: "-", 5754 Type: topodatapb.TabletType_PRIMARY, 5755 PrimaryTermStartTime: logutil.TimeToProto(time.Date(2006, time.January, 2, 16, 4, 5, 0, time.UTC)), 5756 }, 5757 }, 5758 shouldErr: false, 5759 }, 5760 { 5761 name: "keyspace and shard filter - error", 5762 cells: []string{"cell1"}, 5763 tablets: []*topodatapb.Tablet{}, 5764 req: &vtctldatapb.GetTabletsRequest{ 5765 Keyspace: "ks1", 5766 Shard: "-", 5767 }, 5768 expected: []*topodatapb.Tablet{}, 5769 shouldErr: true, 5770 }, 5771 { 5772 name: "cells filter", 5773 cells: []string{"cell1", "cell2", "cell3"}, 5774 tablets: []*topodatapb.Tablet{ 5775 { 5776 Alias: &topodatapb.TabletAlias{ 5777 Cell: "cell1", 5778 Uid: 100, 5779 }, 5780 }, 5781 { 5782 Alias: &topodatapb.TabletAlias{ 5783 Cell: "cell2", 5784 Uid: 200, 5785 }, 5786 }, 5787 { 5788 Alias: &topodatapb.TabletAlias{ 5789 Cell: "cell3", 5790 Uid: 300, 5791 }, 5792 }, 5793 }, 5794 req: &vtctldatapb.GetTabletsRequest{ 5795 Cells: []string{"cell1", "cell3"}, 5796 }, 5797 expected: []*topodatapb.Tablet{ 5798 { 5799 Alias: &topodatapb.TabletAlias{ 5800 Cell: "cell1", 5801 Uid: 100, 5802 }, 5803 }, 5804 { 5805 Alias: &topodatapb.TabletAlias{ 5806 Cell: "cell3", 5807 Uid: 300, 5808 }, 5809 }, 5810 }, 5811 shouldErr: false, 5812 }, 5813 { 5814 name: "cells filter with single error is nonfatal", 5815 cells: []string{"cell1"}, 5816 tablets: []*topodatapb.Tablet{ 5817 { 5818 Alias: &topodatapb.TabletAlias{ 5819 Cell: "cell1", 5820 Uid: 100, 5821 }, 5822 Keyspace: "ks1", 5823 Shard: "-", 5824 }, 5825 }, 5826 req: &vtctldatapb.GetTabletsRequest{ 5827 Cells: []string{"cell1", "doesnotexist"}, 5828 }, 5829 expected: []*topodatapb.Tablet{ 5830 { 5831 Alias: &topodatapb.TabletAlias{ 5832 Cell: "cell1", 5833 Uid: 100, 5834 }, 5835 Keyspace: "ks1", 5836 Shard: "-", 5837 }, 5838 }, 5839 shouldErr: false, 5840 }, 5841 { 5842 name: "cells filter with single error is fatal in strict mode", 5843 cells: []string{"cell1"}, 5844 tablets: []*topodatapb.Tablet{ 5845 { 5846 Alias: &topodatapb.TabletAlias{ 5847 Cell: "cell1", 5848 Uid: 100, 5849 }, 5850 Keyspace: "ks1", 5851 Shard: "-", 5852 }, 5853 }, 5854 req: &vtctldatapb.GetTabletsRequest{ 5855 Cells: []string{"cell1", "doesnotexist"}, 5856 Strict: true, 5857 }, 5858 shouldErr: true, 5859 }, 5860 { 5861 name: "in nonstrict mode if all cells fail the request fails", 5862 cells: []string{"cell1"}, 5863 tablets: []*topodatapb.Tablet{ 5864 { 5865 Alias: &topodatapb.TabletAlias{ 5866 Cell: "cell1", 5867 Uid: 100, 5868 }, 5869 Keyspace: "ks1", 5870 Shard: "-", 5871 }, 5872 }, 5873 req: &vtctldatapb.GetTabletsRequest{ 5874 Cells: []string{"doesnotexist", "alsodoesnotexist"}, 5875 }, 5876 shouldErr: true, 5877 }, 5878 { 5879 name: "tablet alias filtering", 5880 cells: []string{"zone1"}, 5881 tablets: []*topodatapb.Tablet{ 5882 { 5883 Alias: &topodatapb.TabletAlias{ 5884 Cell: "zone1", 5885 Uid: 100, 5886 }, 5887 Keyspace: "testkeyspace", 5888 Shard: "-", 5889 }, 5890 }, 5891 req: &vtctldatapb.GetTabletsRequest{ 5892 TabletAliases: []*topodatapb.TabletAlias{ 5893 { 5894 Cell: "zone1", 5895 Uid: 100, 5896 }, 5897 { 5898 // This tablet doesn't exist, but doesn't cause a failure. 5899 Cell: "zone404", 5900 Uid: 404, 5901 }, 5902 }, 5903 // The below filters are ignored, because TabletAliases always 5904 // takes precedence. 5905 Keyspace: "another_keyspace", 5906 Shard: "-80", 5907 Cells: []string{"zone404"}, 5908 }, 5909 expected: []*topodatapb.Tablet{ 5910 { 5911 Alias: &topodatapb.TabletAlias{ 5912 Cell: "zone1", 5913 Uid: 100, 5914 }, 5915 Keyspace: "testkeyspace", 5916 Shard: "-", 5917 }, 5918 }, 5919 shouldErr: false, 5920 }, 5921 { 5922 name: "tablet alias filter with none found", 5923 tablets: []*topodatapb.Tablet{}, 5924 req: &vtctldatapb.GetTabletsRequest{ 5925 TabletAliases: []*topodatapb.TabletAlias{ 5926 { 5927 Cell: "zone1", 5928 Uid: 101, 5929 }, 5930 }, 5931 }, 5932 expected: []*topodatapb.Tablet{}, 5933 shouldErr: false, 5934 }, 5935 } 5936 5937 for _, tt := range tests { 5938 tt := tt 5939 5940 t.Run(tt.name, func(t *testing.T) { 5941 t.Parallel() 5942 5943 ctx := context.Background() 5944 ts := memorytopo.NewServer(tt.cells...) 5945 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 5946 return NewVtctldServer(ts) 5947 }) 5948 5949 testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) 5950 5951 resp, err := vtctld.GetTablets(ctx, tt.req) 5952 if tt.shouldErr { 5953 assert.Error(t, err) 5954 return 5955 } 5956 5957 assert.NoError(t, err) 5958 testutil.AssertSameTablets(t, tt.expected, resp.Tablets) 5959 }) 5960 } 5961 } 5962 5963 func TestGetTopologyPath(t *testing.T) { 5964 t.Parallel() 5965 5966 ctx := context.Background() 5967 ts := memorytopo.NewServer("cell1", "cell2", "cell3") 5968 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 5969 return NewVtctldServer(ts) 5970 }) 5971 5972 err := ts.CreateKeyspace(ctx, "keyspace1", &topodatapb.Keyspace{}) 5973 require.NoError(t, err) 5974 5975 testutil.AddTablets(ctx, t, ts, nil, &topodatapb.Tablet{ 5976 Alias: &topodatapb.TabletAlias{Cell: "cell1", Uid: 100}, 5977 Hostname: "localhost", 5978 Keyspace: "keyspace1", 5979 MysqlHostname: "localhost", 5980 MysqlPort: 17100, 5981 }) 5982 require.NoError(t, err) 5983 5984 tests := []struct { 5985 name string 5986 path string 5987 shouldErr bool 5988 expected *vtctldatapb.GetTopologyPathResponse 5989 }{ 5990 { 5991 name: "root path", 5992 path: "/", 5993 expected: &vtctldatapb.GetTopologyPathResponse{ 5994 Cell: &vtctldatapb.TopologyCell{ 5995 Path: "/", 5996 Children: []string{"global", "cell1", "cell2", "cell3"}, 5997 }, 5998 }, 5999 }, 6000 { 6001 name: "invalid path", 6002 path: "", 6003 shouldErr: true, 6004 }, 6005 { 6006 name: "global path", 6007 path: "/global", 6008 expected: &vtctldatapb.GetTopologyPathResponse{ 6009 Cell: &vtctldatapb.TopologyCell{ 6010 Name: "global", 6011 Path: "/global", 6012 Children: []string{"cells", "keyspaces"}, 6013 }, 6014 }, 6015 }, 6016 { 6017 name: "terminal data path", 6018 path: "/cell1/tablets/cell1-0000000100/Tablet", 6019 expected: &vtctldatapb.GetTopologyPathResponse{ 6020 Cell: &vtctldatapb.TopologyCell{ 6021 Name: "Tablet", 6022 Path: "/cell1/tablets/cell1-0000000100/Tablet", 6023 Data: "alias:{cell:\"cell1\" uid:100} hostname:\"localhost\" keyspace:\"keyspace1\" mysql_hostname:\"localhost\" mysql_port:17100", 6024 }, 6025 }, 6026 }, 6027 } 6028 6029 for _, tt := range tests { 6030 tt := tt 6031 6032 t.Run(tt.name, func(t *testing.T) { 6033 t.Parallel() 6034 6035 ctx := context.Background() 6036 resp, err := vtctld.GetTopologyPath(ctx, &vtctldatapb.GetTopologyPathRequest{ 6037 Path: tt.path, 6038 }) 6039 6040 if tt.shouldErr { 6041 assert.Error(t, err) 6042 return 6043 } 6044 6045 assert.NoError(t, err) 6046 utils.MustMatch(t, tt.expected, resp) 6047 }) 6048 } 6049 } 6050 6051 func TestGetVSchema(t *testing.T) { 6052 t.Parallel() 6053 6054 ctx := context.Background() 6055 ts := memorytopo.NewServer("zone1") 6056 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 6057 return NewVtctldServer(ts) 6058 }) 6059 6060 t.Run("found", func(t *testing.T) { 6061 t.Parallel() 6062 6063 err := ts.SaveVSchema(ctx, "testkeyspace", &vschemapb.Keyspace{ 6064 Sharded: true, 6065 Vindexes: map[string]*vschemapb.Vindex{ 6066 "v1": { 6067 Type: "hash", 6068 }, 6069 }, 6070 }) 6071 require.NoError(t, err) 6072 6073 expected := &vtctldatapb.GetVSchemaResponse{ 6074 VSchema: &vschemapb.Keyspace{ 6075 Sharded: true, 6076 Vindexes: map[string]*vschemapb.Vindex{ 6077 "v1": { 6078 Type: "hash", 6079 }, 6080 }, 6081 }, 6082 } 6083 6084 resp, err := vtctld.GetVSchema(ctx, &vtctldatapb.GetVSchemaRequest{ 6085 Keyspace: "testkeyspace", 6086 }) 6087 assert.NoError(t, err) 6088 utils.MustMatch(t, expected, resp) 6089 }) 6090 6091 t.Run("not found", func(t *testing.T) { 6092 t.Parallel() 6093 6094 _, err := vtctld.GetVSchema(ctx, &vtctldatapb.GetVSchemaRequest{ 6095 Keyspace: "doesnotexist", 6096 }) 6097 assert.Error(t, err) 6098 }) 6099 } 6100 6101 func TestPingTablet(t *testing.T) { 6102 t.Parallel() 6103 6104 ctx := context.Background() 6105 ts := memorytopo.NewServer("zone1") 6106 testutil.AddTablet(ctx, t, ts, &topodatapb.Tablet{ 6107 Alias: &topodatapb.TabletAlias{ 6108 Cell: "zone1", 6109 Uid: 100, 6110 }, 6111 Keyspace: "testkeyspace", 6112 Shard: "-", 6113 }, nil) 6114 6115 tests := []struct { 6116 name string 6117 tmc testutil.TabletManagerClient 6118 req *vtctldatapb.PingTabletRequest 6119 expected *vtctldatapb.PingTabletResponse 6120 shouldErr bool 6121 }{ 6122 { 6123 name: "ok", 6124 tmc: testutil.TabletManagerClient{ 6125 PingResults: map[string]error{ 6126 "zone1-0000000100": nil, 6127 }, 6128 }, 6129 req: &vtctldatapb.PingTabletRequest{ 6130 TabletAlias: &topodatapb.TabletAlias{ 6131 Cell: "zone1", 6132 Uid: 100, 6133 }, 6134 }, 6135 expected: &vtctldatapb.PingTabletResponse{}, 6136 }, 6137 { 6138 name: "tablet not found", 6139 tmc: testutil.TabletManagerClient{ 6140 PingResults: map[string]error{ 6141 "zone1-0000000100": nil, 6142 }, 6143 }, 6144 req: &vtctldatapb.PingTabletRequest{ 6145 TabletAlias: &topodatapb.TabletAlias{ 6146 Cell: "zone2", 6147 Uid: 404, 6148 }, 6149 }, 6150 shouldErr: true, 6151 }, 6152 { 6153 name: "ping rpc error", 6154 tmc: testutil.TabletManagerClient{ 6155 PingResults: map[string]error{ 6156 "zone1-0000000100": assert.AnError, 6157 }, 6158 }, 6159 req: &vtctldatapb.PingTabletRequest{ 6160 TabletAlias: &topodatapb.TabletAlias{ 6161 Cell: "zone1", 6162 Uid: 100, 6163 }, 6164 }, 6165 shouldErr: true, 6166 }, 6167 } 6168 6169 for _, tt := range tests { 6170 tt := tt 6171 t.Run(tt.name, func(t *testing.T) { 6172 t.Parallel() 6173 6174 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 6175 return NewVtctldServer(ts) 6176 }) 6177 6178 resp, err := vtctld.PingTablet(ctx, tt.req) 6179 if tt.shouldErr { 6180 assert.Error(t, err) 6181 assert.Nil(t, resp) 6182 return 6183 } 6184 6185 require.NoError(t, err) 6186 assert.Equal(t, tt.expected, resp) 6187 }) 6188 } 6189 } 6190 6191 func TestPlannedReparentShard(t *testing.T) { 6192 t.Parallel() 6193 6194 tests := []struct { 6195 name string 6196 ts *topo.Server 6197 tmc tmclient.TabletManagerClient 6198 tablets []*topodatapb.Tablet 6199 6200 req *vtctldatapb.PlannedReparentShardRequest 6201 expected *vtctldatapb.PlannedReparentShardResponse 6202 expectEventsToOccur bool 6203 shouldErr bool 6204 }{ 6205 { 6206 name: "successful reparent", 6207 ts: memorytopo.NewServer("zone1"), 6208 tablets: []*topodatapb.Tablet{ 6209 { 6210 Alias: &topodatapb.TabletAlias{ 6211 Cell: "zone1", 6212 Uid: 100, 6213 }, 6214 Type: topodatapb.TabletType_PRIMARY, 6215 PrimaryTermStartTime: &vttime.Time{ 6216 Seconds: 100, 6217 }, 6218 Keyspace: "testkeyspace", 6219 Shard: "-", 6220 }, 6221 { 6222 Alias: &topodatapb.TabletAlias{ 6223 Cell: "zone1", 6224 Uid: 200, 6225 }, 6226 Type: topodatapb.TabletType_REPLICA, 6227 Keyspace: "testkeyspace", 6228 Shard: "-", 6229 }, 6230 { 6231 Alias: &topodatapb.TabletAlias{ 6232 Cell: "zone1", 6233 Uid: 101, 6234 }, 6235 Type: topodatapb.TabletType_RDONLY, 6236 Keyspace: "testkeyspace", 6237 Shard: "-", 6238 }, 6239 }, 6240 tmc: &testutil.TabletManagerClient{ 6241 DemotePrimaryResults: map[string]struct { 6242 Status *replicationdatapb.PrimaryStatus 6243 Error error 6244 }{ 6245 "zone1-0000000100": { 6246 Status: &replicationdatapb.PrimaryStatus{ 6247 Position: "primary-demotion position", 6248 }, 6249 Error: nil, 6250 }, 6251 }, 6252 PrimaryPositionResults: map[string]struct { 6253 Position string 6254 Error error 6255 }{ 6256 "zone1-0000000100": { 6257 Position: "doesn't matter", 6258 Error: nil, 6259 }, 6260 }, 6261 PopulateReparentJournalResults: map[string]error{ 6262 "zone1-0000000200": nil, 6263 }, 6264 PromoteReplicaResults: map[string]struct { 6265 Result string 6266 Error error 6267 }{ 6268 "zone1-0000000200": { 6269 Result: "promotion position", 6270 Error: nil, 6271 }, 6272 }, 6273 SetReplicationSourceResults: map[string]error{ 6274 "zone1-0000000200": nil, // waiting for primary-position during promotion 6275 // reparent SetReplicationSource calls 6276 "zone1-0000000100": nil, 6277 "zone1-0000000101": nil, 6278 }, 6279 WaitForPositionResults: map[string]map[string]error{ 6280 "zone1-0000000200": { 6281 "primary-demotion position": nil, 6282 }, 6283 }, 6284 }, 6285 req: &vtctldatapb.PlannedReparentShardRequest{ 6286 Keyspace: "testkeyspace", 6287 Shard: "-", 6288 NewPrimary: &topodatapb.TabletAlias{ 6289 Cell: "zone1", 6290 Uid: 200, 6291 }, 6292 WaitReplicasTimeout: protoutil.DurationToProto(time.Millisecond * 10), 6293 }, 6294 expected: &vtctldatapb.PlannedReparentShardResponse{ 6295 Keyspace: "testkeyspace", 6296 Shard: "-", 6297 PromotedPrimary: &topodatapb.TabletAlias{ 6298 Cell: "zone1", 6299 Uid: 200, 6300 }, 6301 }, 6302 expectEventsToOccur: true, 6303 shouldErr: false, 6304 }, 6305 { 6306 // Note: this is testing the error-handling done in 6307 // (*VtctldServer).PlannedReparentShard, not the logic of an PRS. 6308 // That logic is tested in reparentutil, and not here. Therefore, 6309 // the simplest way to trigger a failure is to attempt an PRS on a 6310 // shard that does not exist. 6311 name: "failed reparent", 6312 ts: memorytopo.NewServer("zone1"), 6313 tablets: nil, 6314 req: &vtctldatapb.PlannedReparentShardRequest{ 6315 Keyspace: "testkeyspace", 6316 Shard: "-", 6317 }, 6318 expectEventsToOccur: false, 6319 shouldErr: true, 6320 }, 6321 { 6322 name: "invalid WaitReplicasTimeout", 6323 req: &vtctldatapb.PlannedReparentShardRequest{ 6324 WaitReplicasTimeout: &vttime.Duration{ 6325 Seconds: -1, 6326 Nanos: 1, 6327 }, 6328 }, 6329 shouldErr: true, 6330 }, 6331 } 6332 6333 ctx := context.Background() 6334 6335 for _, tt := range tests { 6336 tt := tt 6337 6338 t.Run(tt.name, func(t *testing.T) { 6339 t.Parallel() 6340 6341 testutil.AddTablets(ctx, t, tt.ts, &testutil.AddTabletOptions{ 6342 AlsoSetShardPrimary: true, 6343 ForceSetShardPrimary: true, 6344 SkipShardCreation: false, 6345 }, tt.tablets...) 6346 6347 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 6348 return NewVtctldServer(ts) 6349 }) 6350 resp, err := vtctld.PlannedReparentShard(ctx, tt.req) 6351 6352 // We defer this because we want to check in both error and non- 6353 // error cases, but after the main set of assertions for those 6354 // cases. 6355 defer func() { 6356 if !tt.expectEventsToOccur { 6357 testutil.AssertNoLogutilEventsOccurred(t, resp, "expected no events to occur during ERS") 6358 6359 return 6360 } 6361 6362 testutil.AssertLogutilEventsOccurred(t, resp, "expected events to occur during ERS") 6363 }() 6364 6365 if tt.shouldErr { 6366 assert.Error(t, err) 6367 6368 return 6369 } 6370 6371 assert.NoError(t, err) 6372 testutil.AssertPlannedReparentShardResponsesEqual(t, tt.expected, resp) 6373 }) 6374 } 6375 } 6376 6377 func TestRebuildKeyspaceGraph(t *testing.T) { 6378 t.Parallel() 6379 6380 t.Run("ok", func(t *testing.T) { 6381 t.Parallel() 6382 6383 ctx := context.Background() 6384 ts := memorytopo.NewServer("zone1") 6385 testutil.AddKeyspace(ctx, t, ts, &vtctldatapb.Keyspace{ 6386 Name: "testkeyspace", 6387 }) 6388 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 6389 return NewVtctldServer(ts) 6390 }) 6391 6392 _, err := vtctld.RebuildKeyspaceGraph(ctx, &vtctldatapb.RebuildKeyspaceGraphRequest{ 6393 Keyspace: "testkeyspace", 6394 }) 6395 assert.NoError(t, err) 6396 }) 6397 6398 t.Run("no such keyspace", func(t *testing.T) { 6399 t.Parallel() 6400 6401 ts := memorytopo.NewServer("zone1") 6402 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 6403 return NewVtctldServer(ts) 6404 }) 6405 6406 _, err := vtctld.RebuildKeyspaceGraph(context.Background(), &vtctldatapb.RebuildKeyspaceGraphRequest{ 6407 Keyspace: "testkeyspace", 6408 }) 6409 assert.Error(t, err) 6410 }) 6411 6412 t.Run("topo unavailable", func(t *testing.T) { 6413 t.Parallel() 6414 6415 ctx := context.Background() 6416 ts, factory := memorytopo.NewServerAndFactory("zone1") 6417 testutil.AddKeyspace(ctx, t, ts, &vtctldatapb.Keyspace{ 6418 Name: "testkeyspace", 6419 }) 6420 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 6421 return NewVtctldServer(ts) 6422 }) 6423 factory.SetError(assert.AnError) 6424 6425 _, err := vtctld.RebuildKeyspaceGraph(ctx, &vtctldatapb.RebuildKeyspaceGraphRequest{ 6426 Keyspace: "testkeyspace", 6427 }) 6428 assert.Error(t, err) 6429 }) 6430 6431 t.Run("lock error", func(t *testing.T) { 6432 t.Parallel() 6433 6434 ctx := context.Background() 6435 ts := memorytopo.NewServer("zone1") 6436 testutil.AddKeyspace(ctx, t, ts, &vtctldatapb.Keyspace{ 6437 Name: "testkeyspace", 6438 }) 6439 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 6440 return NewVtctldServer(ts) 6441 }) 6442 6443 _, unlock, lerr := ts.LockKeyspace(context.Background(), "testkeyspace", "test lock") 6444 require.NoError(t, lerr, "could not lock keyspace for testing") 6445 6446 defer unlock(&lerr) 6447 defer func() { require.NoError(t, lerr, "could not unlock testkeyspace after test") }() 6448 6449 ctx, cancel := context.WithTimeout(ctx, time.Millisecond*50) 6450 defer cancel() 6451 _, err := vtctld.RebuildKeyspaceGraph(ctx, &vtctldatapb.RebuildKeyspaceGraphRequest{ 6452 Keyspace: "testkeyspace", 6453 }) 6454 assert.Error(t, err) 6455 }) 6456 } 6457 6458 func TestRebuildVSchemaGraph(t *testing.T) { 6459 t.Parallel() 6460 6461 ctx := context.Background() 6462 req := &vtctldatapb.RebuildVSchemaGraphRequest{} 6463 tests := []struct { 6464 name string 6465 topoDown bool 6466 shouldErr bool 6467 }{ 6468 { 6469 name: "success", 6470 }, 6471 { 6472 name: "topo down", 6473 topoDown: true, 6474 shouldErr: true, 6475 }, 6476 } 6477 6478 for _, tt := range tests { 6479 tt := tt 6480 t.Run(tt.name, func(t *testing.T) { 6481 t.Parallel() 6482 6483 ts, factory := memorytopo.NewServerAndFactory("zone1") 6484 if tt.topoDown { 6485 factory.SetError(errors.New("topo down for testing")) 6486 } 6487 6488 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 6489 return NewVtctldServer(ts) 6490 }) 6491 _, err := vtctld.RebuildVSchemaGraph(ctx, req) 6492 if tt.shouldErr { 6493 assert.Error(t, err) 6494 return 6495 } 6496 6497 assert.NoError(t, err) 6498 }) 6499 } 6500 } 6501 6502 func TestRefreshState(t *testing.T) { 6503 t.Parallel() 6504 6505 ctx := context.Background() 6506 tests := []struct { 6507 name string 6508 ts *topo.Server 6509 tablet *topodatapb.Tablet 6510 refreshStateError error 6511 req *vtctldatapb.RefreshStateRequest 6512 shouldErr bool 6513 }{ 6514 { 6515 name: "success", 6516 ts: memorytopo.NewServer("zone1"), 6517 tablet: &topodatapb.Tablet{ 6518 Alias: &topodatapb.TabletAlias{ 6519 Cell: "zone1", 6520 Uid: 100, 6521 }, 6522 }, 6523 refreshStateError: nil, 6524 req: &vtctldatapb.RefreshStateRequest{ 6525 TabletAlias: &topodatapb.TabletAlias{ 6526 Cell: "zone1", 6527 Uid: 100, 6528 }, 6529 }, 6530 }, 6531 { 6532 name: "tablet alias nil", 6533 ts: memorytopo.NewServer(), 6534 req: &vtctldatapb.RefreshStateRequest{}, 6535 shouldErr: true, 6536 }, 6537 { 6538 name: "tablet not found", 6539 ts: memorytopo.NewServer("zone1"), 6540 tablet: &topodatapb.Tablet{ 6541 Alias: &topodatapb.TabletAlias{ 6542 Cell: "zone1", 6543 Uid: 100, 6544 }, 6545 }, 6546 refreshStateError: nil, 6547 req: &vtctldatapb.RefreshStateRequest{ 6548 TabletAlias: &topodatapb.TabletAlias{ 6549 Cell: "zone1", 6550 Uid: 400, 6551 }, 6552 }, 6553 shouldErr: true, 6554 }, 6555 { 6556 name: "RefreshState failed", 6557 ts: memorytopo.NewServer("zone1"), 6558 tablet: &topodatapb.Tablet{ 6559 Alias: &topodatapb.TabletAlias{ 6560 Cell: "zone1", 6561 Uid: 100, 6562 }, 6563 }, 6564 refreshStateError: fmt.Errorf("%w: RefreshState failed", assert.AnError), 6565 req: &vtctldatapb.RefreshStateRequest{ 6566 TabletAlias: &topodatapb.TabletAlias{ 6567 Cell: "zone1", 6568 Uid: 100, 6569 }, 6570 }, 6571 shouldErr: true, 6572 }, 6573 } 6574 6575 for _, tt := range tests { 6576 tt := tt 6577 6578 t.Run(tt.name, func(t *testing.T) { 6579 t.Parallel() 6580 6581 var tmc testutil.TabletManagerClient 6582 if tt.tablet != nil { 6583 testutil.AddTablet(ctx, t, tt.ts, tt.tablet, nil) 6584 tmc.RefreshStateResults = map[string]error{ 6585 topoproto.TabletAliasString(tt.tablet.Alias): tt.refreshStateError, 6586 } 6587 } 6588 6589 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 6590 return NewVtctldServer(ts) 6591 }) 6592 _, err := vtctld.RefreshState(ctx, tt.req) 6593 if tt.shouldErr { 6594 assert.Error(t, err) 6595 return 6596 } 6597 6598 assert.NoError(t, err) 6599 }) 6600 } 6601 } 6602 6603 func TestRefreshStateByShard(t *testing.T) { 6604 t.Parallel() 6605 6606 ctx := context.Background() 6607 tests := []struct { 6608 name string 6609 ts *topo.Server 6610 tablets []*topodatapb.Tablet 6611 refreshStateErrors []error // must have len(tablets) 6612 req *vtctldatapb.RefreshStateByShardRequest 6613 expected *vtctldatapb.RefreshStateByShardResponse 6614 shouldErr bool 6615 }{ 6616 { 6617 name: "success", 6618 ts: memorytopo.NewServer("zone1", "zone2"), 6619 tablets: []*topodatapb.Tablet{ 6620 { 6621 Hostname: "zone1-100", 6622 Alias: &topodatapb.TabletAlias{ 6623 Cell: "zone1", 6624 Uid: 100, 6625 }, 6626 Keyspace: "ks", 6627 Shard: "-", 6628 }, 6629 { 6630 Hostname: "zone2-100", 6631 Alias: &topodatapb.TabletAlias{ 6632 Cell: "zone2", 6633 Uid: 100, 6634 }, 6635 Keyspace: "ks", 6636 Shard: "-", 6637 }, 6638 }, 6639 refreshStateErrors: []error{ 6640 nil, // zone1-100 6641 nil, // zone2-100 6642 }, 6643 req: &vtctldatapb.RefreshStateByShardRequest{ 6644 Keyspace: "ks", 6645 Shard: "-", 6646 }, 6647 expected: &vtctldatapb.RefreshStateByShardResponse{}, 6648 }, 6649 { 6650 name: "cell filtering", 6651 ts: memorytopo.NewServer("zone1", "zone2"), 6652 tablets: []*topodatapb.Tablet{ 6653 { 6654 Hostname: "zone1-100", 6655 Alias: &topodatapb.TabletAlias{ 6656 Cell: "zone1", 6657 Uid: 100, 6658 }, 6659 Keyspace: "ks", 6660 Shard: "-", 6661 }, 6662 { 6663 Hostname: "zone2-100", 6664 Alias: &topodatapb.TabletAlias{ 6665 Cell: "zone2", 6666 Uid: 100, 6667 }, 6668 Keyspace: "ks", 6669 Shard: "-", 6670 }, 6671 }, 6672 refreshStateErrors: []error{ 6673 nil, 6674 fmt.Errorf("%w: RefreshState failed on zone2-100", assert.AnError), 6675 }, 6676 req: &vtctldatapb.RefreshStateByShardRequest{ 6677 Keyspace: "ks", 6678 Shard: "-", 6679 Cells: []string{"zone1"}, // If we didn't filter, we would get IsPartialRefresh=true because of the failure in zone2. 6680 }, 6681 expected: &vtctldatapb.RefreshStateByShardResponse{ 6682 IsPartialRefresh: false, 6683 PartialRefreshDetails: "", 6684 }, 6685 shouldErr: false, 6686 }, 6687 { 6688 name: "partial result", 6689 ts: memorytopo.NewServer("zone1", "zone2"), 6690 tablets: []*topodatapb.Tablet{ 6691 { 6692 Hostname: "zone1-100", 6693 Alias: &topodatapb.TabletAlias{ 6694 Cell: "zone1", 6695 Uid: 100, 6696 }, 6697 Keyspace: "ks", 6698 Shard: "-", 6699 }, 6700 { 6701 Hostname: "zone2-100", 6702 Alias: &topodatapb.TabletAlias{ 6703 Cell: "zone2", 6704 Uid: 100, 6705 }, 6706 Keyspace: "ks", 6707 Shard: "-", 6708 }, 6709 }, 6710 refreshStateErrors: []error{ 6711 nil, 6712 fmt.Errorf("%w: RefreshState failed on zone2-100", assert.AnError), 6713 }, 6714 req: &vtctldatapb.RefreshStateByShardRequest{ 6715 Keyspace: "ks", 6716 Shard: "-", 6717 }, 6718 expected: &vtctldatapb.RefreshStateByShardResponse{ 6719 IsPartialRefresh: true, 6720 PartialRefreshDetails: "failed to refresh tablet zone2-0000000100: assert.AnError general error for testing: RefreshState failed on zone2-100", 6721 }, 6722 shouldErr: false, 6723 }, 6724 { 6725 name: "missing keyspace argument", 6726 ts: memorytopo.NewServer(), 6727 req: &vtctldatapb.RefreshStateByShardRequest{}, 6728 shouldErr: true, 6729 }, 6730 { 6731 name: "missing shard argument", 6732 ts: memorytopo.NewServer(), 6733 req: &vtctldatapb.RefreshStateByShardRequest{ 6734 Keyspace: "ks", 6735 }, 6736 shouldErr: true, 6737 }, 6738 { 6739 name: "shard not found", 6740 ts: memorytopo.NewServer("zone1"), 6741 tablets: []*topodatapb.Tablet{ 6742 { 6743 Alias: &topodatapb.TabletAlias{ 6744 Cell: "zone1", 6745 Uid: 100, 6746 }, 6747 Keyspace: "ks", 6748 Shard: "-80", 6749 }, 6750 }, 6751 refreshStateErrors: []error{nil}, 6752 req: &vtctldatapb.RefreshStateByShardRequest{ 6753 Keyspace: "ks2", 6754 Shard: "-", 6755 }, 6756 shouldErr: true, 6757 }, 6758 } 6759 6760 for _, tt := range tests { 6761 tt := tt 6762 6763 t.Run(tt.name, func(t *testing.T) { 6764 t.Parallel() 6765 6766 require.Equal(t, len(tt.tablets), len(tt.refreshStateErrors), "Invalid test case: must have one refreshStateError for each tablet") 6767 6768 tmc := &testutil.TabletManagerClient{ 6769 RefreshStateResults: make(map[string]error, len(tt.tablets)), 6770 } 6771 testutil.AddTablets(ctx, t, tt.ts, nil, tt.tablets...) 6772 for i, tablet := range tt.tablets { 6773 key := topoproto.TabletAliasString(tablet.Alias) 6774 tmc.RefreshStateResults[key] = tt.refreshStateErrors[i] 6775 } 6776 6777 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 6778 return NewVtctldServer(ts) 6779 }) 6780 resp, err := vtctld.RefreshStateByShard(ctx, tt.req) 6781 if tt.shouldErr { 6782 assert.Error(t, err) 6783 return 6784 } 6785 6786 require.NoError(t, err) 6787 utils.MustMatch(t, tt.expected, resp) 6788 }) 6789 } 6790 } 6791 6792 func TestReloadSchema(t *testing.T) { 6793 t.Parallel() 6794 6795 tests := []struct { 6796 name string 6797 tablets []*topodatapb.Tablet 6798 tmc testutil.TabletManagerClient 6799 req *vtctldatapb.ReloadSchemaRequest 6800 shouldErr bool 6801 }{ 6802 { 6803 name: "ok", 6804 tablets: []*topodatapb.Tablet{ 6805 { 6806 Alias: &topodatapb.TabletAlias{ 6807 Cell: "zone1", 6808 Uid: 100, 6809 }, 6810 }, 6811 }, 6812 tmc: testutil.TabletManagerClient{ 6813 ReloadSchemaResults: map[string]error{ 6814 "zone1-0000000100": nil, 6815 }, 6816 }, 6817 req: &vtctldatapb.ReloadSchemaRequest{ 6818 TabletAlias: &topodatapb.TabletAlias{ 6819 Cell: "zone1", 6820 Uid: 100, 6821 }, 6822 }, 6823 }, 6824 { 6825 name: "tablet not found", 6826 tablets: []*topodatapb.Tablet{ 6827 { 6828 Alias: &topodatapb.TabletAlias{ 6829 Cell: "zone1", 6830 Uid: 100, 6831 }, 6832 }, 6833 }, 6834 tmc: testutil.TabletManagerClient{ 6835 ReloadSchemaResults: map[string]error{ 6836 "zone1-0000000100": nil, 6837 }, 6838 }, 6839 req: &vtctldatapb.ReloadSchemaRequest{ 6840 TabletAlias: &topodatapb.TabletAlias{ 6841 Cell: "zone1", 6842 Uid: 404, 6843 }, 6844 }, 6845 shouldErr: true, 6846 }, 6847 { 6848 name: "tmc failure", 6849 tablets: []*topodatapb.Tablet{ 6850 { 6851 Alias: &topodatapb.TabletAlias{ 6852 Cell: "zone1", 6853 Uid: 100, 6854 }, 6855 }, 6856 }, 6857 tmc: testutil.TabletManagerClient{ 6858 ReloadSchemaResults: map[string]error{ 6859 "zone1-0000000100": assert.AnError, 6860 }, 6861 }, 6862 req: &vtctldatapb.ReloadSchemaRequest{ 6863 TabletAlias: &topodatapb.TabletAlias{ 6864 Cell: "zone1", 6865 Uid: 100, 6866 }, 6867 }, 6868 shouldErr: true, 6869 }, 6870 } 6871 6872 ctx := context.Background() 6873 for _, tt := range tests { 6874 tt := tt 6875 t.Run(tt.name, func(t *testing.T) { 6876 ts := memorytopo.NewServer("zone1") 6877 testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) 6878 6879 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 6880 return NewVtctldServer(ts) 6881 }) 6882 _, err := vtctld.ReloadSchema(ctx, tt.req) 6883 if tt.shouldErr { 6884 assert.Error(t, err) 6885 return 6886 } 6887 6888 require.NoError(t, err) 6889 }) 6890 } 6891 } 6892 6893 func TestReloadSchemaKeyspace(t *testing.T) { 6894 t.Parallel() 6895 6896 tests := []struct { 6897 name string 6898 tablets []*topodatapb.Tablet 6899 tmc testutil.TabletManagerClient 6900 req *vtctldatapb.ReloadSchemaKeyspaceRequest 6901 expected *vtctldatapb.ReloadSchemaKeyspaceResponse 6902 shouldErr bool 6903 }{ 6904 { 6905 name: "ok", 6906 tablets: []*topodatapb.Tablet{ 6907 { 6908 Keyspace: "ks1", 6909 Shard: "-80", 6910 Alias: &topodatapb.TabletAlias{ 6911 Cell: "zone1", 6912 Uid: 100, 6913 }, 6914 Type: topodatapb.TabletType_PRIMARY, 6915 }, 6916 { 6917 Keyspace: "ks1", 6918 Shard: "-80", 6919 Alias: &topodatapb.TabletAlias{ 6920 Cell: "zone2", 6921 Uid: 200, 6922 }, 6923 Type: topodatapb.TabletType_REPLICA, 6924 }, 6925 { 6926 Keyspace: "ks1", 6927 Shard: "80-", 6928 Alias: &topodatapb.TabletAlias{ 6929 Cell: "zone1", 6930 Uid: 101, 6931 }, 6932 Type: topodatapb.TabletType_PRIMARY, 6933 }, 6934 { 6935 Keyspace: "ks1", 6936 Shard: "80-", 6937 Alias: &topodatapb.TabletAlias{ 6938 Cell: "zone2", 6939 Uid: 201, 6940 }, 6941 Type: topodatapb.TabletType_REPLICA, 6942 }, 6943 }, 6944 tmc: testutil.TabletManagerClient{ 6945 ReloadSchemaResults: map[string]error{ 6946 "zone2-0000000200": nil, 6947 "zone2-0000000201": nil, 6948 }, 6949 }, 6950 req: &vtctldatapb.ReloadSchemaKeyspaceRequest{ 6951 Keyspace: "ks1", 6952 }, 6953 expected: &vtctldatapb.ReloadSchemaKeyspaceResponse{}, 6954 }, 6955 { 6956 name: "keyspace not found", 6957 req: &vtctldatapb.ReloadSchemaKeyspaceRequest{ 6958 Keyspace: "ks1", 6959 }, 6960 shouldErr: true, 6961 }, 6962 } 6963 6964 ctx := context.Background() 6965 for _, tt := range tests { 6966 tt := tt 6967 t.Run(tt.name, func(t *testing.T) { 6968 t.Parallel() 6969 6970 ts := memorytopo.NewServer("zone1", "zone2", "zone3") 6971 testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ 6972 AlsoSetShardPrimary: true, 6973 }, tt.tablets...) 6974 6975 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 6976 return NewVtctldServer(ts) 6977 }) 6978 resp, err := vtctld.ReloadSchemaKeyspace(ctx, tt.req) 6979 if tt.shouldErr { 6980 assert.Error(t, err) 6981 return 6982 } 6983 6984 require.NoError(t, err) 6985 6986 // ReloadSchemaKeyspace does each shard concurrently, so we sort 6987 // here to reduce flakes. 6988 sort.Sort(testutil.EventValueSorter(resp.Events)) 6989 sort.Sort(testutil.EventValueSorter(tt.expected.Events)) 6990 6991 testutil.AssertLogutilEventsMatch(t, tt.expected.Events, resp.Events) 6992 }) 6993 } 6994 } 6995 6996 func TestReloadSchemaShard(t *testing.T) { 6997 t.Parallel() 6998 6999 tests := []struct { 7000 name string 7001 tablets []*topodatapb.Tablet 7002 tmc testutil.TabletManagerClient 7003 req *vtctldatapb.ReloadSchemaShardRequest 7004 expected *vtctldatapb.ReloadSchemaShardResponse 7005 shouldErr bool 7006 }{ 7007 { 7008 name: "ok", 7009 tablets: []*topodatapb.Tablet{ 7010 { 7011 Keyspace: "ks1", 7012 Shard: "-", 7013 Type: topodatapb.TabletType_PRIMARY, 7014 Alias: &topodatapb.TabletAlias{ 7015 Cell: "zone1", 7016 Uid: 100, 7017 }, 7018 }, 7019 { 7020 Keyspace: "ks1", 7021 Shard: "-", 7022 Type: topodatapb.TabletType_REPLICA, 7023 Alias: &topodatapb.TabletAlias{ 7024 Cell: "zone2", 7025 Uid: 200, 7026 }, 7027 }, 7028 { 7029 Keyspace: "ks1", 7030 Shard: "-", 7031 Type: topodatapb.TabletType_REPLICA, 7032 Alias: &topodatapb.TabletAlias{ 7033 Cell: "zone3", 7034 Uid: 300, 7035 }, 7036 }, 7037 }, 7038 tmc: testutil.TabletManagerClient{ 7039 ReloadSchemaResults: map[string]error{ 7040 "zone1-0000000100": assert.AnError, // without IncludePrimary this is fine. 7041 "zone2-0000000200": nil, 7042 "zone3-0000000300": nil, 7043 }, 7044 }, 7045 req: &vtctldatapb.ReloadSchemaShardRequest{ 7046 Keyspace: "ks1", 7047 Shard: "-", 7048 }, 7049 expected: &vtctldatapb.ReloadSchemaShardResponse{}, 7050 shouldErr: false, 7051 }, 7052 { 7053 name: "shard not found", 7054 req: &vtctldatapb.ReloadSchemaShardRequest{ 7055 Keyspace: "ks1", 7056 Shard: "-", 7057 }, 7058 expected: &vtctldatapb.ReloadSchemaShardResponse{ 7059 Events: []*logutilpb.Event{ 7060 { 7061 Value: `ReloadSchemaShard\(ks1/-\) failed to load tablet list, will not reload schema \(use vtctl ReloadSchemaShard to try again\):.*$`, 7062 }, 7063 }, 7064 }, 7065 }, 7066 { 7067 name: "include primary, with failure", 7068 tablets: []*topodatapb.Tablet{ 7069 { 7070 Keyspace: "ks1", 7071 Shard: "-", 7072 Type: topodatapb.TabletType_PRIMARY, 7073 Alias: &topodatapb.TabletAlias{ 7074 Cell: "zone1", 7075 Uid: 100, 7076 }, 7077 }, 7078 { 7079 Keyspace: "ks1", 7080 Shard: "-", 7081 Type: topodatapb.TabletType_REPLICA, 7082 Alias: &topodatapb.TabletAlias{ 7083 Cell: "zone2", 7084 Uid: 200, 7085 }, 7086 }, 7087 { 7088 Keyspace: "ks1", 7089 Shard: "-", 7090 Type: topodatapb.TabletType_REPLICA, 7091 Alias: &topodatapb.TabletAlias{ 7092 Cell: "zone3", 7093 Uid: 300, 7094 }, 7095 }, 7096 }, 7097 tmc: testutil.TabletManagerClient{ 7098 ReloadSchemaResults: map[string]error{ 7099 "zone1-0000000100": assert.AnError, // with IncludePrimary this triggers an event 7100 "zone2-0000000200": nil, 7101 "zone3-0000000300": nil, 7102 }, 7103 }, 7104 req: &vtctldatapb.ReloadSchemaShardRequest{ 7105 Keyspace: "ks1", 7106 Shard: "-", 7107 IncludePrimary: true, 7108 }, 7109 expected: &vtctldatapb.ReloadSchemaShardResponse{ 7110 Events: []*logutilpb.Event{ 7111 { 7112 Value: "Failed to reload schema on replica tablet zone1-0000000100 in ks1/-.*$", 7113 }, 7114 }, 7115 }, 7116 shouldErr: false, 7117 }, 7118 } 7119 7120 ctx := context.Background() 7121 for _, tt := range tests { 7122 tt := tt 7123 t.Run(tt.name, func(t *testing.T) { 7124 t.Parallel() 7125 7126 ts := memorytopo.NewServer("zone1", "zone2", "zone3") 7127 testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ 7128 AlsoSetShardPrimary: true, 7129 }, tt.tablets...) 7130 7131 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 7132 return NewVtctldServer(ts) 7133 }) 7134 resp, err := vtctld.ReloadSchemaShard(ctx, tt.req) 7135 if tt.shouldErr { 7136 assert.Error(t, err) 7137 return 7138 } 7139 7140 require.NoError(t, err) 7141 testutil.AssertLogutilEventsMatch(t, tt.expected.Events, resp.Events) 7142 }) 7143 } 7144 } 7145 7146 func TestRemoveBackup(t *testing.T) { 7147 ctx := context.Background() 7148 ts := memorytopo.NewServer() 7149 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 7150 return NewVtctldServer(ts) 7151 }) 7152 7153 setup := func() { 7154 testutil.BackupStorage.Backups = map[string][]string{ 7155 "testkeyspace/-": {"backup1", "backup2", "backup3"}, 7156 } 7157 } 7158 7159 t.Run("ok", func(t *testing.T) { 7160 setup() 7161 _, err := vtctld.RemoveBackup(ctx, &vtctldatapb.RemoveBackupRequest{ 7162 Keyspace: "testkeyspace", 7163 Shard: "-", 7164 Name: "backup2", 7165 }) 7166 7167 assert.NoError(t, err) 7168 7169 resp, err := vtctld.GetBackups(ctx, &vtctldatapb.GetBackupsRequest{ 7170 Keyspace: "testkeyspace", 7171 Shard: "-", 7172 }) 7173 require.NoError(t, err) 7174 7175 var backupNames []string 7176 for _, bi := range resp.Backups { 7177 backupNames = append(backupNames, bi.Name) 7178 } 7179 utils.MustMatch(t, []string{"backup1", "backup3"}, backupNames, "expected \"backup2\" to be removed") 7180 }) 7181 7182 t.Run("no bucket found", func(t *testing.T) { 7183 setup() 7184 _, err := vtctld.RemoveBackup(ctx, &vtctldatapb.RemoveBackupRequest{ 7185 Keyspace: "notfound", 7186 Shard: "-", 7187 Name: "somebackup", 7188 }) 7189 assert.Error(t, err) 7190 }) 7191 7192 t.Run("no backup found in bucket", func(t *testing.T) { 7193 setup() 7194 _, err := vtctld.RemoveBackup(ctx, &vtctldatapb.RemoveBackupRequest{ 7195 Keyspace: "testkeyspace", 7196 Shard: "-", 7197 Name: "notfound", 7198 }) 7199 assert.Error(t, err) 7200 }) 7201 } 7202 7203 func TestRemoveKeyspaceCell(t *testing.T) { 7204 t.Parallel() 7205 7206 tests := []struct { 7207 name string 7208 keyspace *vtctldatapb.Keyspace 7209 shards []*vtctldatapb.Shard 7210 topoError error 7211 topoIsLocked bool 7212 srvKeyspaceDoesNotExist bool 7213 req *vtctldatapb.RemoveKeyspaceCellRequest 7214 expected *vtctldatapb.RemoveKeyspaceCellResponse 7215 shouldErr bool 7216 }{ 7217 { 7218 name: "success", 7219 keyspace: nil, 7220 shards: []*vtctldatapb.Shard{ 7221 { 7222 Keyspace: "testkeyspace", 7223 Name: "-", 7224 }, 7225 }, 7226 topoError: nil, 7227 topoIsLocked: false, 7228 srvKeyspaceDoesNotExist: false, 7229 req: &vtctldatapb.RemoveKeyspaceCellRequest{ 7230 Keyspace: "testkeyspace", 7231 Cell: "zone1", 7232 }, 7233 expected: &vtctldatapb.RemoveKeyspaceCellResponse{}, 7234 shouldErr: false, 7235 }, 7236 { 7237 name: "success/empty keyspace", 7238 keyspace: &vtctldatapb.Keyspace{ 7239 Name: "testkeyspace", 7240 Keyspace: &topodatapb.Keyspace{}, 7241 }, 7242 shards: nil, 7243 topoError: nil, 7244 topoIsLocked: false, 7245 srvKeyspaceDoesNotExist: false, 7246 req: &vtctldatapb.RemoveKeyspaceCellRequest{ 7247 Keyspace: "testkeyspace", 7248 Cell: "zone1", 7249 }, 7250 expected: &vtctldatapb.RemoveKeyspaceCellResponse{}, 7251 shouldErr: false, 7252 }, 7253 { 7254 name: "keyspace not found", 7255 keyspace: &vtctldatapb.Keyspace{ 7256 Name: "otherkeyspace", 7257 Keyspace: &topodatapb.Keyspace{}, 7258 }, 7259 shards: nil, 7260 topoError: nil, 7261 topoIsLocked: false, 7262 srvKeyspaceDoesNotExist: false, 7263 req: &vtctldatapb.RemoveKeyspaceCellRequest{ 7264 Keyspace: "testkeyspace", 7265 Cell: "zone1", 7266 }, 7267 expected: nil, 7268 shouldErr: true, 7269 }, 7270 { 7271 name: "topo is down", 7272 keyspace: nil, 7273 shards: []*vtctldatapb.Shard{ 7274 { 7275 Keyspace: "testkeyspace", 7276 Name: "-", 7277 }, 7278 }, 7279 topoError: assert.AnError, 7280 topoIsLocked: false, 7281 srvKeyspaceDoesNotExist: false, 7282 req: &vtctldatapb.RemoveKeyspaceCellRequest{ 7283 Keyspace: "testkeyspace", 7284 Cell: "zone1", 7285 }, 7286 expected: nil, 7287 shouldErr: true, 7288 }, 7289 { 7290 name: "topo is locked", 7291 keyspace: nil, 7292 shards: []*vtctldatapb.Shard{ 7293 { 7294 Keyspace: "testkeyspace", 7295 Name: "-", 7296 }, 7297 }, 7298 topoError: nil, 7299 topoIsLocked: true, 7300 srvKeyspaceDoesNotExist: false, 7301 req: &vtctldatapb.RemoveKeyspaceCellRequest{ 7302 Keyspace: "testkeyspace", 7303 Cell: "zone1", 7304 }, 7305 expected: nil, 7306 shouldErr: true, 7307 }, 7308 { 7309 name: "srvkeyspace already deleted", 7310 keyspace: nil, 7311 shards: []*vtctldatapb.Shard{ 7312 { 7313 Keyspace: "testkeyspace", 7314 Name: "-", 7315 }, 7316 }, 7317 topoError: nil, 7318 topoIsLocked: false, 7319 srvKeyspaceDoesNotExist: true, 7320 req: &vtctldatapb.RemoveKeyspaceCellRequest{ 7321 Keyspace: "testkeyspace", 7322 Cell: "zone1", 7323 }, 7324 expected: nil, 7325 shouldErr: true, 7326 }, 7327 } 7328 7329 for _, tt := range tests { 7330 tt := tt 7331 7332 t.Run(tt.name, func(t *testing.T) { 7333 t.Parallel() 7334 7335 cells := []string{"zone1", "zone2", "zone3"} 7336 7337 ctx := context.Background() 7338 ts, topofactory := memorytopo.NewServerAndFactory(cells...) 7339 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 7340 return NewVtctldServer(ts) 7341 }) 7342 7343 // Setup topo 7344 if tt.keyspace != nil { 7345 testutil.AddKeyspace(ctx, t, ts, tt.keyspace) 7346 } 7347 7348 testutil.AddShards(ctx, t, ts, tt.shards...) 7349 7350 // For certain tests, we don't actually create the SrvKeyspace 7351 // object. 7352 if !tt.srvKeyspaceDoesNotExist { 7353 updateSrvKeyspace := func(keyspace string) { 7354 for _, cell := range cells { 7355 err := ts.UpdateSrvKeyspace(ctx, cell, keyspace, &topodatapb.SrvKeyspace{}) 7356 require.NoError(t, err, "could not create empty SrvKeyspace for keyspace %s in cell %s", tt.req.Keyspace, cell) 7357 } 7358 } 7359 7360 updateSrvKeyspace(tt.req.Keyspace) 7361 if tt.keyspace != nil { 7362 updateSrvKeyspace(tt.keyspace.Name) 7363 } 7364 } 7365 7366 // Set errors and locks 7367 if tt.topoError != nil { 7368 topofactory.SetError(tt.topoError) 7369 } 7370 7371 if tt.topoIsLocked { 7372 lctx, unlock, err := ts.LockKeyspace(ctx, tt.req.Keyspace, "testing locked keyspace") 7373 require.NoError(t, err, "cannot lock keyspace %s", tt.req.Keyspace) 7374 defer unlock(&err) 7375 7376 ctx = lctx 7377 } 7378 7379 resp, err := vtctld.RemoveKeyspaceCell(ctx, tt.req) 7380 if tt.shouldErr { 7381 assert.Error(t, err) 7382 return 7383 } 7384 7385 assert.NoError(t, err) 7386 utils.MustMatch(t, tt.expected, resp) 7387 }) 7388 } 7389 } 7390 7391 func TestRemoveShardCell(t *testing.T) { 7392 t.Parallel() 7393 7394 tests := []struct { 7395 name string 7396 servingCells []string 7397 shards []*vtctldatapb.Shard 7398 replicationGraphs []*topo.ShardReplicationInfo 7399 topoError error 7400 topoIsLocked bool 7401 req *vtctldatapb.RemoveShardCellRequest 7402 expected *vtctldatapb.RemoveShardCellResponse 7403 shouldErr bool 7404 }{ 7405 { 7406 name: "success", 7407 shards: []*vtctldatapb.Shard{ 7408 { 7409 Keyspace: "testkeyspace", 7410 Name: "-", 7411 }, 7412 }, 7413 replicationGraphs: []*topo.ShardReplicationInfo{ 7414 topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ 7415 Nodes: []*topodatapb.ShardReplication_Node{ 7416 { 7417 TabletAlias: &topodatapb.TabletAlias{ 7418 Cell: "zone1", 7419 Uid: 100, 7420 }, 7421 }, 7422 }, 7423 }, "zone1", "testkeyspace", "-"), 7424 topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ 7425 Nodes: []*topodatapb.ShardReplication_Node{ 7426 { 7427 TabletAlias: &topodatapb.TabletAlias{ 7428 Cell: "zone2", 7429 Uid: 200, 7430 }, 7431 }, 7432 }, 7433 }, "zone2", "testkeyspace", "-"), 7434 topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ 7435 Nodes: []*topodatapb.ShardReplication_Node{ 7436 { 7437 TabletAlias: &topodatapb.TabletAlias{ 7438 Cell: "zone3", 7439 Uid: 300, 7440 }, 7441 }, 7442 }, 7443 }, "zone3", "testkeyspace", "-"), 7444 }, 7445 req: &vtctldatapb.RemoveShardCellRequest{ 7446 Keyspace: "testkeyspace", 7447 ShardName: "-", 7448 Cell: "zone2", 7449 Recursive: true, 7450 }, 7451 expected: &vtctldatapb.RemoveShardCellResponse{}, 7452 shouldErr: false, 7453 }, 7454 { 7455 name: "success/no tablets", 7456 shards: []*vtctldatapb.Shard{ 7457 { 7458 Keyspace: "testkeyspace", 7459 Name: "-", 7460 }, 7461 }, 7462 req: &vtctldatapb.RemoveShardCellRequest{ 7463 Keyspace: "testkeyspace", 7464 ShardName: "-", 7465 Cell: "zone2", 7466 }, 7467 expected: &vtctldatapb.RemoveShardCellResponse{}, 7468 shouldErr: false, 7469 }, 7470 { 7471 name: "nonexistent shard", 7472 shards: nil, 7473 replicationGraphs: nil, 7474 req: &vtctldatapb.RemoveShardCellRequest{ 7475 Keyspace: "testkeyspace", 7476 ShardName: "-", 7477 Cell: "zone2", 7478 }, 7479 expected: nil, 7480 shouldErr: true, 7481 }, 7482 { 7483 name: "cell does not exist", 7484 shards: []*vtctldatapb.Shard{ 7485 { 7486 Keyspace: "testkeyspace", 7487 Name: "-", 7488 }, 7489 }, 7490 req: &vtctldatapb.RemoveShardCellRequest{ 7491 Keyspace: "testkeyspace", 7492 ShardName: "-", 7493 Cell: "fourthzone", 7494 }, 7495 expected: nil, 7496 shouldErr: true, 7497 }, 7498 { 7499 name: "cell not in serving list", 7500 shards: []*vtctldatapb.Shard{ 7501 { 7502 Keyspace: "testkeyspace", 7503 Name: "-", 7504 }, 7505 }, 7506 servingCells: []string{"zone1"}, 7507 replicationGraphs: nil, 7508 req: &vtctldatapb.RemoveShardCellRequest{ 7509 Keyspace: "testkeyspace", 7510 ShardName: "-", 7511 Cell: "zone2", 7512 }, 7513 expected: nil, 7514 shouldErr: true, 7515 }, 7516 { 7517 name: "tablets/non-recursive", 7518 shards: []*vtctldatapb.Shard{ 7519 { 7520 Keyspace: "testkeyspace", 7521 Name: "-", 7522 }, 7523 }, 7524 replicationGraphs: []*topo.ShardReplicationInfo{ 7525 topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ 7526 Nodes: []*topodatapb.ShardReplication_Node{ 7527 { 7528 TabletAlias: &topodatapb.TabletAlias{ 7529 Cell: "zone1", 7530 Uid: 100, 7531 }, 7532 }, 7533 }, 7534 }, "zone1", "testkeyspace", "-"), 7535 topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ 7536 Nodes: []*topodatapb.ShardReplication_Node{ 7537 { 7538 TabletAlias: &topodatapb.TabletAlias{ 7539 Cell: "zone2", 7540 Uid: 200, 7541 }, 7542 }, 7543 }, 7544 }, "zone2", "testkeyspace", "-"), 7545 topo.NewShardReplicationInfo(&topodatapb.ShardReplication{ 7546 Nodes: []*topodatapb.ShardReplication_Node{ 7547 { 7548 TabletAlias: &topodatapb.TabletAlias{ 7549 Cell: "zone3", 7550 Uid: 300, 7551 }, 7552 }, 7553 }, 7554 }, "zone3", "testkeyspace", "-"), 7555 }, 7556 req: &vtctldatapb.RemoveShardCellRequest{ 7557 Keyspace: "testkeyspace", 7558 ShardName: "-", 7559 Cell: "zone2", 7560 Recursive: false, // non-recursive + replication graph = failure 7561 }, 7562 expected: nil, 7563 shouldErr: true, 7564 }, 7565 { 7566 name: "topo server down", 7567 shards: []*vtctldatapb.Shard{ 7568 { 7569 Keyspace: "testkeyspace", 7570 Name: "-", 7571 }, 7572 }, 7573 replicationGraphs: nil, 7574 req: &vtctldatapb.RemoveShardCellRequest{ 7575 Keyspace: "testkeyspace", 7576 ShardName: "-", 7577 Cell: "zone2", 7578 }, 7579 topoError: assert.AnError, 7580 topoIsLocked: false, 7581 expected: nil, 7582 shouldErr: true, 7583 }, 7584 // Not sure how to set up this test case. 7585 // { 7586 // name: "topo server down for replication check/no force", 7587 // }, 7588 // Not sure how to set up this test case. 7589 // { 7590 // name: "topo server down for replication check/force", 7591 // }, 7592 { 7593 name: "cannot lock keyspace", 7594 shards: []*vtctldatapb.Shard{ 7595 { 7596 Keyspace: "testkeyspace", 7597 Name: "-", 7598 }, 7599 }, 7600 replicationGraphs: nil, 7601 req: &vtctldatapb.RemoveShardCellRequest{ 7602 Keyspace: "testkeyspace", 7603 ShardName: "-", 7604 Cell: "zone2", 7605 }, 7606 topoError: nil, 7607 topoIsLocked: true, 7608 expected: nil, 7609 shouldErr: true, 7610 }, 7611 // Not sure how to set up this test case. 7612 // { 7613 // name: "cannot delete srvkeyspace partition", 7614 // }, 7615 } 7616 7617 for _, tt := range tests { 7618 tt := tt 7619 7620 t.Run(tt.name, func(t *testing.T) { 7621 t.Parallel() 7622 7623 cells := []string{"zone1", "zone2", "zone3"} 7624 7625 ctx := context.Background() 7626 ts, topofactory := memorytopo.NewServerAndFactory(cells...) 7627 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 7628 return NewVtctldServer(ts) 7629 }) 7630 7631 // Setup shard topos and replication graphs. 7632 testutil.AddShards(ctx, t, ts, tt.shards...) 7633 testutil.SetupReplicationGraphs(ctx, t, ts, tt.replicationGraphs...) 7634 7635 // Set up srvkeyspace partitions; a little gross. 7636 servingCells := tt.servingCells 7637 if servingCells == nil { // we expect an explicit empty list to have a shard with no serving cells 7638 servingCells = cells 7639 } 7640 7641 for _, shard := range tt.shards { 7642 lctx, unlock, lerr := ts.LockKeyspace(ctx, shard.Keyspace, "initializing serving graph for test") 7643 require.NoError(t, lerr, "cannot lock keyspace %s to initialize serving graph", shard.Keyspace) 7644 7645 for _, cell := range servingCells { 7646 7647 err := ts.UpdateSrvKeyspace(lctx, cell, shard.Keyspace, &topodatapb.SrvKeyspace{ 7648 Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ 7649 { 7650 ServedType: topodatapb.TabletType_REPLICA, 7651 ShardReferences: []*topodatapb.ShardReference{ 7652 { 7653 Name: shard.Name, 7654 }, 7655 }, 7656 }, 7657 }, 7658 }) 7659 require.NoError(t, err, "cannot update srvkeyspace for %s/%s in cell %v", shard.Keyspace, shard.Name, cell) 7660 } 7661 7662 unlock(&lerr) 7663 } 7664 7665 // Set errors and locks. 7666 if tt.topoError != nil { 7667 topofactory.SetError(tt.topoError) 7668 } 7669 7670 if tt.topoIsLocked { 7671 lctx, unlock, err := ts.LockKeyspace(ctx, tt.req.Keyspace, "testing locked keyspace") 7672 require.NoError(t, err, "cannot lock keyspace %s", tt.req.Keyspace) 7673 defer unlock(&err) 7674 7675 // Need to use the lock ctx in the RPC call so we fail when 7676 // attempting to lock the keyspace rather than waiting forever 7677 // for the lock. Explicitly setting a deadline would be another 7678 // way to achieve this. 7679 ctx = lctx 7680 } 7681 7682 // Make the RPC and assert things about it. 7683 resp, err := vtctld.RemoveShardCell(ctx, tt.req) 7684 if tt.shouldErr { 7685 assert.Error(t, err) 7686 return 7687 } 7688 7689 assert.NoError(t, err) 7690 utils.MustMatch(t, tt.expected, resp) 7691 }) 7692 } 7693 } 7694 7695 func TestReparentTablet(t *testing.T) { 7696 t.Parallel() 7697 7698 tests := []struct { 7699 name string 7700 tmc tmclient.TabletManagerClient 7701 tablets []*topodatapb.Tablet 7702 shards []*vtctldatapb.Shard 7703 topoErr error 7704 req *vtctldatapb.ReparentTabletRequest 7705 expected *vtctldatapb.ReparentTabletResponse 7706 shouldErr bool 7707 }{ 7708 { 7709 name: "success", 7710 tmc: &testutil.TabletManagerClient{ 7711 SetReplicationSourceResults: map[string]error{ 7712 "zone1-0000000100": nil, 7713 }, 7714 }, 7715 tablets: []*topodatapb.Tablet{ 7716 { 7717 Alias: &topodatapb.TabletAlias{ 7718 Cell: "zone1", 7719 Uid: 100, 7720 }, 7721 Type: topodatapb.TabletType_REPLICA, 7722 Keyspace: "testkeyspace", 7723 Shard: "-", 7724 }, 7725 { 7726 Alias: &topodatapb.TabletAlias{ 7727 Cell: "zone2", 7728 Uid: 200, 7729 }, 7730 Type: topodatapb.TabletType_PRIMARY, 7731 Keyspace: "testkeyspace", 7732 Shard: "-", 7733 PrimaryTermStartTime: &vttime.Time{ 7734 Seconds: 1000, 7735 }, 7736 }, 7737 }, 7738 shards: []*vtctldatapb.Shard{ 7739 { 7740 Keyspace: "testkeyspace", 7741 Name: "-", 7742 Shard: &topodatapb.Shard{ 7743 PrimaryAlias: &topodatapb.TabletAlias{ 7744 Cell: "zone2", 7745 Uid: 200, 7746 }, 7747 PrimaryTermStartTime: &vttime.Time{ 7748 Seconds: 1000, 7749 }, 7750 IsPrimaryServing: true, 7751 }, 7752 }, 7753 }, 7754 req: &vtctldatapb.ReparentTabletRequest{ 7755 Tablet: &topodatapb.TabletAlias{ 7756 Cell: "zone1", 7757 Uid: 100, 7758 }, 7759 }, 7760 expected: &vtctldatapb.ReparentTabletResponse{ 7761 Keyspace: "testkeyspace", 7762 Shard: "-", 7763 Primary: &topodatapb.TabletAlias{ 7764 Cell: "zone2", 7765 Uid: 200, 7766 }, 7767 }, 7768 shouldErr: false, 7769 }, 7770 { 7771 name: "tablet is nil", 7772 tablets: []*topodatapb.Tablet{ 7773 { 7774 Alias: &topodatapb.TabletAlias{ 7775 Cell: "zone1", 7776 Uid: 100, 7777 }, 7778 Type: topodatapb.TabletType_REPLICA, 7779 Keyspace: "testkeyspace", 7780 Shard: "-", 7781 }, 7782 { 7783 Alias: &topodatapb.TabletAlias{ 7784 Cell: "zone2", 7785 Uid: 200, 7786 }, 7787 Type: topodatapb.TabletType_PRIMARY, 7788 Keyspace: "testkeyspace", 7789 Shard: "-", 7790 PrimaryTermStartTime: &vttime.Time{ 7791 Seconds: 1000, 7792 }, 7793 }, 7794 }, 7795 shards: []*vtctldatapb.Shard{ 7796 { 7797 Keyspace: "testkeyspace", 7798 Name: "-", 7799 Shard: &topodatapb.Shard{ 7800 PrimaryAlias: &topodatapb.TabletAlias{ 7801 Cell: "zone2", 7802 Uid: 200, 7803 }, 7804 PrimaryTermStartTime: &vttime.Time{ 7805 Seconds: 1000, 7806 }, 7807 IsPrimaryServing: true, 7808 }, 7809 }, 7810 }, 7811 req: &vtctldatapb.ReparentTabletRequest{ 7812 Tablet: nil, 7813 }, 7814 expected: nil, 7815 shouldErr: true, 7816 }, 7817 { 7818 name: "tablet not in topo", 7819 tablets: []*topodatapb.Tablet{ 7820 { 7821 Alias: &topodatapb.TabletAlias{ 7822 Cell: "zone2", 7823 Uid: 200, 7824 }, 7825 Type: topodatapb.TabletType_PRIMARY, 7826 Keyspace: "testkeyspace", 7827 Shard: "-", 7828 PrimaryTermStartTime: &vttime.Time{ 7829 Seconds: 1000, 7830 }, 7831 }, 7832 }, 7833 shards: []*vtctldatapb.Shard{ 7834 { 7835 Keyspace: "testkeyspace", 7836 Name: "-", 7837 Shard: &topodatapb.Shard{ 7838 PrimaryAlias: &topodatapb.TabletAlias{ 7839 Cell: "zone2", 7840 Uid: 200, 7841 }, 7842 PrimaryTermStartTime: &vttime.Time{ 7843 Seconds: 1000, 7844 }, 7845 IsPrimaryServing: true, 7846 }, 7847 }, 7848 }, 7849 req: &vtctldatapb.ReparentTabletRequest{ 7850 Tablet: &topodatapb.TabletAlias{ 7851 Cell: "zone1", 7852 Uid: 100, 7853 }, 7854 }, 7855 expected: nil, 7856 shouldErr: true, 7857 }, 7858 { 7859 name: "shard not in topo", 7860 tablets: []*topodatapb.Tablet{ 7861 { 7862 Alias: &topodatapb.TabletAlias{ 7863 Cell: "zone1", 7864 Uid: 100, 7865 }, 7866 Type: topodatapb.TabletType_REPLICA, 7867 Keyspace: "testkeyspace", 7868 Shard: "-", 7869 }, 7870 { 7871 Alias: &topodatapb.TabletAlias{ 7872 Cell: "zone2", 7873 Uid: 200, 7874 }, 7875 Type: topodatapb.TabletType_PRIMARY, 7876 Keyspace: "testkeyspace", 7877 Shard: "-", 7878 PrimaryTermStartTime: &vttime.Time{ 7879 Seconds: 1000, 7880 }, 7881 }, 7882 }, 7883 shards: nil, 7884 req: &vtctldatapb.ReparentTabletRequest{ 7885 Tablet: &topodatapb.TabletAlias{ 7886 Cell: "zone1", 7887 Uid: 100, 7888 }, 7889 }, 7890 expected: nil, 7891 shouldErr: true, 7892 }, 7893 { 7894 name: "shard has no primary", 7895 tablets: []*topodatapb.Tablet{ 7896 { 7897 Alias: &topodatapb.TabletAlias{ 7898 Cell: "zone1", 7899 Uid: 100, 7900 }, 7901 Type: topodatapb.TabletType_REPLICA, 7902 Keyspace: "testkeyspace", 7903 Shard: "-", 7904 }, 7905 }, 7906 shards: []*vtctldatapb.Shard{ 7907 { 7908 Keyspace: "testkeyspace", 7909 Name: "-", 7910 Shard: &topodatapb.Shard{ 7911 IsPrimaryServing: false, 7912 }, 7913 }, 7914 }, 7915 req: &vtctldatapb.ReparentTabletRequest{ 7916 Tablet: &topodatapb.TabletAlias{ 7917 Cell: "zone1", 7918 Uid: 100, 7919 }, 7920 }, 7921 expected: nil, 7922 shouldErr: true, 7923 }, 7924 { 7925 name: "shard primary not in topo", 7926 tablets: []*topodatapb.Tablet{ 7927 { 7928 Alias: &topodatapb.TabletAlias{ 7929 Cell: "zone1", 7930 Uid: 100, 7931 }, 7932 Type: topodatapb.TabletType_REPLICA, 7933 Keyspace: "testkeyspace", 7934 Shard: "-", 7935 }, 7936 { 7937 Alias: &topodatapb.TabletAlias{ 7938 Cell: "zone2", 7939 Uid: 200, 7940 }, 7941 Type: topodatapb.TabletType_PRIMARY, 7942 Keyspace: "testkeyspace", 7943 Shard: "-", 7944 PrimaryTermStartTime: &vttime.Time{ 7945 Seconds: 1000, 7946 }, 7947 }, 7948 }, 7949 shards: []*vtctldatapb.Shard{ 7950 { 7951 Keyspace: "testkeyspace", 7952 Name: "-", 7953 Shard: &topodatapb.Shard{ 7954 PrimaryAlias: &topodatapb.TabletAlias{ 7955 Cell: "zone3", 7956 Uid: 300, 7957 }, 7958 PrimaryTermStartTime: &vttime.Time{ 7959 Seconds: 1010, 7960 }, 7961 IsPrimaryServing: true, 7962 }, 7963 }, 7964 }, 7965 req: &vtctldatapb.ReparentTabletRequest{ 7966 Tablet: &topodatapb.TabletAlias{ 7967 Cell: "zone1", 7968 Uid: 100, 7969 }, 7970 }, 7971 expected: nil, 7972 shouldErr: true, 7973 }, 7974 { 7975 name: "shard primary is not type PRIMARY", 7976 tablets: []*topodatapb.Tablet{ 7977 { 7978 Alias: &topodatapb.TabletAlias{ 7979 Cell: "zone1", 7980 Uid: 100, 7981 }, 7982 Type: topodatapb.TabletType_REPLICA, 7983 Keyspace: "testkeyspace", 7984 Shard: "-", 7985 }, 7986 { 7987 Alias: &topodatapb.TabletAlias{ 7988 Cell: "zone2", 7989 Uid: 200, 7990 }, 7991 Type: topodatapb.TabletType_REPLICA, 7992 Keyspace: "testkeyspace", 7993 Shard: "-", 7994 }, 7995 }, 7996 shards: []*vtctldatapb.Shard{ 7997 { 7998 Keyspace: "testkeyspace", 7999 Name: "-", 8000 Shard: &topodatapb.Shard{ 8001 PrimaryAlias: &topodatapb.TabletAlias{ 8002 Cell: "zone2", 8003 Uid: 200, 8004 }, 8005 PrimaryTermStartTime: &vttime.Time{ 8006 Seconds: 1010, 8007 }, 8008 IsPrimaryServing: true, 8009 }, 8010 }, 8011 }, 8012 req: &vtctldatapb.ReparentTabletRequest{ 8013 Tablet: &topodatapb.TabletAlias{ 8014 Cell: "zone1", 8015 Uid: 100, 8016 }, 8017 }, 8018 expected: nil, 8019 shouldErr: true, 8020 }, 8021 { 8022 name: "shard primary is not actually in shard", 8023 tablets: []*topodatapb.Tablet{ 8024 { 8025 Alias: &topodatapb.TabletAlias{ 8026 Cell: "zone1", 8027 Uid: 100, 8028 }, 8029 Type: topodatapb.TabletType_REPLICA, 8030 Keyspace: "testkeyspace", 8031 Shard: "-", 8032 }, 8033 { 8034 Alias: &topodatapb.TabletAlias{ 8035 Cell: "zone2", 8036 Uid: 200, 8037 }, 8038 Type: topodatapb.TabletType_PRIMARY, 8039 Keyspace: "otherkeyspace", 8040 Shard: "-", 8041 PrimaryTermStartTime: &vttime.Time{ 8042 Seconds: 1000, 8043 }, 8044 }, 8045 }, 8046 shards: []*vtctldatapb.Shard{ 8047 { 8048 Keyspace: "testkeyspace", 8049 Name: "-", 8050 Shard: &topodatapb.Shard{ 8051 PrimaryAlias: &topodatapb.TabletAlias{ 8052 Cell: "zone2", 8053 Uid: 200, 8054 }, 8055 PrimaryTermStartTime: &vttime.Time{ 8056 Seconds: 1010, 8057 }, 8058 IsPrimaryServing: true, 8059 }, 8060 }, 8061 }, 8062 req: &vtctldatapb.ReparentTabletRequest{ 8063 Tablet: &topodatapb.TabletAlias{ 8064 Cell: "zone1", 8065 Uid: 100, 8066 }, 8067 }, 8068 expected: nil, 8069 shouldErr: true, 8070 }, 8071 { 8072 name: "requested tablet is shard primary", 8073 tablets: []*topodatapb.Tablet{ 8074 { 8075 Alias: &topodatapb.TabletAlias{ 8076 Cell: "zone1", 8077 Uid: 100, 8078 }, 8079 Type: topodatapb.TabletType_PRIMARY, 8080 Keyspace: "testkeyspace", 8081 Shard: "-", 8082 }, 8083 }, 8084 shards: []*vtctldatapb.Shard{ 8085 { 8086 Keyspace: "testkeyspace", 8087 Name: "-", 8088 Shard: &topodatapb.Shard{ 8089 PrimaryAlias: &topodatapb.TabletAlias{ 8090 Cell: "zone1", 8091 Uid: 100, 8092 }, 8093 PrimaryTermStartTime: &vttime.Time{ 8094 Seconds: 1010, 8095 }, 8096 IsPrimaryServing: true, 8097 }, 8098 }, 8099 }, 8100 req: &vtctldatapb.ReparentTabletRequest{ 8101 Tablet: &topodatapb.TabletAlias{ 8102 Cell: "zone1", 8103 Uid: 100, 8104 }, 8105 }, 8106 expected: nil, 8107 shouldErr: true, 8108 }, 8109 { 8110 name: "tmc.SetReplicationSource failure", 8111 tmc: &testutil.TabletManagerClient{ 8112 SetReplicationSourceResults: map[string]error{ 8113 "zone1-0000000100": assert.AnError, 8114 }, 8115 }, 8116 tablets: []*topodatapb.Tablet{ 8117 { 8118 Alias: &topodatapb.TabletAlias{ 8119 Cell: "zone1", 8120 Uid: 100, 8121 }, 8122 Type: topodatapb.TabletType_REPLICA, 8123 Keyspace: "testkeyspace", 8124 Shard: "-", 8125 }, 8126 { 8127 Alias: &topodatapb.TabletAlias{ 8128 Cell: "zone2", 8129 Uid: 200, 8130 }, 8131 Type: topodatapb.TabletType_PRIMARY, 8132 Keyspace: "testkeyspace", 8133 Shard: "-", 8134 PrimaryTermStartTime: &vttime.Time{ 8135 Seconds: 1000, 8136 }, 8137 }, 8138 }, 8139 shards: []*vtctldatapb.Shard{ 8140 { 8141 Keyspace: "testkeyspace", 8142 Name: "-", 8143 Shard: &topodatapb.Shard{ 8144 PrimaryAlias: &topodatapb.TabletAlias{ 8145 Cell: "zone2", 8146 Uid: 200, 8147 }, 8148 PrimaryTermStartTime: &vttime.Time{ 8149 Seconds: 1000, 8150 }, 8151 IsPrimaryServing: true, 8152 }, 8153 }, 8154 }, 8155 req: &vtctldatapb.ReparentTabletRequest{ 8156 Tablet: &topodatapb.TabletAlias{ 8157 Cell: "zone1", 8158 Uid: 100, 8159 }, 8160 }, 8161 expected: nil, 8162 shouldErr: true, 8163 }, 8164 { 8165 name: "topo is down", 8166 tmc: &testutil.TabletManagerClient{ 8167 SetReplicationSourceResults: map[string]error{ 8168 "zone1-0000000100": nil, 8169 }, 8170 }, 8171 tablets: []*topodatapb.Tablet{ 8172 { 8173 Alias: &topodatapb.TabletAlias{ 8174 Cell: "zone1", 8175 Uid: 100, 8176 }, 8177 Type: topodatapb.TabletType_REPLICA, 8178 Keyspace: "testkeyspace", 8179 Shard: "-", 8180 }, 8181 { 8182 Alias: &topodatapb.TabletAlias{ 8183 Cell: "zone2", 8184 Uid: 200, 8185 }, 8186 Type: topodatapb.TabletType_PRIMARY, 8187 Keyspace: "testkeyspace", 8188 Shard: "-", 8189 PrimaryTermStartTime: &vttime.Time{ 8190 Seconds: 1000, 8191 }, 8192 }, 8193 }, 8194 shards: []*vtctldatapb.Shard{ 8195 { 8196 Keyspace: "testkeyspace", 8197 Name: "-", 8198 Shard: &topodatapb.Shard{ 8199 PrimaryAlias: &topodatapb.TabletAlias{ 8200 Cell: "zone2", 8201 Uid: 200, 8202 }, 8203 PrimaryTermStartTime: &vttime.Time{ 8204 Seconds: 1000, 8205 }, 8206 IsPrimaryServing: true, 8207 }, 8208 }, 8209 }, 8210 topoErr: assert.AnError, 8211 req: &vtctldatapb.ReparentTabletRequest{ 8212 Tablet: &topodatapb.TabletAlias{ 8213 Cell: "zone1", 8214 Uid: 100, 8215 }, 8216 }, 8217 expected: nil, 8218 shouldErr: true, 8219 }, 8220 } 8221 8222 for _, tt := range tests { 8223 tt := tt 8224 8225 t.Run(tt.name, func(t *testing.T) { 8226 t.Parallel() 8227 8228 if tt.req == nil { 8229 t.Skip("focused on other test cases right now") 8230 } 8231 8232 cells := []string{"zone1", "zone2", "zone3"} 8233 8234 ctx := context.Background() 8235 ts, topofactory := memorytopo.NewServerAndFactory(cells...) 8236 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 8237 return NewVtctldServer(ts) 8238 }) 8239 8240 testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ 8241 SkipShardCreation: true, 8242 }, tt.tablets...) 8243 testutil.AddShards(ctx, t, ts, tt.shards...) 8244 8245 if tt.topoErr != nil { 8246 topofactory.SetError(tt.topoErr) 8247 } 8248 8249 resp, err := vtctld.ReparentTablet(ctx, tt.req) 8250 if tt.shouldErr { 8251 assert.Error(t, err) 8252 return 8253 } 8254 8255 assert.NoError(t, err) 8256 utils.MustMatch(t, tt.expected, resp) 8257 }) 8258 } 8259 } 8260 8261 func TestRestoreFromBackup(t *testing.T) { 8262 ctx := context.Background() 8263 tests := []struct { 8264 name string 8265 ts *topo.Server 8266 tmc tmclient.TabletManagerClient 8267 tablets []*topodatapb.Tablet 8268 req *vtctldatapb.RestoreFromBackupRequest 8269 shouldErr bool 8270 assertion func(t *testing.T, responses []*vtctldatapb.RestoreFromBackupResponse, err error) 8271 }{ 8272 { 8273 name: "ok", 8274 ts: memorytopo.NewServer("zone1"), 8275 tmc: &testutil.TabletManagerClient{ 8276 RestoreFromBackupResults: map[string]struct { 8277 Events []*logutilpb.Event 8278 EventInterval time.Duration 8279 EventJitter time.Duration 8280 ErrorAfter time.Duration 8281 }{ 8282 "zone1-0000000100": { 8283 Events: []*logutilpb.Event{{}, {}, {}}, 8284 }, 8285 }, 8286 SetReplicationSourceResults: map[string]error{ 8287 "zone1-0000000100": nil, 8288 }, 8289 }, 8290 tablets: []*topodatapb.Tablet{ 8291 { 8292 Alias: &topodatapb.TabletAlias{ 8293 Cell: "zone1", 8294 Uid: 100, 8295 }, 8296 Keyspace: "ks", 8297 Shard: "-", 8298 Type: topodatapb.TabletType_REPLICA, 8299 }, 8300 { 8301 Alias: &topodatapb.TabletAlias{ 8302 Cell: "zone1", 8303 Uid: 200, 8304 }, 8305 Keyspace: "ks", 8306 Shard: "-", 8307 Type: topodatapb.TabletType_PRIMARY, 8308 }, 8309 }, 8310 req: &vtctldatapb.RestoreFromBackupRequest{ 8311 TabletAlias: &topodatapb.TabletAlias{ 8312 Cell: "zone1", 8313 Uid: 100, 8314 }, 8315 }, 8316 assertion: func(t *testing.T, responses []*vtctldatapb.RestoreFromBackupResponse, err error) { 8317 assert.ErrorIs(t, err, io.EOF, "expected Recv loop to end with io.EOF") 8318 assert.Equal(t, 3, len(responses), "expected 3 messages from restorefrombackupclient stream") 8319 }, 8320 }, 8321 { 8322 name: "no such tablet", 8323 ts: memorytopo.NewServer("zone1"), 8324 tmc: &testutil.TabletManagerClient{ 8325 Backups: map[string]struct { 8326 Events []*logutilpb.Event 8327 EventInterval time.Duration 8328 EventJitter time.Duration 8329 ErrorAfter time.Duration 8330 }{ 8331 "zone1-0000000100": { 8332 Events: []*logutilpb.Event{{}, {}, {}}, 8333 }, 8334 }, 8335 }, 8336 tablets: []*topodatapb.Tablet{ 8337 { 8338 Alias: &topodatapb.TabletAlias{ 8339 Cell: "zone1", 8340 Uid: 100, 8341 }, 8342 Type: topodatapb.TabletType_REPLICA, 8343 Keyspace: "ks", 8344 Shard: "-", 8345 }, 8346 }, 8347 req: &vtctldatapb.RestoreFromBackupRequest{ 8348 TabletAlias: &topodatapb.TabletAlias{ 8349 Cell: "zone404", 8350 Uid: 404, 8351 }, 8352 }, 8353 assertion: func(t *testing.T, responses []*vtctldatapb.RestoreFromBackupResponse, err error) { 8354 assert.NotErrorIs(t, err, io.EOF, "expected restorefrombackupclient stream to close with non-EOF") 8355 assert.Zero(t, len(responses), "expected no restorefrombackupclient messages") 8356 }, 8357 }, 8358 } 8359 8360 for _, tt := range tests { 8361 tt := tt 8362 t.Run(tt.name, func(t *testing.T) { 8363 testutil.AddTablets(ctx, t, tt.ts, 8364 &testutil.AddTabletOptions{ 8365 AlsoSetShardPrimary: true, 8366 }, tt.tablets..., 8367 ) 8368 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 8369 return NewVtctldServer(ts) 8370 }) 8371 client := localvtctldclient.New(vtctld) 8372 stream, err := client.RestoreFromBackup(ctx, tt.req) 8373 if tt.shouldErr { 8374 assert.Error(t, err) 8375 } else { 8376 require.NoError(t, err) 8377 } 8378 8379 responses, err := func() (responses []*vtctldatapb.RestoreFromBackupResponse, err error) { 8380 for { 8381 resp, err := stream.Recv() 8382 if err != nil { 8383 return responses, err 8384 } 8385 8386 responses = append(responses, resp) 8387 } 8388 }() 8389 8390 if tt.assertion != nil { 8391 func() { 8392 t.Helper() 8393 tt.assertion(t, responses, err) 8394 }() 8395 } 8396 }) 8397 } 8398 } 8399 8400 func TestRunHealthCheck(t *testing.T) { 8401 t.Parallel() 8402 8403 ctx := context.Background() 8404 tests := []struct { 8405 name string 8406 tablets []*topodatapb.Tablet 8407 tmc testutil.TabletManagerClient 8408 req *vtctldatapb.RunHealthCheckRequest 8409 shouldErr bool 8410 }{ 8411 { 8412 name: "ok", 8413 tablets: []*topodatapb.Tablet{ 8414 { 8415 Alias: &topodatapb.TabletAlias{ 8416 Cell: "zone1", 8417 Uid: 100, 8418 }, 8419 }, 8420 }, 8421 tmc: testutil.TabletManagerClient{ 8422 RunHealthCheckResults: map[string]error{ 8423 "zone1-0000000100": nil, 8424 }, 8425 }, 8426 req: &vtctldatapb.RunHealthCheckRequest{ 8427 TabletAlias: &topodatapb.TabletAlias{ 8428 Cell: "zone1", 8429 Uid: 100, 8430 }, 8431 }, 8432 }, 8433 { 8434 name: "no tablet", 8435 tablets: []*topodatapb.Tablet{ 8436 { 8437 Alias: &topodatapb.TabletAlias{ 8438 Cell: "zone1", 8439 Uid: 404, 8440 }, 8441 }, 8442 }, 8443 tmc: testutil.TabletManagerClient{ 8444 RunHealthCheckResults: map[string]error{ 8445 "zone1-0000000100": nil, 8446 }, 8447 }, 8448 req: &vtctldatapb.RunHealthCheckRequest{ 8449 TabletAlias: &topodatapb.TabletAlias{ 8450 Cell: "zone1", 8451 Uid: 100, 8452 }, 8453 }, 8454 shouldErr: true, 8455 }, 8456 { 8457 name: "tmc call failed", 8458 tablets: []*topodatapb.Tablet{ 8459 { 8460 Alias: &topodatapb.TabletAlias{ 8461 Cell: "zone1", 8462 Uid: 100, 8463 }, 8464 }, 8465 }, 8466 tmc: testutil.TabletManagerClient{ 8467 RunHealthCheckResults: map[string]error{ 8468 "zone1-0000000100": assert.AnError, 8469 }, 8470 }, 8471 req: &vtctldatapb.RunHealthCheckRequest{ 8472 TabletAlias: &topodatapb.TabletAlias{ 8473 Cell: "zone1", 8474 Uid: 100, 8475 }, 8476 }, 8477 shouldErr: true, 8478 }, 8479 } 8480 8481 for _, tt := range tests { 8482 tt := tt 8483 t.Run(tt.name, func(t *testing.T) { 8484 t.Parallel() 8485 8486 ts := memorytopo.NewServer("zone1") 8487 testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) 8488 8489 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 8490 return NewVtctldServer(ts) 8491 }) 8492 _, err := vtctld.RunHealthCheck(ctx, tt.req) 8493 if tt.shouldErr { 8494 assert.Error(t, err) 8495 return 8496 } 8497 8498 require.NoError(t, err) 8499 }) 8500 } 8501 } 8502 8503 func TestSetKeyspaceDurabilityPolicy(t *testing.T) { 8504 t.Parallel() 8505 8506 tests := []struct { 8507 name string 8508 keyspaces []*vtctldatapb.Keyspace 8509 req *vtctldatapb.SetKeyspaceDurabilityPolicyRequest 8510 expected *vtctldatapb.SetKeyspaceDurabilityPolicyResponse 8511 expectedErr string 8512 }{ 8513 { 8514 name: "ok", 8515 keyspaces: []*vtctldatapb.Keyspace{ 8516 { 8517 Name: "ks1", 8518 Keyspace: &topodatapb.Keyspace{}, 8519 }, 8520 { 8521 Name: "ks2", 8522 Keyspace: &topodatapb.Keyspace{}, 8523 }, 8524 }, 8525 req: &vtctldatapb.SetKeyspaceDurabilityPolicyRequest{ 8526 Keyspace: "ks1", 8527 DurabilityPolicy: "none", 8528 }, 8529 expected: &vtctldatapb.SetKeyspaceDurabilityPolicyResponse{ 8530 Keyspace: &topodatapb.Keyspace{ 8531 DurabilityPolicy: "none", 8532 }, 8533 }, 8534 }, 8535 { 8536 name: "keyspace not found", 8537 req: &vtctldatapb.SetKeyspaceDurabilityPolicyRequest{ 8538 Keyspace: "ks1", 8539 }, 8540 expectedErr: "node doesn't exist: keyspaces/ks1", 8541 }, 8542 { 8543 name: "fail to update durability policy", 8544 keyspaces: []*vtctldatapb.Keyspace{ 8545 { 8546 Name: "ks1", 8547 Keyspace: &topodatapb.Keyspace{}, 8548 }, 8549 }, 8550 req: &vtctldatapb.SetKeyspaceDurabilityPolicyRequest{ 8551 Keyspace: "ks1", 8552 DurabilityPolicy: "non-existent", 8553 }, 8554 expectedErr: "durability policy <non-existent> is not a valid policy. Please register it as a policy first", 8555 }, 8556 } 8557 8558 ctx := context.Background() 8559 8560 for _, tt := range tests { 8561 tt := tt 8562 t.Run(tt.name, func(t *testing.T) { 8563 t.Parallel() 8564 8565 ts := memorytopo.NewServer("zone1") 8566 testutil.AddKeyspaces(ctx, t, ts, tt.keyspaces...) 8567 8568 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 8569 return NewVtctldServer(ts) 8570 }) 8571 resp, err := vtctld.SetKeyspaceDurabilityPolicy(ctx, tt.req) 8572 if tt.expectedErr != "" { 8573 assert.EqualError(t, err, tt.expectedErr) 8574 return 8575 } 8576 8577 require.NoError(t, err) 8578 utils.MustMatch(t, tt.expected, resp) 8579 }) 8580 } 8581 } 8582 8583 func TestSetShardIsPrimaryServing(t *testing.T) { 8584 t.Parallel() 8585 8586 type testcase struct { 8587 name string 8588 ctx context.Context 8589 ts *topo.Server 8590 setup func(*testing.T, *testcase) 8591 teardown func(*testing.T, *testcase) 8592 req *vtctldatapb.SetShardIsPrimaryServingRequest 8593 expected *vtctldatapb.SetShardIsPrimaryServingResponse 8594 shouldErr bool 8595 } 8596 8597 tests := []*testcase{ 8598 { 8599 name: "ok", 8600 setup: func(t *testing.T, tt *testcase) { 8601 tt.ctx = context.Background() 8602 tt.ts = memorytopo.NewServer("zone1") 8603 testutil.AddShards(tt.ctx, t, tt.ts, &vtctldatapb.Shard{ 8604 Keyspace: "testkeyspace", 8605 Name: "-", 8606 Shard: &topodatapb.Shard{}, 8607 }) 8608 }, 8609 req: &vtctldatapb.SetShardIsPrimaryServingRequest{ 8610 Keyspace: "testkeyspace", 8611 Shard: "-", 8612 IsServing: true, 8613 }, 8614 expected: &vtctldatapb.SetShardIsPrimaryServingResponse{ 8615 Shard: &topodatapb.Shard{ 8616 IsPrimaryServing: true, 8617 }, 8618 }, 8619 }, 8620 { 8621 name: "lock error", 8622 setup: func(t *testing.T, tt *testcase) { 8623 var cancel func() 8624 tt.ctx, cancel = context.WithTimeout(context.Background(), time.Millisecond*50) 8625 tt.ts = memorytopo.NewServer("zone1") 8626 testutil.AddShards(tt.ctx, t, tt.ts, &vtctldatapb.Shard{ 8627 Keyspace: "testkeyspace", 8628 Name: "-", 8629 Shard: &topodatapb.Shard{}, 8630 }) 8631 8632 _, unlock, err := tt.ts.LockKeyspace(tt.ctx, "testkeyspace", "test lock") 8633 require.NoError(t, err) 8634 tt.teardown = func(t *testing.T, tt *testcase) { 8635 var err error 8636 unlock(&err) 8637 assert.NoError(t, err) 8638 cancel() 8639 } 8640 }, 8641 req: &vtctldatapb.SetShardIsPrimaryServingRequest{ 8642 Keyspace: "testkeyspace", 8643 Shard: "-", 8644 IsServing: true, 8645 }, 8646 expected: nil, 8647 shouldErr: true, 8648 }, 8649 } 8650 8651 for _, tt := range tests { 8652 tt := tt 8653 8654 t.Run(tt.name, func(t *testing.T) { 8655 t.Parallel() 8656 8657 if tt.setup != nil { 8658 tt.setup(t, tt) 8659 } 8660 if tt.teardown != nil { 8661 defer tt.teardown(t, tt) 8662 } 8663 8664 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 8665 return NewVtctldServer(ts) 8666 }) 8667 resp, err := vtctld.SetShardIsPrimaryServing(tt.ctx, tt.req) 8668 if tt.shouldErr { 8669 assert.Error(t, err) 8670 return 8671 } 8672 8673 assert.NoError(t, err) 8674 utils.MustMatch(t, tt.expected, resp) 8675 }) 8676 } 8677 } 8678 8679 func TestSetShardTabletControl(t *testing.T) { 8680 t.Parallel() 8681 8682 type testcase struct { 8683 name string 8684 ctx context.Context 8685 ts *topo.Server 8686 setup func(*testing.T, *testcase) 8687 teardown func(*testing.T, *testcase) 8688 req *vtctldatapb.SetShardTabletControlRequest 8689 expected *vtctldatapb.SetShardTabletControlResponse 8690 shouldErr bool 8691 } 8692 8693 tests := []*testcase{ 8694 { 8695 name: "ok", 8696 setup: func(t *testing.T, tt *testcase) { 8697 tt.ctx = context.Background() 8698 tt.ts = memorytopo.NewServer("zone1", "zone2", "zone3") 8699 8700 testutil.AddShards(tt.ctx, t, tt.ts, &vtctldatapb.Shard{ 8701 Keyspace: "testkeyspace", 8702 Name: "-", 8703 Shard: &topodatapb.Shard{ 8704 TabletControls: []*topodatapb.Shard_TabletControl{ 8705 { 8706 TabletType: topodatapb.TabletType_REPLICA, 8707 Cells: []string{"zone1"}, 8708 DeniedTables: []string{"t1"}, 8709 }, 8710 { 8711 TabletType: topodatapb.TabletType_REPLICA, 8712 Cells: []string{"zone2", "zone3"}, 8713 DeniedTables: []string{"t2"}, 8714 }, 8715 }, 8716 }, 8717 }) 8718 }, 8719 req: &vtctldatapb.SetShardTabletControlRequest{ 8720 Keyspace: "testkeyspace", 8721 Shard: "-", 8722 DeniedTables: []string{"t1"}, 8723 Cells: []string{"zone2", "zone3"}, 8724 TabletType: topodatapb.TabletType_REPLICA, 8725 }, 8726 expected: &vtctldatapb.SetShardTabletControlResponse{ 8727 Shard: &topodatapb.Shard{ 8728 TabletControls: []*topodatapb.Shard_TabletControl{ 8729 { 8730 TabletType: topodatapb.TabletType_REPLICA, 8731 Cells: []string{"zone1", "zone2", "zone3"}, 8732 DeniedTables: []string{"t1"}, 8733 }, 8734 { 8735 TabletType: topodatapb.TabletType_REPLICA, 8736 Cells: []string{"zone2", "zone3"}, 8737 DeniedTables: []string{"t2"}, 8738 }, 8739 }, 8740 }, 8741 }, 8742 }, 8743 { 8744 name: "remove tabletcontrols", 8745 setup: func(t *testing.T, tt *testcase) { 8746 tt.ctx = context.Background() 8747 tt.ts = memorytopo.NewServer("zone1", "zone2", "zone3") 8748 8749 testutil.AddShards(tt.ctx, t, tt.ts, &vtctldatapb.Shard{ 8750 Keyspace: "testkeyspace", 8751 Name: "-", 8752 Shard: &topodatapb.Shard{ 8753 TabletControls: []*topodatapb.Shard_TabletControl{ 8754 { 8755 TabletType: topodatapb.TabletType_REPLICA, 8756 Cells: []string{"zone1"}, 8757 DeniedTables: []string{"t1"}, 8758 }, 8759 { 8760 TabletType: topodatapb.TabletType_REPLICA, 8761 Cells: []string{"zone2", "zone3"}, 8762 DeniedTables: []string{"t2"}, 8763 }, 8764 }, 8765 }, 8766 }) 8767 }, 8768 req: &vtctldatapb.SetShardTabletControlRequest{ 8769 Keyspace: "testkeyspace", 8770 Shard: "-", 8771 TabletType: topodatapb.TabletType_REPLICA, 8772 Remove: true, 8773 }, 8774 expected: &vtctldatapb.SetShardTabletControlResponse{ 8775 Shard: &topodatapb.Shard{}, 8776 }, 8777 }, 8778 { 8779 name: "disable queryservice", 8780 setup: func(t *testing.T, tt *testcase) { 8781 tt.ctx = context.Background() 8782 tt.ts = memorytopo.NewServer("zone1", "zone2", "zone3") 8783 8784 testutil.AddShards(tt.ctx, t, tt.ts, &vtctldatapb.Shard{ 8785 Keyspace: "testkeyspace", 8786 Name: "-", 8787 }) 8788 8789 lctx, unlock, lerr := tt.ts.LockKeyspace(tt.ctx, "testkeyspace", "locking to create partitions for test") 8790 require.NoError(t, lerr, "could not lock keyspace to setup test partitions") 8791 var err error 8792 defer unlock(&err) 8793 defer func() { require.NoError(t, err) }() 8794 8795 err = tt.ts.UpdateSrvKeyspace(lctx, "zone1", "testkeyspace", &topodatapb.SrvKeyspace{ 8796 Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ 8797 { 8798 ServedType: topodatapb.TabletType_REPLICA, 8799 ShardTabletControls: []*topodatapb.ShardTabletControl{ 8800 { 8801 Name: "-", 8802 QueryServiceDisabled: false, 8803 }, 8804 }, 8805 }, 8806 }, 8807 }) 8808 require.NoError(t, err) 8809 err = tt.ts.UpdateSrvKeyspace(lctx, "zone2", "testkeyspace", &topodatapb.SrvKeyspace{ 8810 Partitions: []*topodatapb.SrvKeyspace_KeyspacePartition{ 8811 { 8812 ServedType: topodatapb.TabletType_REPLICA, 8813 ShardTabletControls: []*topodatapb.ShardTabletControl{ 8814 { 8815 Name: "-", 8816 QueryServiceDisabled: true, 8817 }, 8818 }, 8819 }, 8820 }, 8821 }) 8822 require.NoError(t, err) 8823 }, 8824 teardown: func(t *testing.T, tt *testcase) { 8825 expected := map[string][]*topodatapb.ShardTabletControl{ 8826 "zone1": { 8827 { 8828 Name: "-", 8829 QueryServiceDisabled: true, 8830 }, 8831 }, 8832 "zone2": { 8833 { 8834 Name: "-", 8835 QueryServiceDisabled: true, 8836 }, 8837 }, 8838 } 8839 for cell, expectedControls := range expected { 8840 partitions, err := tt.ts.GetSrvKeyspace(tt.ctx, cell, "testkeyspace") 8841 require.NoError(t, err, "could not get srvkeyspace for testkeyspace/%s", cell) 8842 8843 for _, partition := range partitions.Partitions { 8844 if partition.ServedType != topodatapb.TabletType_REPLICA { 8845 continue 8846 } 8847 8848 utils.MustMatch(t, expectedControls, partition.ShardTabletControls) 8849 } 8850 } 8851 }, 8852 req: &vtctldatapb.SetShardTabletControlRequest{ 8853 Keyspace: "testkeyspace", 8854 Shard: "-", 8855 Cells: []string{"zone1", "zone2"}, 8856 TabletType: topodatapb.TabletType_REPLICA, 8857 DisableQueryService: true, 8858 }, 8859 expected: &vtctldatapb.SetShardTabletControlResponse{ 8860 Shard: &topodatapb.Shard{ 8861 TabletControls: []*topodatapb.Shard_TabletControl{ 8862 { 8863 TabletType: topodatapb.TabletType_REPLICA, 8864 Cells: []string{"zone1", "zone2"}, 8865 }, 8866 }, 8867 IsPrimaryServing: true, 8868 KeyRange: &topodatapb.KeyRange{}, 8869 }, 8870 }, 8871 }, 8872 { 8873 name: "keyspace lock error", 8874 setup: func(t *testing.T, tt *testcase) { 8875 var cancel func() 8876 tt.ctx, cancel = context.WithTimeout(context.Background(), time.Millisecond*50) 8877 tt.ts = memorytopo.NewServer("zone1") 8878 testutil.AddShards(tt.ctx, t, tt.ts, &vtctldatapb.Shard{ 8879 Keyspace: "testkeyspace", 8880 Name: "-", 8881 Shard: &topodatapb.Shard{}, 8882 }) 8883 8884 _, unlock, err := tt.ts.LockKeyspace(tt.ctx, "testkeyspace", "test lock") 8885 require.NoError(t, err) 8886 tt.teardown = func(t *testing.T, tt *testcase) { 8887 var err error 8888 unlock(&err) 8889 assert.NoError(t, err) 8890 cancel() 8891 } 8892 }, 8893 req: &vtctldatapb.SetShardTabletControlRequest{ 8894 Keyspace: "testkeyspace", 8895 Shard: "-", 8896 DeniedTables: []string{"t1"}, 8897 TabletType: topodatapb.TabletType_REPLICA, 8898 }, 8899 shouldErr: true, 8900 }, 8901 } 8902 for _, tt := range tests { 8903 tt := tt 8904 t.Run(tt.name, func(t *testing.T) { 8905 t.Parallel() 8906 8907 if tt.setup != nil { 8908 tt.setup(t, tt) 8909 } 8910 if tt.teardown != nil { 8911 defer tt.teardown(t, tt) 8912 } 8913 8914 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 8915 return NewVtctldServer(ts) 8916 }) 8917 resp, err := vtctld.SetShardTabletControl(tt.ctx, tt.req) 8918 if tt.shouldErr { 8919 assert.Error(t, err) 8920 return 8921 } 8922 8923 assert.NoError(t, err) 8924 utils.MustMatch(t, tt.expected, resp) 8925 }) 8926 } 8927 } 8928 8929 func TestSetWritable(t *testing.T) { 8930 t.Parallel() 8931 8932 tests := []struct { 8933 name string 8934 cells []string 8935 tablets []*topodatapb.Tablet 8936 tmc testutil.TabletManagerClient 8937 req *vtctldatapb.SetWritableRequest 8938 shouldErr bool 8939 }{ 8940 { 8941 name: "writable ok", 8942 cells: []string{"zone1"}, 8943 tablets: []*topodatapb.Tablet{ 8944 { 8945 Alias: &topodatapb.TabletAlias{ 8946 Cell: "zone1", 8947 Uid: 100, 8948 }, 8949 Keyspace: "testkeyspace", 8950 Shard: "-", 8951 Type: topodatapb.TabletType_REPLICA, 8952 }, 8953 }, 8954 tmc: testutil.TabletManagerClient{ 8955 SetReadOnlyResults: map[string]error{ 8956 "zone1-0000000100": assert.AnError, 8957 }, 8958 SetReadWriteResults: map[string]error{ 8959 "zone1-0000000100": nil, 8960 }, 8961 }, 8962 req: &vtctldatapb.SetWritableRequest{ 8963 TabletAlias: &topodatapb.TabletAlias{ 8964 Cell: "zone1", 8965 Uid: 100, 8966 }, 8967 Writable: true, 8968 }, 8969 shouldErr: false, 8970 }, 8971 { 8972 name: "writable fail", 8973 cells: []string{"zone1"}, 8974 tablets: []*topodatapb.Tablet{ 8975 { 8976 Alias: &topodatapb.TabletAlias{ 8977 Cell: "zone1", 8978 Uid: 100, 8979 }, 8980 Keyspace: "testkeyspace", 8981 Shard: "-", 8982 Type: topodatapb.TabletType_RDONLY, 8983 }, 8984 }, 8985 tmc: testutil.TabletManagerClient{ 8986 SetReadOnlyResults: map[string]error{ 8987 "zone1-0000000100": nil, 8988 }, 8989 SetReadWriteResults: map[string]error{ 8990 "zone1-0000000100": assert.AnError, 8991 }, 8992 }, 8993 req: &vtctldatapb.SetWritableRequest{ 8994 TabletAlias: &topodatapb.TabletAlias{ 8995 Cell: "zone1", 8996 Uid: 100, 8997 }, 8998 Writable: true, 8999 }, 9000 shouldErr: true, 9001 }, 9002 { 9003 name: "read only ok", 9004 cells: []string{"zone1"}, 9005 tablets: []*topodatapb.Tablet{ 9006 { 9007 Alias: &topodatapb.TabletAlias{ 9008 Cell: "zone1", 9009 Uid: 100, 9010 }, 9011 Keyspace: "testkeyspace", 9012 Shard: "-", 9013 Type: topodatapb.TabletType_REPLICA, 9014 }, 9015 }, 9016 tmc: testutil.TabletManagerClient{ 9017 SetReadOnlyResults: map[string]error{ 9018 "zone1-0000000100": nil, 9019 }, 9020 SetReadWriteResults: map[string]error{ 9021 "zone1-0000000100": assert.AnError, 9022 }, 9023 }, 9024 req: &vtctldatapb.SetWritableRequest{ 9025 TabletAlias: &topodatapb.TabletAlias{ 9026 Cell: "zone1", 9027 Uid: 100, 9028 }, 9029 Writable: false, 9030 }, 9031 shouldErr: false, 9032 }, 9033 { 9034 name: "read only fail", 9035 cells: []string{"zone1"}, 9036 tablets: []*topodatapb.Tablet{ 9037 { 9038 Alias: &topodatapb.TabletAlias{ 9039 Cell: "zone1", 9040 Uid: 100, 9041 }, 9042 Keyspace: "testkeyspace", 9043 Shard: "-", 9044 Type: topodatapb.TabletType_PRIMARY, 9045 }, 9046 }, 9047 tmc: testutil.TabletManagerClient{ 9048 SetReadOnlyResults: map[string]error{ 9049 "zone1-0000000100": assert.AnError, 9050 }, 9051 SetReadWriteResults: map[string]error{ 9052 "zone1-0000000100": nil, 9053 }, 9054 }, 9055 req: &vtctldatapb.SetWritableRequest{ 9056 TabletAlias: &topodatapb.TabletAlias{ 9057 Cell: "zone1", 9058 Uid: 100, 9059 }, 9060 Writable: false, 9061 }, 9062 shouldErr: true, 9063 }, 9064 { 9065 name: "no such tablet", 9066 cells: []string{"zone1"}, 9067 tablets: []*topodatapb.Tablet{ 9068 { 9069 Alias: &topodatapb.TabletAlias{ 9070 Cell: "zone1", 9071 Uid: 100, 9072 }, 9073 Keyspace: "testkeyspace", 9074 Shard: "-", 9075 Type: topodatapb.TabletType_PRIMARY, 9076 }, 9077 }, 9078 req: &vtctldatapb.SetWritableRequest{ 9079 TabletAlias: &topodatapb.TabletAlias{ 9080 Cell: "zone2", 9081 Uid: 200, 9082 }, 9083 Writable: false, 9084 }, 9085 shouldErr: true, 9086 }, 9087 { 9088 name: "bad request", 9089 cells: []string{"zone1"}, 9090 tablets: []*topodatapb.Tablet{ 9091 { 9092 Alias: &topodatapb.TabletAlias{ 9093 Cell: "zone1", 9094 Uid: 100, 9095 }, 9096 Keyspace: "testkeyspace", 9097 Shard: "-", 9098 Type: topodatapb.TabletType_PRIMARY, 9099 }, 9100 }, 9101 req: &vtctldatapb.SetWritableRequest{}, 9102 shouldErr: true, 9103 }, 9104 } 9105 9106 ctx := context.Background() 9107 for _, tt := range tests { 9108 tt := tt 9109 t.Run(tt.name, func(t *testing.T) { 9110 t.Parallel() 9111 9112 ts := memorytopo.NewServer(tt.cells...) 9113 defer ts.Close() 9114 9115 testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) 9116 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 9117 return NewVtctldServer(ts) 9118 }) 9119 9120 _, err := vtctld.SetWritable(ctx, tt.req) 9121 if tt.shouldErr { 9122 assert.Error(t, err) 9123 return 9124 } 9125 9126 assert.NoError(t, err) 9127 }) 9128 } 9129 } 9130 9131 func TestShardReplicationAdd(t *testing.T) { 9132 t.Parallel() 9133 9134 ctx := context.Background() 9135 ts := memorytopo.NewServer("zone1") 9136 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 9137 return NewVtctldServer(ts) 9138 }) 9139 9140 tablets := []*topodatapb.Tablet{ 9141 { 9142 Alias: &topodatapb.TabletAlias{ 9143 Cell: "zone1", 9144 Uid: 100, 9145 }, 9146 Keyspace: "ks", 9147 Shard: "-", 9148 }, 9149 { 9150 Alias: &topodatapb.TabletAlias{ 9151 Cell: "zone1", 9152 Uid: 101, 9153 }, 9154 Keyspace: "ks", 9155 Shard: "-", 9156 }, 9157 } 9158 testutil.AddTablets(ctx, t, ts, nil, tablets...) 9159 9160 _, err := vtctld.ShardReplicationAdd(ctx, &vtctldatapb.ShardReplicationAddRequest{ 9161 Keyspace: "ks", 9162 Shard: "-", 9163 TabletAlias: &topodatapb.TabletAlias{ 9164 Cell: "zone1", 9165 Uid: 404, 9166 }, 9167 }) 9168 assert.NoError(t, err) 9169 9170 resp, err := vtctld.ShardReplicationFix(ctx, &vtctldatapb.ShardReplicationFixRequest{ 9171 Keyspace: "ks", 9172 Shard: "-", 9173 Cell: "zone1", 9174 }) 9175 require.NoError(t, err, "ShardReplicationFix failed") 9176 utils.MustMatch(t, &topodatapb.ShardReplicationError{ 9177 TabletAlias: &topodatapb.TabletAlias{ 9178 Cell: "zone1", 9179 Uid: 404, 9180 }, 9181 Type: topodatapb.ShardReplicationError_NOT_FOUND, 9182 }, resp.Error) 9183 } 9184 9185 func TestShardReplicationPositions(t *testing.T) { 9186 t.Parallel() 9187 9188 tests := []struct { 9189 name string 9190 ts *topo.Server 9191 tablets []*topodatapb.Tablet 9192 tmc tmclient.TabletManagerClient 9193 ctxTimeout time.Duration 9194 req *vtctldatapb.ShardReplicationPositionsRequest 9195 expected *vtctldatapb.ShardReplicationPositionsResponse 9196 shouldErr bool 9197 }{ 9198 { 9199 name: "success", 9200 ts: memorytopo.NewServer("zone1"), 9201 tablets: []*topodatapb.Tablet{ 9202 { 9203 Alias: &topodatapb.TabletAlias{ 9204 Cell: "zone1", 9205 Uid: 100, 9206 }, 9207 Keyspace: "testkeyspace", 9208 Shard: "-", 9209 Type: topodatapb.TabletType_PRIMARY, 9210 }, 9211 { 9212 Alias: &topodatapb.TabletAlias{ 9213 Cell: "zone1", 9214 Uid: 101, 9215 }, 9216 Keyspace: "testkeyspace", 9217 Shard: "-", 9218 Type: topodatapb.TabletType_REPLICA, 9219 }, 9220 }, 9221 tmc: &testutil.TabletManagerClient{ 9222 PrimaryPositionResults: map[string]struct { 9223 Position string 9224 Error error 9225 }{ 9226 "zone1-0000000100": { 9227 Position: "primary_tablet_position", 9228 }, 9229 }, 9230 ReplicationStatusResults: map[string]struct { 9231 Position *replicationdatapb.Status 9232 Error error 9233 }{ 9234 "zone1-0000000101": { 9235 Position: &replicationdatapb.Status{ 9236 Position: "replica_tablet_position", 9237 }, 9238 }, 9239 }, 9240 }, 9241 req: &vtctldatapb.ShardReplicationPositionsRequest{ 9242 Keyspace: "testkeyspace", 9243 Shard: "-", 9244 }, 9245 expected: &vtctldatapb.ShardReplicationPositionsResponse{ 9246 ReplicationStatuses: map[string]*replicationdatapb.Status{ 9247 "zone1-0000000100": { 9248 Position: "primary_tablet_position", 9249 }, 9250 "zone1-0000000101": { 9251 Position: "replica_tablet_position", 9252 }, 9253 }, 9254 TabletMap: map[string]*topodatapb.Tablet{ 9255 "zone1-0000000100": { 9256 Alias: &topodatapb.TabletAlias{ 9257 Cell: "zone1", 9258 Uid: 100, 9259 }, 9260 Keyspace: "testkeyspace", 9261 Shard: "-", 9262 Type: topodatapb.TabletType_PRIMARY, 9263 }, 9264 "zone1-0000000101": { 9265 Alias: &topodatapb.TabletAlias{ 9266 Cell: "zone1", 9267 Uid: 101, 9268 }, 9269 Keyspace: "testkeyspace", 9270 Shard: "-", 9271 Type: topodatapb.TabletType_REPLICA, 9272 }, 9273 }, 9274 }, 9275 shouldErr: false, 9276 }, 9277 { 9278 name: "timeouts are nonfatal", 9279 ts: memorytopo.NewServer("zone1"), 9280 tablets: []*topodatapb.Tablet{ 9281 { 9282 Alias: &topodatapb.TabletAlias{ 9283 Cell: "zone1", 9284 Uid: 100, 9285 }, 9286 Keyspace: "testkeyspace", 9287 Shard: "-", 9288 Type: topodatapb.TabletType_PRIMARY, 9289 }, 9290 { 9291 Alias: &topodatapb.TabletAlias{ 9292 Cell: "zone1", 9293 Uid: 101, 9294 }, 9295 Keyspace: "testkeyspace", 9296 Shard: "-", 9297 Type: topodatapb.TabletType_REPLICA, 9298 }, 9299 }, 9300 tmc: &testutil.TabletManagerClient{ 9301 PrimaryPositionDelays: map[string]time.Duration{ 9302 "zone1-0000000100": time.Millisecond * 100, 9303 }, 9304 PrimaryPositionResults: map[string]struct { 9305 Position string 9306 Error error 9307 }{ 9308 "zone1-0000000100": { 9309 Position: "primary_tablet_position", 9310 }, 9311 }, 9312 ReplicationStatusDelays: map[string]time.Duration{ 9313 "zone1-0000000101": time.Millisecond * 100, 9314 }, 9315 ReplicationStatusResults: map[string]struct { 9316 Position *replicationdatapb.Status 9317 Error error 9318 }{ 9319 "zone1-0000000101": { 9320 Position: &replicationdatapb.Status{ 9321 Position: "replica_tablet_position", 9322 }, 9323 }, 9324 }, 9325 }, 9326 ctxTimeout: time.Millisecond * 10, 9327 req: &vtctldatapb.ShardReplicationPositionsRequest{ 9328 Keyspace: "testkeyspace", 9329 Shard: "-", 9330 }, 9331 expected: &vtctldatapb.ShardReplicationPositionsResponse{ 9332 ReplicationStatuses: map[string]*replicationdatapb.Status{ 9333 "zone1-0000000100": nil, 9334 "zone1-0000000101": nil, 9335 }, 9336 TabletMap: map[string]*topodatapb.Tablet{ 9337 "zone1-0000000100": { 9338 Alias: &topodatapb.TabletAlias{ 9339 Cell: "zone1", 9340 Uid: 100, 9341 }, 9342 Keyspace: "testkeyspace", 9343 Shard: "-", 9344 Type: topodatapb.TabletType_PRIMARY, 9345 }, 9346 "zone1-0000000101": { 9347 Alias: &topodatapb.TabletAlias{ 9348 Cell: "zone1", 9349 Uid: 101, 9350 }, 9351 Keyspace: "testkeyspace", 9352 Shard: "-", 9353 Type: topodatapb.TabletType_REPLICA, 9354 }, 9355 }, 9356 }, 9357 shouldErr: false, 9358 }, 9359 { 9360 name: "other rpc errors are fatal", 9361 ts: memorytopo.NewServer("zone1"), 9362 tablets: []*topodatapb.Tablet{ 9363 { 9364 Alias: &topodatapb.TabletAlias{ 9365 Cell: "zone1", 9366 Uid: 100, 9367 }, 9368 Keyspace: "testkeyspace", 9369 Shard: "-", 9370 Type: topodatapb.TabletType_PRIMARY, 9371 }, 9372 { 9373 Alias: &topodatapb.TabletAlias{ 9374 Cell: "zone1", 9375 Uid: 101, 9376 }, 9377 Keyspace: "testkeyspace", 9378 Shard: "-", 9379 Type: topodatapb.TabletType_REPLICA, 9380 }, 9381 }, 9382 tmc: &testutil.TabletManagerClient{ 9383 PrimaryPositionResults: map[string]struct { 9384 Position string 9385 Error error 9386 }{ 9387 "zone1-0000000100": { 9388 Error: assert.AnError, 9389 }, 9390 }, 9391 ReplicationStatusResults: map[string]struct { 9392 Position *replicationdatapb.Status 9393 Error error 9394 }{ 9395 "zone1-0000000101": { 9396 Position: &replicationdatapb.Status{ 9397 Position: "replica_tablet_position", 9398 }, 9399 }, 9400 }, 9401 }, 9402 req: &vtctldatapb.ShardReplicationPositionsRequest{ 9403 Keyspace: "testkeyspace", 9404 Shard: "-", 9405 }, 9406 expected: nil, 9407 shouldErr: true, 9408 }, 9409 { 9410 name: "nonexistent shard", 9411 ts: memorytopo.NewServer("zone1"), 9412 req: &vtctldatapb.ShardReplicationPositionsRequest{ 9413 Keyspace: "testkeyspace", 9414 Shard: "-", 9415 }, 9416 expected: nil, 9417 shouldErr: true, 9418 }, 9419 } 9420 9421 for _, tt := range tests { 9422 tt := tt 9423 9424 t.Run(tt.name, func(t *testing.T) { 9425 t.Parallel() 9426 9427 ctx := context.Background() 9428 9429 testutil.AddTablets(ctx, t, tt.ts, &testutil.AddTabletOptions{ 9430 AlsoSetShardPrimary: true, 9431 SkipShardCreation: false, 9432 }, tt.tablets...) 9433 9434 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 9435 return NewVtctldServer(ts) 9436 }) 9437 9438 if tt.ctxTimeout > 0 { 9439 _ctx, cancel := context.WithTimeout(ctx, tt.ctxTimeout) 9440 defer cancel() 9441 9442 ctx = _ctx 9443 } 9444 9445 resp, err := vtctld.ShardReplicationPositions(ctx, tt.req) 9446 if tt.shouldErr { 9447 assert.Error(t, err) 9448 9449 return 9450 } 9451 9452 assert.NoError(t, err) 9453 utils.MustMatch(t, tt.expected, resp) 9454 }) 9455 } 9456 } 9457 9458 func TestShardReplicationRemove(t *testing.T) { 9459 t.Parallel() 9460 9461 ctx := context.Background() 9462 ts := memorytopo.NewServer("zone1") 9463 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 9464 return NewVtctldServer(ts) 9465 }) 9466 9467 tablets := []*topodatapb.Tablet{ 9468 { 9469 Alias: &topodatapb.TabletAlias{ 9470 Cell: "zone1", 9471 Uid: 100, 9472 }, 9473 Keyspace: "ks", 9474 Shard: "-", 9475 }, 9476 { 9477 Alias: &topodatapb.TabletAlias{ 9478 Cell: "zone1", 9479 Uid: 101, 9480 }, 9481 Keyspace: "ks", 9482 Shard: "-", 9483 }, 9484 } 9485 testutil.AddTablets(ctx, t, ts, nil, tablets...) 9486 9487 _, err := vtctld.ShardReplicationRemove(ctx, &vtctldatapb.ShardReplicationRemoveRequest{ 9488 Keyspace: "ks", 9489 Shard: "-", 9490 TabletAlias: &topodatapb.TabletAlias{ 9491 Cell: "zone1", 9492 Uid: 101, 9493 }, 9494 }) 9495 assert.NoError(t, err) 9496 9497 sri, err := ts.GetShardReplication(ctx, "zone1", "ks", "-") 9498 require.NoError(t, err, "GetShardReplication failed") 9499 9500 utils.MustMatch(t, sri.Nodes, []*topodatapb.ShardReplication_Node{{ 9501 TabletAlias: &topodatapb.TabletAlias{ 9502 Cell: "zone1", 9503 Uid: 100, 9504 }, 9505 }}) 9506 } 9507 9508 func TestSourceShardAdd(t *testing.T) { 9509 t.Parallel() 9510 9511 tests := []struct { 9512 name string 9513 shards []*vtctldatapb.Shard 9514 topoIsLocked bool 9515 req *vtctldatapb.SourceShardAddRequest 9516 expected *vtctldatapb.SourceShardAddResponse 9517 shouldErr bool 9518 assertion func(ctx context.Context, t *testing.T, ts *topo.Server) 9519 }{ 9520 { 9521 name: "ok", 9522 shards: []*vtctldatapb.Shard{ 9523 { 9524 Keyspace: "ks", 9525 Name: "-", 9526 }, 9527 }, 9528 req: &vtctldatapb.SourceShardAddRequest{ 9529 Keyspace: "ks", 9530 Shard: "-", 9531 Uid: 1, 9532 SourceKeyspace: "otherks", 9533 SourceShard: "-80", 9534 }, 9535 expected: &vtctldatapb.SourceShardAddResponse{ 9536 Shard: &topodatapb.Shard{ 9537 IsPrimaryServing: true, 9538 KeyRange: &topodatapb.KeyRange{}, 9539 SourceShards: []*topodatapb.Shard_SourceShard{ 9540 { 9541 Uid: 1, 9542 Keyspace: "otherks", 9543 Shard: "-80", 9544 }, 9545 }, 9546 }, 9547 }, 9548 }, 9549 { 9550 name: "uid already used", 9551 shards: []*vtctldatapb.Shard{ 9552 { 9553 Keyspace: "ks", 9554 Name: "-", 9555 Shard: &topodatapb.Shard{ 9556 SourceShards: []*topodatapb.Shard_SourceShard{ 9557 { 9558 Uid: 1, 9559 Keyspace: "otherks", 9560 Shard: "-80", 9561 }, 9562 }, 9563 }, 9564 }, 9565 }, 9566 req: &vtctldatapb.SourceShardAddRequest{ 9567 Keyspace: "ks", 9568 Shard: "-", 9569 Uid: 1, 9570 SourceKeyspace: "otherks", 9571 SourceShard: "80-", 9572 }, 9573 expected: &vtctldatapb.SourceShardAddResponse{}, 9574 assertion: func(ctx context.Context, t *testing.T, ts *topo.Server) { 9575 si, err := ts.GetShard(ctx, "ks", "-") 9576 require.NoError(t, err, "failed to get shard ks/-") 9577 utils.MustMatch(t, []*topodatapb.Shard_SourceShard{ 9578 { 9579 Uid: 1, 9580 Keyspace: "otherks", 9581 Shard: "-80", 9582 }, 9583 }, si.SourceShards, "SourceShards should not have changed") 9584 }, 9585 }, 9586 { 9587 name: "cannot lock keyspace", 9588 shards: []*vtctldatapb.Shard{ 9589 { 9590 Keyspace: "ks", 9591 Name: "-", 9592 Shard: &topodatapb.Shard{ 9593 SourceShards: []*topodatapb.Shard_SourceShard{ 9594 { 9595 Uid: 1, 9596 Keyspace: "otherks", 9597 Shard: "-80", 9598 }, 9599 }, 9600 }, 9601 }, 9602 }, 9603 topoIsLocked: true, 9604 req: &vtctldatapb.SourceShardAddRequest{ 9605 Keyspace: "ks", 9606 Shard: "-", 9607 Uid: 1, 9608 SourceKeyspace: "otherks", 9609 SourceShard: "80-", 9610 }, 9611 shouldErr: true, 9612 }, 9613 } 9614 9615 for _, tt := range tests { 9616 tt := tt 9617 t.Run(tt.name, func(t *testing.T) { 9618 t.Parallel() 9619 9620 ctx := context.Background() 9621 ts := memorytopo.NewServer("zone1") 9622 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 9623 return NewVtctldServer(ts) 9624 }) 9625 9626 testutil.AddShards(ctx, t, ts, tt.shards...) 9627 if tt.topoIsLocked { 9628 lctx, unlock, lerr := ts.LockKeyspace(ctx, tt.req.Keyspace, "test lock") 9629 require.NoError(t, lerr, "failed to lock %s for test setup", tt.req.Keyspace) 9630 9631 defer func() { 9632 var err error 9633 unlock(&err) 9634 assert.NoError(t, err, "failed to unlock %s after test", tt.req.Keyspace) 9635 }() 9636 9637 ctx = lctx 9638 } 9639 9640 resp, err := vtctld.SourceShardAdd(ctx, tt.req) 9641 if tt.shouldErr { 9642 assert.Error(t, err) 9643 return 9644 } 9645 9646 require.NoError(t, err) 9647 utils.MustMatch(t, tt.expected, resp) 9648 if tt.assertion != nil { 9649 func() { 9650 t.Helper() 9651 tt.assertion(ctx, t, ts) 9652 }() 9653 } 9654 }) 9655 } 9656 } 9657 9658 func TestSourceShardDelete(t *testing.T) { 9659 t.Parallel() 9660 9661 tests := []struct { 9662 name string 9663 shards []*vtctldatapb.Shard 9664 topoIsLocked bool 9665 req *vtctldatapb.SourceShardDeleteRequest 9666 expected *vtctldatapb.SourceShardDeleteResponse 9667 shouldErr bool 9668 assertion func(ctx context.Context, t *testing.T, ts *topo.Server) 9669 }{ 9670 { 9671 name: "ok", 9672 shards: []*vtctldatapb.Shard{ 9673 { 9674 Keyspace: "ks", 9675 Name: "-", 9676 Shard: &topodatapb.Shard{ 9677 SourceShards: []*topodatapb.Shard_SourceShard{ 9678 { 9679 Uid: 1, 9680 Keyspace: "otherks", 9681 Shard: "-80", 9682 }, 9683 }, 9684 }, 9685 }, 9686 }, 9687 req: &vtctldatapb.SourceShardDeleteRequest{ 9688 Keyspace: "ks", 9689 Shard: "-", 9690 Uid: 1, 9691 }, 9692 expected: &vtctldatapb.SourceShardDeleteResponse{ 9693 Shard: &topodatapb.Shard{}, 9694 }, 9695 }, 9696 { 9697 name: "no SourceShard with uid", 9698 shards: []*vtctldatapb.Shard{ 9699 { 9700 Keyspace: "ks", 9701 Name: "-", 9702 Shard: &topodatapb.Shard{ 9703 SourceShards: []*topodatapb.Shard_SourceShard{ 9704 { 9705 Uid: 1, 9706 Keyspace: "otherks", 9707 Shard: "-80", 9708 }, 9709 }, 9710 }, 9711 }, 9712 }, 9713 req: &vtctldatapb.SourceShardDeleteRequest{ 9714 Keyspace: "ks", 9715 Shard: "-", 9716 Uid: 2, 9717 }, 9718 expected: &vtctldatapb.SourceShardDeleteResponse{}, 9719 assertion: func(ctx context.Context, t *testing.T, ts *topo.Server) { 9720 si, err := ts.GetShard(ctx, "ks", "-") 9721 require.NoError(t, err, "failed to get shard ks/-") 9722 utils.MustMatch(t, []*topodatapb.Shard_SourceShard{ 9723 { 9724 Uid: 1, 9725 Keyspace: "otherks", 9726 Shard: "-80", 9727 }, 9728 }, si.SourceShards, "SourceShards should not have changed") 9729 }, 9730 }, 9731 { 9732 name: "cannot lock keyspace", 9733 shards: []*vtctldatapb.Shard{ 9734 { 9735 Keyspace: "ks", 9736 Name: "-", 9737 }, 9738 }, 9739 topoIsLocked: true, 9740 req: &vtctldatapb.SourceShardDeleteRequest{ 9741 Keyspace: "ks", 9742 Shard: "-", 9743 Uid: 1, 9744 }, 9745 shouldErr: true, 9746 }, 9747 } 9748 9749 for _, tt := range tests { 9750 tt := tt 9751 t.Run(tt.name, func(t *testing.T) { 9752 t.Parallel() 9753 9754 ctx := context.Background() 9755 ts := memorytopo.NewServer("zone1") 9756 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 9757 return NewVtctldServer(ts) 9758 }) 9759 9760 testutil.AddShards(ctx, t, ts, tt.shards...) 9761 if tt.topoIsLocked { 9762 lctx, unlock, lerr := ts.LockKeyspace(ctx, tt.req.Keyspace, "test lock") 9763 require.NoError(t, lerr, "failed to lock %s for test setup", tt.req.Keyspace) 9764 9765 defer func() { 9766 var err error 9767 unlock(&err) 9768 assert.NoError(t, err, "failed to unlock %s after test", tt.req.Keyspace) 9769 }() 9770 9771 ctx = lctx 9772 } 9773 9774 resp, err := vtctld.SourceShardDelete(ctx, tt.req) 9775 if tt.shouldErr { 9776 assert.Error(t, err) 9777 return 9778 } 9779 9780 require.NoError(t, err) 9781 utils.MustMatch(t, tt.expected, resp) 9782 if tt.assertion != nil { 9783 func() { 9784 t.Helper() 9785 tt.assertion(ctx, t, ts) 9786 }() 9787 } 9788 }) 9789 } 9790 } 9791 9792 func TestStartReplication(t *testing.T) { 9793 t.Parallel() 9794 9795 tests := []struct { 9796 name string 9797 cells []string 9798 tablets []*topodatapb.Tablet 9799 tmc testutil.TabletManagerClient 9800 req *vtctldatapb.StartReplicationRequest 9801 shouldErr bool 9802 }{ 9803 { 9804 name: "ok", 9805 cells: []string{"zone1"}, 9806 tablets: []*topodatapb.Tablet{ 9807 { 9808 Alias: &topodatapb.TabletAlias{ 9809 Cell: "zone1", 9810 Uid: 100, 9811 }, 9812 Keyspace: "testkeyspace", 9813 Shard: "-", 9814 Type: topodatapb.TabletType_REPLICA, 9815 }, { 9816 Alias: &topodatapb.TabletAlias{ 9817 Cell: "zone1", 9818 Uid: 101, 9819 }, 9820 Keyspace: "testkeyspace", 9821 Shard: "-", 9822 Type: topodatapb.TabletType_PRIMARY, 9823 }, 9824 }, 9825 tmc: testutil.TabletManagerClient{ 9826 StartReplicationResults: map[string]error{ 9827 "zone1-0000000100": nil, 9828 }, 9829 }, 9830 req: &vtctldatapb.StartReplicationRequest{ 9831 TabletAlias: &topodatapb.TabletAlias{ 9832 Cell: "zone1", 9833 Uid: 100, 9834 }, 9835 }, 9836 }, 9837 { 9838 name: "fail", 9839 cells: []string{"zone1"}, 9840 tablets: []*topodatapb.Tablet{ 9841 { 9842 Alias: &topodatapb.TabletAlias{ 9843 Cell: "zone1", 9844 Uid: 100, 9845 }, 9846 Keyspace: "testkeyspace", 9847 Shard: "-", 9848 Type: topodatapb.TabletType_REPLICA, 9849 }, { 9850 Alias: &topodatapb.TabletAlias{ 9851 Cell: "zone1", 9852 Uid: 101, 9853 }, 9854 Keyspace: "testkeyspace", 9855 Shard: "-", 9856 Type: topodatapb.TabletType_PRIMARY, 9857 }, 9858 }, 9859 tmc: testutil.TabletManagerClient{ 9860 StartReplicationResults: map[string]error{ 9861 "zone1-0000000100": assert.AnError, 9862 }, 9863 }, 9864 req: &vtctldatapb.StartReplicationRequest{ 9865 TabletAlias: &topodatapb.TabletAlias{ 9866 Cell: "zone1", 9867 Uid: 100, 9868 }, 9869 }, 9870 shouldErr: true, 9871 }, 9872 { 9873 name: "no such tablet", 9874 cells: []string{"zone1"}, 9875 tablets: []*topodatapb.Tablet{ 9876 { 9877 Alias: &topodatapb.TabletAlias{ 9878 Cell: "zone1", 9879 Uid: 100, 9880 }, 9881 Keyspace: "testkeyspace", 9882 Shard: "-", 9883 Type: topodatapb.TabletType_REPLICA, 9884 }, { 9885 Alias: &topodatapb.TabletAlias{ 9886 Cell: "zone1", 9887 Uid: 101, 9888 }, 9889 Keyspace: "testkeyspace", 9890 Shard: "-", 9891 Type: topodatapb.TabletType_PRIMARY, 9892 }, 9893 }, 9894 tmc: testutil.TabletManagerClient{ 9895 StartReplicationResults: map[string]error{ 9896 "zone1-0000000100": nil, 9897 }, 9898 }, 9899 req: &vtctldatapb.StartReplicationRequest{ 9900 TabletAlias: &topodatapb.TabletAlias{ 9901 Cell: "zone2", 9902 Uid: 200, 9903 }, 9904 }, 9905 shouldErr: true, 9906 }, 9907 { 9908 name: "bad request", 9909 cells: []string{"zone1"}, 9910 tablets: []*topodatapb.Tablet{ 9911 { 9912 Alias: &topodatapb.TabletAlias{ 9913 Cell: "zone1", 9914 Uid: 100, 9915 }, 9916 Keyspace: "testkeyspace", 9917 Shard: "-", 9918 Type: topodatapb.TabletType_REPLICA, 9919 }, { 9920 Alias: &topodatapb.TabletAlias{ 9921 Cell: "zone1", 9922 Uid: 101, 9923 }, 9924 Keyspace: "testkeyspace", 9925 Shard: "-", 9926 Type: topodatapb.TabletType_PRIMARY, 9927 }, 9928 }, 9929 req: &vtctldatapb.StartReplicationRequest{}, 9930 shouldErr: true, 9931 }, 9932 } 9933 9934 ctx := context.Background() 9935 for _, tt := range tests { 9936 tt := tt 9937 t.Run(tt.name, func(t *testing.T) { 9938 t.Parallel() 9939 9940 ts := memorytopo.NewServer(tt.cells...) 9941 defer ts.Close() 9942 9943 testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ 9944 AlsoSetShardPrimary: true, 9945 }, tt.tablets...) 9946 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 9947 return NewVtctldServer(ts) 9948 }) 9949 9950 _, err := vtctld.StartReplication(ctx, tt.req) 9951 if tt.shouldErr { 9952 assert.Error(t, err) 9953 return 9954 } 9955 9956 assert.NoError(t, err) 9957 }) 9958 } 9959 } 9960 9961 func TestStopReplication(t *testing.T) { 9962 t.Parallel() 9963 9964 tests := []struct { 9965 name string 9966 cells []string 9967 tablets []*topodatapb.Tablet 9968 tmc testutil.TabletManagerClient 9969 req *vtctldatapb.StopReplicationRequest 9970 shouldErr bool 9971 }{ 9972 { 9973 name: "ok", 9974 cells: []string{"zone1"}, 9975 tablets: []*topodatapb.Tablet{ 9976 { 9977 Alias: &topodatapb.TabletAlias{ 9978 Cell: "zone1", 9979 Uid: 100, 9980 }, 9981 Keyspace: "testkeyspace", 9982 Shard: "-", 9983 Type: topodatapb.TabletType_REPLICA, 9984 }, 9985 }, 9986 tmc: testutil.TabletManagerClient{ 9987 StopReplicationResults: map[string]error{ 9988 "zone1-0000000100": nil, 9989 }, 9990 }, 9991 req: &vtctldatapb.StopReplicationRequest{ 9992 TabletAlias: &topodatapb.TabletAlias{ 9993 Cell: "zone1", 9994 Uid: 100, 9995 }, 9996 }, 9997 }, 9998 { 9999 name: "fail", 10000 cells: []string{"zone1"}, 10001 tablets: []*topodatapb.Tablet{ 10002 { 10003 Alias: &topodatapb.TabletAlias{ 10004 Cell: "zone1", 10005 Uid: 100, 10006 }, 10007 Keyspace: "testkeyspace", 10008 Shard: "-", 10009 Type: topodatapb.TabletType_REPLICA, 10010 }, 10011 }, 10012 tmc: testutil.TabletManagerClient{ 10013 StopReplicationResults: map[string]error{ 10014 "zone1-0000000100": assert.AnError, 10015 }, 10016 }, 10017 req: &vtctldatapb.StopReplicationRequest{ 10018 TabletAlias: &topodatapb.TabletAlias{ 10019 Cell: "zone1", 10020 Uid: 100, 10021 }, 10022 }, 10023 shouldErr: true, 10024 }, 10025 { 10026 name: "no such tablet", 10027 cells: []string{"zone1"}, 10028 tablets: []*topodatapb.Tablet{ 10029 { 10030 Alias: &topodatapb.TabletAlias{ 10031 Cell: "zone1", 10032 Uid: 100, 10033 }, 10034 Keyspace: "testkeyspace", 10035 Shard: "-", 10036 Type: topodatapb.TabletType_REPLICA, 10037 }, 10038 }, 10039 tmc: testutil.TabletManagerClient{ 10040 StopReplicationResults: map[string]error{ 10041 "zone1-0000000100": nil, 10042 }, 10043 }, 10044 req: &vtctldatapb.StopReplicationRequest{ 10045 TabletAlias: &topodatapb.TabletAlias{ 10046 Cell: "zone2", 10047 Uid: 200, 10048 }, 10049 }, 10050 shouldErr: true, 10051 }, 10052 { 10053 name: "bad request", 10054 cells: []string{"zone1"}, 10055 tablets: []*topodatapb.Tablet{ 10056 { 10057 Alias: &topodatapb.TabletAlias{ 10058 Cell: "zone1", 10059 Uid: 100, 10060 }, 10061 Keyspace: "testkeyspace", 10062 Shard: "-", 10063 Type: topodatapb.TabletType_REPLICA, 10064 }, 10065 }, 10066 req: &vtctldatapb.StopReplicationRequest{}, 10067 shouldErr: true, 10068 }, 10069 } 10070 10071 ctx := context.Background() 10072 for _, tt := range tests { 10073 tt := tt 10074 t.Run(tt.name, func(t *testing.T) { 10075 t.Parallel() 10076 10077 ts := memorytopo.NewServer(tt.cells...) 10078 defer ts.Close() 10079 10080 testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) 10081 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 10082 return NewVtctldServer(ts) 10083 }) 10084 10085 _, err := vtctld.StopReplication(ctx, tt.req) 10086 if tt.shouldErr { 10087 assert.Error(t, err) 10088 return 10089 } 10090 10091 assert.NoError(t, err) 10092 }) 10093 } 10094 } 10095 10096 func TestTabletExternallyReparented(t *testing.T) { 10097 t.Parallel() 10098 10099 tests := []struct { 10100 name string 10101 topo []*topodatapb.Tablet 10102 topoErr error 10103 tmcHasNoTopo bool 10104 req *vtctldatapb.TabletExternallyReparentedRequest 10105 expected *vtctldatapb.TabletExternallyReparentedResponse 10106 shouldErr bool 10107 expectedTopo []*topodatapb.Tablet 10108 }{ 10109 { 10110 name: "success", 10111 topo: []*topodatapb.Tablet{ 10112 { 10113 Alias: &topodatapb.TabletAlias{ 10114 Cell: "zone1", 10115 Uid: 100, 10116 }, 10117 Type: topodatapb.TabletType_PRIMARY, 10118 Keyspace: "testkeyspace", 10119 Shard: "-", 10120 PrimaryTermStartTime: &vttime.Time{ 10121 Seconds: 1000, 10122 }, 10123 }, 10124 { 10125 Alias: &topodatapb.TabletAlias{ 10126 Cell: "zone2", 10127 Uid: 200, 10128 }, 10129 Type: topodatapb.TabletType_REPLICA, 10130 Keyspace: "testkeyspace", 10131 Shard: "-", 10132 }, 10133 { 10134 Alias: &topodatapb.TabletAlias{ 10135 Cell: "zone3", 10136 Uid: 300, 10137 }, 10138 Type: topodatapb.TabletType_REPLICA, 10139 Keyspace: "testkeyspace", 10140 Shard: "-", 10141 }, 10142 }, 10143 topoErr: nil, 10144 req: &vtctldatapb.TabletExternallyReparentedRequest{ 10145 Tablet: &topodatapb.TabletAlias{ 10146 Cell: "zone2", 10147 Uid: 200, 10148 }, 10149 }, 10150 expected: &vtctldatapb.TabletExternallyReparentedResponse{ 10151 Keyspace: "testkeyspace", 10152 Shard: "-", 10153 NewPrimary: &topodatapb.TabletAlias{ 10154 Cell: "zone2", 10155 Uid: 200, 10156 }, 10157 OldPrimary: &topodatapb.TabletAlias{ 10158 Cell: "zone1", 10159 Uid: 100, 10160 }, 10161 }, 10162 shouldErr: false, 10163 // NOTE: this seems weird, right? Why is the old primary still a 10164 // PRIMARY, and why is the new primary's term start 0,0? Well, our 10165 // test client implementation is a little incomplete. See 10166 // ./testutil/test_tmclient.go for reference. 10167 expectedTopo: []*topodatapb.Tablet{ 10168 { 10169 Alias: &topodatapb.TabletAlias{ 10170 Cell: "zone1", 10171 Uid: 100, 10172 }, 10173 Type: topodatapb.TabletType_PRIMARY, 10174 Keyspace: "testkeyspace", 10175 Shard: "-", 10176 PrimaryTermStartTime: &vttime.Time{ 10177 Seconds: 1000, 10178 }, 10179 }, 10180 { 10181 Alias: &topodatapb.TabletAlias{ 10182 Cell: "zone2", 10183 Uid: 200, 10184 }, 10185 Type: topodatapb.TabletType_UNKNOWN, 10186 Keyspace: "testkeyspace", 10187 Shard: "-", 10188 PrimaryTermStartTime: &vttime.Time{}, 10189 }, 10190 { 10191 Alias: &topodatapb.TabletAlias{ 10192 Cell: "zone3", 10193 Uid: 300, 10194 }, 10195 Type: topodatapb.TabletType_REPLICA, 10196 Keyspace: "testkeyspace", 10197 Shard: "-", 10198 }, 10199 }, 10200 }, 10201 { 10202 name: "tablet is nil", 10203 topo: nil, 10204 topoErr: nil, 10205 req: &vtctldatapb.TabletExternallyReparentedRequest{ 10206 Tablet: nil, 10207 }, 10208 expected: nil, 10209 shouldErr: true, 10210 expectedTopo: nil, 10211 }, 10212 { 10213 name: "topo is down", 10214 topo: []*topodatapb.Tablet{ 10215 { 10216 Alias: &topodatapb.TabletAlias{ 10217 Cell: "zone1", 10218 Uid: 100, 10219 }, 10220 Type: topodatapb.TabletType_PRIMARY, 10221 Keyspace: "testkeyspace", 10222 Shard: "-", 10223 PrimaryTermStartTime: &vttime.Time{ 10224 Seconds: 1000, 10225 }, 10226 }, 10227 { 10228 Alias: &topodatapb.TabletAlias{ 10229 Cell: "zone2", 10230 Uid: 200, 10231 }, 10232 Type: topodatapb.TabletType_REPLICA, 10233 Keyspace: "testkeyspace", 10234 Shard: "-", 10235 }, 10236 { 10237 Alias: &topodatapb.TabletAlias{ 10238 Cell: "zone3", 10239 Uid: 300, 10240 }, 10241 Type: topodatapb.TabletType_REPLICA, 10242 Keyspace: "testkeyspace", 10243 Shard: "-", 10244 }, 10245 }, 10246 topoErr: assert.AnError, 10247 req: &vtctldatapb.TabletExternallyReparentedRequest{ 10248 Tablet: &topodatapb.TabletAlias{ 10249 Cell: "zone2", 10250 Uid: 200, 10251 }, 10252 }, 10253 expected: nil, 10254 shouldErr: true, 10255 expectedTopo: []*topodatapb.Tablet{ 10256 { 10257 Alias: &topodatapb.TabletAlias{ 10258 Cell: "zone1", 10259 Uid: 100, 10260 }, 10261 Type: topodatapb.TabletType_PRIMARY, 10262 Keyspace: "testkeyspace", 10263 Shard: "-", 10264 PrimaryTermStartTime: &vttime.Time{ 10265 Seconds: 1000, 10266 }, 10267 }, 10268 { 10269 Alias: &topodatapb.TabletAlias{ 10270 Cell: "zone2", 10271 Uid: 200, 10272 }, 10273 Type: topodatapb.TabletType_REPLICA, 10274 Keyspace: "testkeyspace", 10275 Shard: "-", 10276 }, 10277 { 10278 Alias: &topodatapb.TabletAlias{ 10279 Cell: "zone3", 10280 Uid: 300, 10281 }, 10282 Type: topodatapb.TabletType_REPLICA, 10283 Keyspace: "testkeyspace", 10284 Shard: "-", 10285 }, 10286 }, 10287 }, 10288 { 10289 name: "tablet is already primary", 10290 topo: []*topodatapb.Tablet{ 10291 { 10292 Alias: &topodatapb.TabletAlias{ 10293 Cell: "zone1", 10294 Uid: 100, 10295 }, 10296 Type: topodatapb.TabletType_PRIMARY, 10297 Keyspace: "testkeyspace", 10298 Shard: "-", 10299 PrimaryTermStartTime: &vttime.Time{ 10300 Seconds: 1000, 10301 }, 10302 }, 10303 { 10304 Alias: &topodatapb.TabletAlias{ 10305 Cell: "zone2", 10306 Uid: 200, 10307 }, 10308 Type: topodatapb.TabletType_REPLICA, 10309 Keyspace: "testkeyspace", 10310 Shard: "-", 10311 }, 10312 { 10313 Alias: &topodatapb.TabletAlias{ 10314 Cell: "zone3", 10315 Uid: 300, 10316 }, 10317 Type: topodatapb.TabletType_REPLICA, 10318 Keyspace: "testkeyspace", 10319 Shard: "-", 10320 }, 10321 }, 10322 topoErr: nil, 10323 req: &vtctldatapb.TabletExternallyReparentedRequest{ 10324 Tablet: &topodatapb.TabletAlias{ 10325 Cell: "zone1", 10326 Uid: 100, 10327 }, 10328 }, 10329 expected: &vtctldatapb.TabletExternallyReparentedResponse{ 10330 Keyspace: "testkeyspace", 10331 Shard: "-", 10332 NewPrimary: &topodatapb.TabletAlias{ 10333 Cell: "zone1", 10334 Uid: 100, 10335 }, 10336 OldPrimary: &topodatapb.TabletAlias{ 10337 Cell: "zone1", 10338 Uid: 100, 10339 }, 10340 }, 10341 shouldErr: false, 10342 expectedTopo: []*topodatapb.Tablet{ 10343 { 10344 Alias: &topodatapb.TabletAlias{ 10345 Cell: "zone1", 10346 Uid: 100, 10347 }, 10348 Type: topodatapb.TabletType_PRIMARY, 10349 Keyspace: "testkeyspace", 10350 Shard: "-", 10351 PrimaryTermStartTime: &vttime.Time{ 10352 Seconds: 1000, 10353 }, 10354 }, 10355 { 10356 Alias: &topodatapb.TabletAlias{ 10357 Cell: "zone2", 10358 Uid: 200, 10359 }, 10360 Type: topodatapb.TabletType_REPLICA, 10361 Keyspace: "testkeyspace", 10362 Shard: "-", 10363 }, 10364 { 10365 Alias: &topodatapb.TabletAlias{ 10366 Cell: "zone3", 10367 Uid: 300, 10368 }, 10369 Type: topodatapb.TabletType_REPLICA, 10370 Keyspace: "testkeyspace", 10371 Shard: "-", 10372 }, 10373 }, 10374 }, 10375 { 10376 name: "cannot change tablet type", 10377 topo: []*topodatapb.Tablet{ 10378 { 10379 Alias: &topodatapb.TabletAlias{ 10380 Cell: "zone1", 10381 Uid: 100, 10382 }, 10383 Type: topodatapb.TabletType_PRIMARY, 10384 Keyspace: "testkeyspace", 10385 Shard: "-", 10386 PrimaryTermStartTime: &vttime.Time{ 10387 Seconds: 1000, 10388 }, 10389 }, 10390 { 10391 Alias: &topodatapb.TabletAlias{ 10392 Cell: "zone2", 10393 Uid: 200, 10394 }, 10395 Type: topodatapb.TabletType_REPLICA, 10396 Keyspace: "testkeyspace", 10397 Shard: "-", 10398 }, 10399 { 10400 Alias: &topodatapb.TabletAlias{ 10401 Cell: "zone3", 10402 Uid: 300, 10403 }, 10404 Type: topodatapb.TabletType_REPLICA, 10405 Keyspace: "testkeyspace", 10406 Shard: "-", 10407 }, 10408 }, 10409 topoErr: nil, 10410 tmcHasNoTopo: true, 10411 req: &vtctldatapb.TabletExternallyReparentedRequest{ 10412 Tablet: &topodatapb.TabletAlias{ 10413 Cell: "zone2", 10414 Uid: 200, 10415 }, 10416 }, 10417 expected: nil, 10418 shouldErr: true, 10419 expectedTopo: []*topodatapb.Tablet{ 10420 { 10421 Alias: &topodatapb.TabletAlias{ 10422 Cell: "zone1", 10423 Uid: 100, 10424 }, 10425 Type: topodatapb.TabletType_PRIMARY, 10426 Keyspace: "testkeyspace", 10427 Shard: "-", 10428 PrimaryTermStartTime: &vttime.Time{ 10429 Seconds: 1000, 10430 }, 10431 }, 10432 { 10433 Alias: &topodatapb.TabletAlias{ 10434 Cell: "zone2", 10435 Uid: 200, 10436 }, 10437 Type: topodatapb.TabletType_REPLICA, 10438 Keyspace: "testkeyspace", 10439 Shard: "-", 10440 }, 10441 { 10442 Alias: &topodatapb.TabletAlias{ 10443 Cell: "zone3", 10444 Uid: 300, 10445 }, 10446 Type: topodatapb.TabletType_REPLICA, 10447 Keyspace: "testkeyspace", 10448 Shard: "-", 10449 }, 10450 }, 10451 }, 10452 } 10453 10454 for _, tt := range tests { 10455 tt := tt 10456 10457 t.Run(tt.name, func(t *testing.T) { 10458 t.Parallel() 10459 10460 cells := []string{"zone1", "zone2", "zone3"} 10461 10462 ctx := context.Background() 10463 ts, topofactory := memorytopo.NewServerAndFactory(cells...) 10464 tmc := testutil.TabletManagerClient{ 10465 TopoServer: ts, 10466 } 10467 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 10468 return NewVtctldServer(ts) 10469 }) 10470 10471 if tt.tmcHasNoTopo { 10472 // For certain test cases, we want specifically just the 10473 // ChangeType call to fail, which is why we rely on a separate 10474 // bool rather than using tt.topoErr. 10475 tmc.TopoServer = nil 10476 } 10477 10478 testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ 10479 AlsoSetShardPrimary: true, 10480 }, tt.topo...) 10481 10482 if tt.topoErr != nil { 10483 topofactory.SetError(tt.topoErr) 10484 } 10485 10486 if tt.expectedTopo != nil { 10487 // assert on expectedTopo state when we've fininished the rest 10488 // of the test. 10489 defer func() { 10490 topofactory.SetError(nil) 10491 10492 ctx, cancel := context.WithTimeout(ctx, time.Millisecond*10) 10493 defer cancel() 10494 10495 resp, err := vtctld.GetTablets(ctx, &vtctldatapb.GetTabletsRequest{}) 10496 require.NoError(t, err, "cannot get all tablets in the topo") 10497 testutil.AssertSameTablets(t, tt.expectedTopo, resp.Tablets) 10498 }() 10499 } 10500 10501 resp, err := vtctld.TabletExternallyReparented(ctx, tt.req) 10502 if tt.shouldErr { 10503 assert.Error(t, err) 10504 return 10505 } 10506 10507 assert.NoError(t, err) 10508 utils.MustMatch(t, tt.expected, resp) 10509 }) 10510 } 10511 } 10512 10513 func TestUpdateCellInfo(t *testing.T) { 10514 t.Parallel() 10515 10516 ctx := context.Background() 10517 tests := []struct { 10518 name string 10519 cells map[string]*topodatapb.CellInfo 10520 forceTopoError bool 10521 req *vtctldatapb.UpdateCellInfoRequest 10522 expected *vtctldatapb.UpdateCellInfoResponse 10523 shouldErr bool 10524 }{ 10525 { 10526 name: "update", 10527 cells: map[string]*topodatapb.CellInfo{ 10528 "zone1": { 10529 ServerAddress: ":1111", 10530 Root: "/zone1", 10531 }, 10532 }, 10533 req: &vtctldatapb.UpdateCellInfoRequest{ 10534 Name: "zone1", 10535 CellInfo: &topodatapb.CellInfo{ 10536 ServerAddress: ":0101", 10537 Root: "/zones/zone1", 10538 }, 10539 }, 10540 expected: &vtctldatapb.UpdateCellInfoResponse{ 10541 Name: "zone1", 10542 CellInfo: &topodatapb.CellInfo{ 10543 ServerAddress: ":0101", 10544 Root: "/zones/zone1", 10545 }, 10546 }, 10547 }, 10548 { 10549 name: "partial update", 10550 cells: map[string]*topodatapb.CellInfo{ 10551 "zone1": { 10552 ServerAddress: ":1111", 10553 Root: "/zone1", 10554 }, 10555 }, 10556 req: &vtctldatapb.UpdateCellInfoRequest{ 10557 Name: "zone1", 10558 CellInfo: &topodatapb.CellInfo{ 10559 Root: "/zones/zone1", 10560 }, 10561 }, 10562 expected: &vtctldatapb.UpdateCellInfoResponse{ 10563 Name: "zone1", 10564 CellInfo: &topodatapb.CellInfo{ 10565 ServerAddress: ":1111", 10566 Root: "/zones/zone1", 10567 }, 10568 }, 10569 }, 10570 { 10571 name: "no update", 10572 cells: map[string]*topodatapb.CellInfo{ 10573 "zone1": { 10574 ServerAddress: ":1111", 10575 Root: "/zone1", 10576 }, 10577 }, 10578 req: &vtctldatapb.UpdateCellInfoRequest{ 10579 Name: "zone1", 10580 CellInfo: &topodatapb.CellInfo{ 10581 Root: "/zone1", 10582 }, 10583 }, 10584 expected: &vtctldatapb.UpdateCellInfoResponse{ 10585 Name: "zone1", 10586 CellInfo: &topodatapb.CellInfo{ 10587 ServerAddress: ":1111", 10588 Root: "/zone1", 10589 }, 10590 }, 10591 }, 10592 { 10593 name: "cell not found", 10594 cells: map[string]*topodatapb.CellInfo{ 10595 "zone1": { 10596 ServerAddress: ":1111", 10597 Root: "/zone1", 10598 }, 10599 }, 10600 req: &vtctldatapb.UpdateCellInfoRequest{ 10601 Name: "zone404", 10602 CellInfo: &topodatapb.CellInfo{ 10603 ServerAddress: ":4040", 10604 Root: "/zone404", 10605 }, 10606 }, 10607 expected: &vtctldatapb.UpdateCellInfoResponse{ 10608 Name: "zone404", 10609 CellInfo: &topodatapb.CellInfo{ 10610 ServerAddress: ":4040", 10611 Root: "/zone404", 10612 }, 10613 }, 10614 }, 10615 { 10616 name: "cannot update", 10617 cells: map[string]*topodatapb.CellInfo{ 10618 "zone1": { 10619 ServerAddress: ":1111", 10620 Root: "/zone1", 10621 }, 10622 }, 10623 forceTopoError: true, 10624 req: &vtctldatapb.UpdateCellInfoRequest{ 10625 Name: "zone1", 10626 CellInfo: &topodatapb.CellInfo{ 10627 Root: "/zone1", 10628 }, 10629 }, 10630 expected: nil, 10631 shouldErr: true, 10632 }, 10633 } 10634 10635 for _, tt := range tests { 10636 tt := tt 10637 t.Run(tt.name, func(t *testing.T) { 10638 t.Parallel() 10639 10640 ts, factory := memorytopo.NewServerAndFactory() 10641 for name, cell := range tt.cells { 10642 err := ts.CreateCellInfo(ctx, name, cell) 10643 require.NoError(t, err, "failed to create cell %s: %+v for test", name, cell) 10644 } 10645 10646 if tt.forceTopoError { 10647 factory.SetError(fmt.Errorf("%w: topo down for testing", assert.AnError)) 10648 } 10649 10650 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 10651 return NewVtctldServer(ts) 10652 }) 10653 resp, err := vtctld.UpdateCellInfo(ctx, tt.req) 10654 if tt.shouldErr { 10655 assert.Error(t, err) 10656 return 10657 } 10658 10659 require.NoError(t, err) 10660 utils.MustMatch(t, tt.expected, resp) 10661 }) 10662 } 10663 } 10664 10665 func TestUpdateCellsAlias(t *testing.T) { 10666 t.Parallel() 10667 10668 ctx := context.Background() 10669 tests := []struct { 10670 name string 10671 cells []string 10672 aliases map[string][]string 10673 req *vtctldatapb.UpdateCellsAliasRequest 10674 expected *vtctldatapb.UpdateCellsAliasResponse 10675 shouldErr bool 10676 }{ 10677 { 10678 name: "remove one cell", 10679 aliases: map[string][]string{ 10680 "zone": { 10681 "zone1", 10682 "zone2", 10683 "zone3", 10684 }, 10685 }, 10686 req: &vtctldatapb.UpdateCellsAliasRequest{ 10687 Name: "zone", 10688 CellsAlias: &topodatapb.CellsAlias{ 10689 Cells: []string{"zone1", "zone2"}, 10690 }, 10691 }, 10692 expected: &vtctldatapb.UpdateCellsAliasResponse{ 10693 Name: "zone", 10694 CellsAlias: &topodatapb.CellsAlias{ 10695 Cells: []string{"zone1", "zone2"}, 10696 }, 10697 }, 10698 }, 10699 { 10700 name: "add one cell", 10701 cells: []string{"zone4"}, // all other cells get created via the aliases map 10702 aliases: map[string][]string{ 10703 "zone": { 10704 "zone1", 10705 "zone2", 10706 "zone3", 10707 }, 10708 }, 10709 req: &vtctldatapb.UpdateCellsAliasRequest{ 10710 Name: "zone", 10711 CellsAlias: &topodatapb.CellsAlias{ 10712 Cells: []string{ 10713 "zone1", 10714 "zone2", 10715 "zone3", 10716 "zone4", 10717 }, 10718 }, 10719 }, 10720 expected: &vtctldatapb.UpdateCellsAliasResponse{ 10721 Name: "zone", 10722 CellsAlias: &topodatapb.CellsAlias{ 10723 Cells: []string{ 10724 "zone1", 10725 "zone2", 10726 "zone3", 10727 "zone4", 10728 }, 10729 }, 10730 }, 10731 }, 10732 { 10733 name: "alias does not exist", 10734 cells: []string{"zone1", "zone2"}, 10735 req: &vtctldatapb.UpdateCellsAliasRequest{ 10736 Name: "zone", 10737 CellsAlias: &topodatapb.CellsAlias{ 10738 Cells: []string{"zone1", "zone2"}, 10739 }, 10740 }, 10741 expected: &vtctldatapb.UpdateCellsAliasResponse{ 10742 Name: "zone", 10743 CellsAlias: &topodatapb.CellsAlias{ 10744 Cells: []string{"zone1", "zone2"}, 10745 }, 10746 }, 10747 }, 10748 { 10749 name: "invalid alias list", 10750 aliases: map[string][]string{ 10751 "zone_a": { 10752 "zone1", 10753 "zone2", 10754 }, 10755 "zone_b": { 10756 "zone3", 10757 "zone4", 10758 }, 10759 }, 10760 req: &vtctldatapb.UpdateCellsAliasRequest{ 10761 Name: "zone_a", 10762 CellsAlias: &topodatapb.CellsAlias{ 10763 Cells: []string{ 10764 "zone1", 10765 "zone2", 10766 "zone3", // this is invalid because it belongs to alias zone_b 10767 }, 10768 }, 10769 }, 10770 shouldErr: true, 10771 }, 10772 } 10773 10774 for _, tt := range tests { 10775 tt := tt 10776 t.Run(tt.name, func(t *testing.T) { 10777 t.Parallel() 10778 10779 ts := memorytopo.NewServer(tt.cells...) 10780 for name, cells := range tt.aliases { 10781 for _, cell := range cells { 10782 // We use UpdateCellInfoFields rather than CreateCellInfo 10783 // for the update-or-create behavior. 10784 err := ts.UpdateCellInfoFields(ctx, cell, func(ci *topodatapb.CellInfo) error { 10785 ci.Root = "/" + cell 10786 ci.ServerAddress = cell + ":8080" 10787 return nil 10788 }) 10789 require.NoError(t, err, "failed to create cell %v", cell) 10790 } 10791 10792 err := ts.CreateCellsAlias(ctx, name, &topodatapb.CellsAlias{ 10793 Cells: cells, 10794 }) 10795 require.NoError(t, err, "failed to create cell alias %v (cells = %v)", name, cells) 10796 } 10797 10798 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 10799 return NewVtctldServer(ts) 10800 }) 10801 resp, err := vtctld.UpdateCellsAlias(ctx, tt.req) 10802 if tt.shouldErr { 10803 assert.Error(t, err) 10804 return 10805 } 10806 10807 require.NoError(t, err) 10808 utils.MustMatch(t, tt.expected, resp) 10809 }) 10810 } 10811 } 10812 10813 func TestValidate(t *testing.T) { 10814 t.Parallel() 10815 10816 ctx := context.Background() 10817 ts := memorytopo.NewServer("zone1", "zone2", "zone3") 10818 tablets := []*topodatapb.Tablet{ 10819 { 10820 Keyspace: "ks1", 10821 Shard: "-", 10822 Type: topodatapb.TabletType_PRIMARY, 10823 Alias: &topodatapb.TabletAlias{ 10824 Cell: "zone1", 10825 Uid: 100, 10826 }, 10827 Hostname: "ks1-00-00-primary", 10828 }, 10829 { 10830 Keyspace: "ks1", 10831 Shard: "-", 10832 Type: topodatapb.TabletType_REPLICA, 10833 Alias: &topodatapb.TabletAlias{ 10834 Cell: "zone1", 10835 Uid: 101, 10836 }, 10837 Hostname: "ks1-00-00-replica", 10838 }, 10839 { 10840 Keyspace: "ks2", 10841 Shard: "-80", 10842 Type: topodatapb.TabletType_PRIMARY, 10843 Alias: &topodatapb.TabletAlias{ 10844 Cell: "zone1", 10845 Uid: 102, 10846 }, 10847 Hostname: "ks2-00-80-primary", 10848 }, 10849 { 10850 Keyspace: "ks2", 10851 Shard: "-80", 10852 Type: topodatapb.TabletType_REPLICA, 10853 Alias: &topodatapb.TabletAlias{ 10854 Cell: "zone2", 10855 Uid: 200, 10856 }, 10857 Hostname: "ks2-00-80-replica1", 10858 }, 10859 { 10860 Keyspace: "ks2", 10861 Shard: "-80", 10862 Type: topodatapb.TabletType_REPLICA, 10863 Alias: &topodatapb.TabletAlias{ 10864 Cell: "zone3", 10865 Uid: 300, 10866 }, 10867 Hostname: "ks2-00-80-replica2", 10868 }, 10869 { 10870 Keyspace: "ks2", 10871 Shard: "80-", 10872 Type: topodatapb.TabletType_PRIMARY, 10873 Alias: &topodatapb.TabletAlias{ 10874 Cell: "zone3", 10875 Uid: 301, 10876 }, 10877 Hostname: "ks2-80-00-primary", 10878 }, 10879 { 10880 Keyspace: "ks2", 10881 Shard: "80-", 10882 Type: topodatapb.TabletType_REPLICA, 10883 Alias: &topodatapb.TabletAlias{ 10884 Cell: "zone2", 10885 Uid: 201, 10886 }, 10887 Hostname: "ks2-80-00-replica1", 10888 }, 10889 { 10890 Keyspace: "ks2", 10891 Shard: "80-", 10892 Type: topodatapb.TabletType_RDONLY, 10893 Alias: &topodatapb.TabletAlias{ 10894 Cell: "zone1", 10895 Uid: 103, 10896 }, 10897 Hostname: "ks2-80-00-rdonly1", 10898 }, 10899 } 10900 testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ 10901 AlsoSetShardPrimary: true, 10902 ForceSetShardPrimary: true, 10903 SkipShardCreation: false, 10904 }, tablets...) 10905 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { 10906 return NewVtctldServer(ts) 10907 }) 10908 10909 resp, err := vtctld.Validate(ctx, &vtctldatapb.ValidateRequest{ 10910 PingTablets: false, 10911 }) 10912 require.NoError(t, err) 10913 assert.Equal(t, &vtctldatapb.ValidateResponse{ 10914 ResultsByKeyspace: map[string]*vtctldatapb.ValidateKeyspaceResponse{ 10915 "ks1": { 10916 ResultsByShard: map[string]*vtctldatapb.ValidateShardResponse{ 10917 "-": {}, 10918 }, 10919 }, 10920 "ks2": { 10921 ResultsByShard: map[string]*vtctldatapb.ValidateShardResponse{ 10922 "-80": {}, 10923 "80-": {}, 10924 }, 10925 }, 10926 }, 10927 }, resp) 10928 } 10929 10930 func TestValidateSchemaKeyspace(t *testing.T) { 10931 ctx := context.Background() 10932 ts := memorytopo.NewServer("zone1", "zone2", "zone3") 10933 tmc := testutil.TabletManagerClient{ 10934 GetSchemaResults: map[string]struct { 10935 Schema *tabletmanagerdatapb.SchemaDefinition 10936 Error error 10937 }{}, 10938 } 10939 testutil.AddKeyspace(ctx, t, ts, &vtctldatapb.Keyspace{ 10940 Name: "ks1", 10941 Keyspace: &topodatapb.Keyspace{ 10942 KeyspaceType: topodatapb.KeyspaceType_NORMAL, 10943 }, 10944 }) 10945 testutil.AddKeyspace(ctx, t, ts, &vtctldatapb.Keyspace{ 10946 Name: "ks2", 10947 Keyspace: &topodatapb.Keyspace{ 10948 KeyspaceType: topodatapb.KeyspaceType_NORMAL, 10949 }, 10950 }) 10951 tablets := []*topodatapb.Tablet{ 10952 { 10953 Keyspace: "ks1", 10954 Shard: "-", 10955 Type: topodatapb.TabletType_PRIMARY, 10956 Alias: &topodatapb.TabletAlias{ 10957 Cell: "zone1", 10958 Uid: 100, 10959 }, 10960 Hostname: "ks1-00-00-primary", 10961 }, 10962 { 10963 Keyspace: "ks1", 10964 Shard: "-", 10965 Type: topodatapb.TabletType_REPLICA, 10966 Alias: &topodatapb.TabletAlias{ 10967 Cell: "zone1", 10968 Uid: 101, 10969 }, 10970 Hostname: "ks1-00-00-replica", 10971 }, 10972 // ks2 shard -80 has no Primary intentionally for testing 10973 { 10974 Keyspace: "ks2", 10975 Shard: "-80", 10976 Type: topodatapb.TabletType_REPLICA, 10977 Alias: &topodatapb.TabletAlias{ 10978 Cell: "zone1", 10979 Uid: 102, 10980 }, 10981 Hostname: "ks2-00-80-replica0", 10982 }, 10983 { 10984 Keyspace: "ks2", 10985 Shard: "-80", 10986 Type: topodatapb.TabletType_REPLICA, 10987 Alias: &topodatapb.TabletAlias{ 10988 Cell: "zone2", 10989 Uid: 200, 10990 }, 10991 Hostname: "ks2-00-80-replica1", 10992 }, 10993 // 10994 { 10995 Keyspace: "ks2", 10996 Shard: "80-", 10997 Type: topodatapb.TabletType_PRIMARY, 10998 Alias: &topodatapb.TabletAlias{ 10999 Cell: "zone1", 11000 Uid: 103, 11001 }, 11002 Hostname: "ks2-80-00-primary1", 11003 }, 11004 { 11005 Keyspace: "ks2", 11006 Shard: "80-", 11007 Type: topodatapb.TabletType_REPLICA, 11008 Alias: &topodatapb.TabletAlias{ 11009 Cell: "zone2", 11010 Uid: 201, 11011 }, 11012 Hostname: "ks2-80-00-replica1", 11013 }, 11014 } 11015 testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ 11016 AlsoSetShardPrimary: true, 11017 ForceSetShardPrimary: true, 11018 SkipShardCreation: false, 11019 }, tablets...) 11020 11021 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 11022 return NewVtctldServer(ts) 11023 }) 11024 11025 schema1 := &tabletmanagerdatapb.SchemaDefinition{ 11026 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{{ 11027 Name: "not_in_vschema", 11028 Columns: []string{"c1", "c2"}, 11029 PrimaryKeyColumns: []string{"c1"}, 11030 Fields: sqltypes.MakeTestFields("c1|c2", "int64|int64"), 11031 }}, 11032 } 11033 11034 schema2 := &tabletmanagerdatapb.SchemaDefinition{ 11035 TableDefinitions: []*tabletmanagerdatapb.TableDefinition{ 11036 { 11037 Name: "t1", 11038 Columns: []string{"c1"}, 11039 }, 11040 { 11041 Name: "t2", 11042 Columns: []string{"c1"}, 11043 }, 11044 { 11045 Name: "t3", 11046 Columns: []string{"c1"}, 11047 }, 11048 }, 11049 } 11050 11051 // we need to run this on each test case or they will pollute each other 11052 setupSchema := func(tablet *topodatapb.TabletAlias, schema *tabletmanagerdatapb.SchemaDefinition) { 11053 tmc.GetSchemaResults[topoproto.TabletAliasString(tablet)] = struct { 11054 Schema *tabletmanagerdatapb.SchemaDefinition 11055 Error error 11056 }{ 11057 Schema: schema, 11058 Error: nil, 11059 } 11060 } 11061 11062 tests := []*struct { 11063 name string 11064 req *vtctldatapb.ValidateSchemaKeyspaceRequest 11065 expected *vtctldatapb.ValidateSchemaKeyspaceResponse 11066 setup func() 11067 shouldErr bool 11068 }{ 11069 { 11070 name: "valid schemas", 11071 req: &vtctldatapb.ValidateSchemaKeyspaceRequest{ 11072 Keyspace: "ks1", 11073 }, 11074 expected: &vtctldatapb.ValidateSchemaKeyspaceResponse{ 11075 Results: []string{}, 11076 ResultsByShard: map[string]*vtctldatapb.ValidateShardResponse{ 11077 "-": {Results: []string{}}, 11078 }, 11079 }, 11080 setup: func() { 11081 setupSchema(&topodatapb.TabletAlias{ 11082 Cell: "zone1", 11083 Uid: 100, 11084 }, schema1) 11085 setupSchema(&topodatapb.TabletAlias{ 11086 Cell: "zone1", 11087 Uid: 101, 11088 }, schema1) 11089 }, 11090 shouldErr: false, 11091 }, 11092 { 11093 name: "different schemas", 11094 req: &vtctldatapb.ValidateSchemaKeyspaceRequest{ 11095 Keyspace: "ks1", 11096 }, 11097 expected: &vtctldatapb.ValidateSchemaKeyspaceResponse{ 11098 Results: []string{"zone1-0000000100 has an extra table named not_in_vschema"}, 11099 ResultsByShard: map[string]*vtctldatapb.ValidateShardResponse{ 11100 "-": {Results: []string{"zone1-0000000100 has an extra table named not_in_vschema"}}, 11101 }, 11102 }, 11103 setup: func() { 11104 setupSchema(&topodatapb.TabletAlias{ 11105 Cell: "zone1", 11106 Uid: 100, 11107 }, schema1) 11108 setupSchema(&topodatapb.TabletAlias{ 11109 Cell: "zone1", 11110 Uid: 101, 11111 }, schema2) 11112 }, 11113 shouldErr: false, 11114 }, 11115 { 11116 name: "skip-no-primary: no primary", 11117 req: &vtctldatapb.ValidateSchemaKeyspaceRequest{ 11118 Keyspace: "ks2", 11119 SkipNoPrimary: false, 11120 }, 11121 expected: &vtctldatapb.ValidateSchemaKeyspaceResponse{ 11122 Results: []string{"no primary in shard ks2/-80"}, 11123 ResultsByShard: map[string]*vtctldatapb.ValidateShardResponse{ 11124 "-80": {Results: []string{"no primary in shard ks2/-80"}}, 11125 "80-": {Results: []string{}}, 11126 }, 11127 }, 11128 setup: func() { 11129 setupSchema(&topodatapb.TabletAlias{ 11130 Cell: "zone1", 11131 Uid: 103, 11132 }, schema1) 11133 setupSchema(&topodatapb.TabletAlias{ 11134 Cell: "zone2", 11135 Uid: 201, 11136 }, schema1) 11137 }, 11138 shouldErr: false, 11139 }, 11140 } 11141 11142 for _, tt := range tests { 11143 t.Run(tt.name, func(t *testing.T) { 11144 tt.setup() 11145 resp, err := vtctld.ValidateSchemaKeyspace(ctx, tt.req) 11146 if tt.shouldErr { 11147 assert.Error(t, err) 11148 return 11149 } 11150 11151 assert.NoError(t, err) 11152 utils.MustMatch(t, tt.expected, resp) 11153 }) 11154 } 11155 } 11156 11157 func TestValidateVersionKeyspace(t *testing.T) { 11158 ctx := context.Background() 11159 ts := memorytopo.NewServer("zone1", "zone2") 11160 tmc := testutil.TabletManagerClient{ 11161 GetSchemaResults: map[string]struct { 11162 Schema *tabletmanagerdatapb.SchemaDefinition 11163 Error error 11164 }{}, 11165 } 11166 testutil.AddKeyspace(ctx, t, ts, &vtctldatapb.Keyspace{ 11167 Name: "ks1", 11168 Keyspace: &topodatapb.Keyspace{ 11169 KeyspaceType: topodatapb.KeyspaceType_NORMAL, 11170 }, 11171 }) 11172 testutil.AddKeyspace(ctx, t, ts, &vtctldatapb.Keyspace{ 11173 Name: "ks2", 11174 Keyspace: &topodatapb.Keyspace{ 11175 KeyspaceType: topodatapb.KeyspaceType_NORMAL, 11176 }, 11177 }) 11178 tablets := []*topodatapb.Tablet{ 11179 { 11180 Keyspace: "ks1", 11181 Shard: "-", 11182 Type: topodatapb.TabletType_PRIMARY, 11183 Alias: &topodatapb.TabletAlias{ 11184 Cell: "zone1", 11185 Uid: 100, 11186 }, 11187 Hostname: "primary", 11188 }, 11189 { 11190 Keyspace: "ks1", 11191 Shard: "-", 11192 Type: topodatapb.TabletType_REPLICA, 11193 Alias: &topodatapb.TabletAlias{ 11194 Cell: "zone1", 11195 Uid: 101, 11196 }, 11197 Hostname: "replica", 11198 }, 11199 } 11200 testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ 11201 AlsoSetShardPrimary: true, 11202 ForceSetShardPrimary: true, 11203 SkipShardCreation: false, 11204 }, tablets...) 11205 11206 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 11207 return NewVtctldServer(ts) 11208 }) 11209 11210 tests := []*struct { 11211 name string 11212 req *vtctldatapb.ValidateVersionKeyspaceRequest 11213 expected *vtctldatapb.ValidateVersionKeyspaceResponse 11214 setup func() 11215 shouldErr bool 11216 }{ 11217 { 11218 name: "valid versions", 11219 req: &vtctldatapb.ValidateVersionKeyspaceRequest{ 11220 Keyspace: "ks1", 11221 }, 11222 expected: &vtctldatapb.ValidateVersionKeyspaceResponse{ 11223 Results: []string{}, 11224 ResultsByShard: map[string]*vtctldatapb.ValidateShardResponse{ 11225 "-": {Results: []string{}}, 11226 }, 11227 }, 11228 setup: func() { 11229 addrVersionMap := map[string]string{ 11230 "primary:0": "version1", 11231 "replica:0": "version1", 11232 } 11233 SetVersionFunc(testutil.MockGetVersionFromTablet(addrVersionMap)) 11234 }, 11235 shouldErr: false, 11236 }, 11237 { 11238 name: "different versions", 11239 req: &vtctldatapb.ValidateVersionKeyspaceRequest{ 11240 Keyspace: "ks1", 11241 }, 11242 expected: &vtctldatapb.ValidateVersionKeyspaceResponse{ 11243 Results: []string{"primary zone1-0000000100 version version:\"version1\" is different than replica zone1-0000000101 version version:\"version2\""}, 11244 ResultsByShard: map[string]*vtctldatapb.ValidateShardResponse{ 11245 "-": {Results: []string{"primary zone1-0000000100 version version:\"version1\" is different than replica zone1-0000000101 version version:\"version2\""}}, 11246 }, 11247 }, 11248 setup: func() { 11249 addrVersionMap := map[string]string{ 11250 "primary:0": "version1", 11251 "replica:0": "version2", 11252 } 11253 SetVersionFunc(testutil.MockGetVersionFromTablet(addrVersionMap)) 11254 }, 11255 shouldErr: false, 11256 }, 11257 } 11258 11259 for _, tt := range tests { 11260 t.Run(tt.name, func(t *testing.T) { 11261 tt.setup() 11262 resp, err := vtctld.ValidateVersionKeyspace(ctx, tt.req) 11263 if tt.shouldErr { 11264 assert.Error(t, err) 11265 return 11266 } 11267 11268 assert.NoError(t, err) 11269 utils.MustMatch(t, tt.expected, resp) 11270 }) 11271 } 11272 } 11273 11274 func TestValidateVersionShard(t *testing.T) { 11275 t.Parallel() 11276 11277 ctx := context.Background() 11278 ts := memorytopo.NewServer("zone1", "zone2") 11279 tmc := testutil.TabletManagerClient{ 11280 GetSchemaResults: map[string]struct { 11281 Schema *tabletmanagerdatapb.SchemaDefinition 11282 Error error 11283 }{}, 11284 } 11285 testutil.AddKeyspace(ctx, t, ts, &vtctldatapb.Keyspace{ 11286 Name: "ks", 11287 Keyspace: &topodatapb.Keyspace{ 11288 KeyspaceType: topodatapb.KeyspaceType_NORMAL, 11289 }, 11290 }) 11291 11292 tablets := []*topodatapb.Tablet{ 11293 { 11294 Keyspace: "ks", 11295 Shard: "-", 11296 Type: topodatapb.TabletType_PRIMARY, 11297 Alias: &topodatapb.TabletAlias{ 11298 Cell: "zone1", 11299 Uid: 100, 11300 }, 11301 Hostname: "primary", 11302 }, 11303 { 11304 Keyspace: "ks", 11305 Shard: "-", 11306 Type: topodatapb.TabletType_REPLICA, 11307 Alias: &topodatapb.TabletAlias{ 11308 Cell: "zone1", 11309 Uid: 101, 11310 }, 11311 Hostname: "replica", 11312 }, 11313 } 11314 testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ 11315 AlsoSetShardPrimary: true, 11316 ForceSetShardPrimary: true, 11317 SkipShardCreation: false, 11318 }, tablets...) 11319 11320 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 11321 return NewVtctldServer(ts) 11322 }) 11323 11324 tests := []*struct { 11325 name string 11326 req *vtctldatapb.ValidateVersionShardRequest 11327 expected *vtctldatapb.ValidateVersionShardResponse 11328 setup func() 11329 shouldErr bool 11330 }{ 11331 { 11332 name: "valid versions", 11333 req: &vtctldatapb.ValidateVersionShardRequest{ 11334 Keyspace: "ks", 11335 Shard: "-", 11336 }, 11337 expected: &vtctldatapb.ValidateVersionShardResponse{ 11338 Results: []string{}, 11339 }, 11340 setup: func() { 11341 addrVersionMap := map[string]string{ 11342 "primary:0": "version1", 11343 "replica:0": "version1", 11344 } 11345 SetVersionFunc(testutil.MockGetVersionFromTablet(addrVersionMap)) 11346 }, 11347 shouldErr: false, 11348 }, 11349 { 11350 name: "different versions", 11351 req: &vtctldatapb.ValidateVersionShardRequest{ 11352 Keyspace: "ks", 11353 Shard: "-", 11354 }, 11355 expected: &vtctldatapb.ValidateVersionShardResponse{ 11356 Results: []string{"primary zone1-0000000100 version version1 is different than replica zone1-0000000101 version version:\"version2\""}, 11357 }, 11358 setup: func() { 11359 addrVersionMap := map[string]string{ 11360 "primary:0": "version1", 11361 "replica:0": "version2", 11362 } 11363 SetVersionFunc(testutil.MockGetVersionFromTablet(addrVersionMap)) 11364 }, 11365 shouldErr: false, 11366 }, 11367 } 11368 11369 for _, tt := range tests { 11370 curT := tt 11371 t.Run(tt.name, func(t *testing.T) { 11372 t.Parallel() 11373 11374 curT.setup() 11375 resp, err := vtctld.ValidateVersionShard(ctx, curT.req) 11376 if curT.shouldErr { 11377 assert.Error(t, err) 11378 return 11379 } 11380 11381 assert.NoError(t, err) 11382 utils.MustMatch(t, curT.expected, resp) 11383 }) 11384 } 11385 } 11386 11387 func TestValidateShard(t *testing.T) { 11388 t.Parallel() 11389 11390 type testcase struct { 11391 name string 11392 ts *topo.Server 11393 tmc *testutil.TabletManagerClient 11394 setup func(t *testing.T, tt *testcase) 11395 req *vtctldatapb.ValidateShardRequest 11396 expected *vtctldatapb.ValidateShardResponse 11397 shouldErr bool 11398 } 11399 11400 ctx := context.Background() 11401 tests := []*testcase{ 11402 { 11403 name: "ok", 11404 ts: memorytopo.NewServer("zone1"), 11405 tmc: nil, 11406 setup: func(t *testing.T, tt *testcase) { 11407 tablets := []*topodatapb.Tablet{ 11408 { 11409 Keyspace: "ks1", 11410 Shard: "-", 11411 Alias: &topodatapb.TabletAlias{ 11412 Cell: "zone1", 11413 Uid: 100, 11414 }, 11415 Type: topodatapb.TabletType_PRIMARY, 11416 Hostname: "ks1-primary", 11417 }, 11418 { 11419 Keyspace: "ks1", 11420 Shard: "-", 11421 Alias: &topodatapb.TabletAlias{ 11422 Cell: "zone1", 11423 Uid: 101, 11424 }, 11425 Type: topodatapb.TabletType_REPLICA, 11426 Hostname: "ks1-replica", 11427 }, 11428 } 11429 testutil.AddTablets(ctx, t, tt.ts, &testutil.AddTabletOptions{ 11430 AlsoSetShardPrimary: true, 11431 }, tablets...) 11432 }, 11433 req: &vtctldatapb.ValidateShardRequest{ 11434 Keyspace: "ks1", 11435 Shard: "-", 11436 }, 11437 expected: &vtctldatapb.ValidateShardResponse{}, 11438 }, 11439 { 11440 name: "no shard", 11441 ts: memorytopo.NewServer("zone1"), 11442 tmc: nil, 11443 setup: func(t *testing.T, tt *testcase) { 11444 tablets := []*topodatapb.Tablet{ 11445 { 11446 Keyspace: "ks1", 11447 Shard: "-", 11448 Alias: &topodatapb.TabletAlias{ 11449 Cell: "zone1", 11450 Uid: 100, 11451 }, 11452 Type: topodatapb.TabletType_PRIMARY, 11453 Hostname: "ks1-primary", 11454 }, 11455 { 11456 Keyspace: "ks1", 11457 Shard: "-", 11458 Alias: &topodatapb.TabletAlias{ 11459 Cell: "zone1", 11460 Uid: 101, 11461 }, 11462 Type: topodatapb.TabletType_REPLICA, 11463 Hostname: "ks1-replica", 11464 }, 11465 } 11466 testutil.AddTablets(ctx, t, tt.ts, &testutil.AddTabletOptions{ 11467 SkipShardCreation: true, 11468 }, tablets...) 11469 }, 11470 req: &vtctldatapb.ValidateShardRequest{ 11471 Keyspace: "ks1", 11472 Shard: "-", 11473 }, 11474 expected: &vtctldatapb.ValidateShardResponse{ 11475 Results: []string{ 11476 "TopologyServer.GetShard(ks1, -) failed: node doesn't exist: keyspaces/ks1/shards/-/Shard", 11477 }, 11478 }, 11479 }, 11480 { 11481 name: "no primary in shard", 11482 ts: memorytopo.NewServer("zone1"), 11483 tmc: nil, 11484 setup: func(t *testing.T, tt *testcase) { 11485 tablets := []*topodatapb.Tablet{ 11486 { 11487 Keyspace: "ks1", 11488 Shard: "-", 11489 Alias: &topodatapb.TabletAlias{ 11490 Cell: "zone1", 11491 Uid: 101, 11492 }, 11493 Type: topodatapb.TabletType_REPLICA, 11494 Hostname: "ks1-replica", 11495 }, 11496 } 11497 testutil.AddTablets(ctx, t, tt.ts, nil, tablets...) 11498 }, 11499 req: &vtctldatapb.ValidateShardRequest{ 11500 Keyspace: "ks1", 11501 Shard: "-", 11502 }, 11503 expected: &vtctldatapb.ValidateShardResponse{ 11504 Results: []string{"no primary for shard ks1/-"}, 11505 }, 11506 }, 11507 { 11508 name: "two primaries in shard", 11509 ts: memorytopo.NewServer("zone1"), 11510 tmc: nil, 11511 setup: func(t *testing.T, tt *testcase) { 11512 tablets := []*topodatapb.Tablet{ 11513 { 11514 Keyspace: "ks1", 11515 Shard: "-", 11516 Alias: &topodatapb.TabletAlias{ 11517 Cell: "zone1", 11518 Uid: 100, 11519 }, 11520 Type: topodatapb.TabletType_PRIMARY, 11521 Hostname: "ks1-primary", 11522 }, 11523 { 11524 Keyspace: "ks1", 11525 Shard: "-", 11526 Alias: &topodatapb.TabletAlias{ 11527 Cell: "zone1", 11528 Uid: 101, 11529 }, 11530 Type: topodatapb.TabletType_PRIMARY, 11531 Hostname: "ks1-primary2", 11532 }, 11533 } 11534 testutil.AddTablets(ctx, t, tt.ts, &testutil.AddTabletOptions{ 11535 AlsoSetShardPrimary: true, 11536 ForceSetShardPrimary: true, 11537 }, tablets...) 11538 }, 11539 req: &vtctldatapb.ValidateShardRequest{ 11540 Keyspace: "ks1", 11541 Shard: "-", 11542 }, 11543 expected: &vtctldatapb.ValidateShardResponse{ 11544 Results: []string{ 11545 "shard ks1/- already has primary zone1-0000000100 but found other primary zone1-0000000101", 11546 "primary mismatch for shard ks1/-: found zone1-0000000100, expected zone1-0000000101", 11547 }, 11548 }, 11549 }, 11550 { 11551 name: "ping_tablets/ok", 11552 ts: memorytopo.NewServer("zone1"), 11553 tmc: &testutil.TabletManagerClient{ 11554 GetReplicasResults: map[string]struct { 11555 Replicas []string 11556 Error error 11557 }{ 11558 "zone1-0000000100": { 11559 Replicas: []string{"11.21.31.41", "12.22.32.42"}, 11560 }, 11561 }, 11562 PingResults: map[string]error{ 11563 "zone1-0000000100": nil, 11564 "zone1-0000000101": nil, 11565 "zone1-0000000102": nil, 11566 }, 11567 }, 11568 setup: func(t *testing.T, tt *testcase) { 11569 tablets := []*topodatapb.Tablet{ 11570 { 11571 Keyspace: "ks1", 11572 Shard: "-", 11573 Alias: &topodatapb.TabletAlias{ 11574 Cell: "zone1", 11575 Uid: 100, 11576 }, 11577 Type: topodatapb.TabletType_PRIMARY, 11578 Hostname: "ks1-primary", 11579 // note: we don't actually use this IP, we just need to 11580 // resolve _something_ for the testcase. The IPs are 11581 // used by the validateReplication function to 11582 // disambiguate/deduplicate tablets. 11583 MysqlHostname: "10.20.30.40", 11584 }, 11585 { 11586 Keyspace: "ks1", 11587 Shard: "-", 11588 Alias: &topodatapb.TabletAlias{ 11589 Cell: "zone1", 11590 Uid: 101, 11591 }, 11592 Type: topodatapb.TabletType_REPLICA, 11593 Hostname: "ks1-replica", 11594 MysqlHostname: "11.21.31.41", 11595 }, 11596 { 11597 Keyspace: "ks1", 11598 Shard: "-", 11599 Alias: &topodatapb.TabletAlias{ 11600 Cell: "zone1", 11601 Uid: 102, 11602 }, 11603 Type: topodatapb.TabletType_RDONLY, 11604 Hostname: "ks1-rdonly", 11605 MysqlHostname: "12.22.32.42", 11606 }, 11607 } 11608 testutil.AddTablets(ctx, t, tt.ts, &testutil.AddTabletOptions{ 11609 AlsoSetShardPrimary: true, 11610 }, tablets...) 11611 }, 11612 req: &vtctldatapb.ValidateShardRequest{ 11613 Keyspace: "ks1", 11614 Shard: "-", 11615 PingTablets: true, 11616 }, 11617 expected: &vtctldatapb.ValidateShardResponse{}, 11618 }, 11619 { 11620 name: "ping_tablets/GetReplicas failed", 11621 ts: memorytopo.NewServer("zone1"), 11622 tmc: &testutil.TabletManagerClient{ 11623 GetReplicasResults: map[string]struct { 11624 Replicas []string 11625 Error error 11626 }{ 11627 "zone1-0000000100": { 11628 Error: assert.AnError, 11629 }, 11630 }, 11631 PingResults: map[string]error{ 11632 "zone1-0000000100": nil, 11633 "zone1-0000000101": nil, 11634 "zone1-0000000102": nil, 11635 }, 11636 }, 11637 setup: func(t *testing.T, tt *testcase) { 11638 tablets := []*topodatapb.Tablet{ 11639 { 11640 Keyspace: "ks1", 11641 Shard: "-", 11642 Alias: &topodatapb.TabletAlias{ 11643 Cell: "zone1", 11644 Uid: 100, 11645 }, 11646 Type: topodatapb.TabletType_PRIMARY, 11647 Hostname: "ks1-primary", 11648 // note: we don't actually use this IP, we just need to 11649 // resolve _something_ for the testcase. The IPs are 11650 // used by the validateReplication function to 11651 // disambiguate/deduplicate tablets. 11652 MysqlHostname: "10.20.30.40", 11653 }, 11654 { 11655 Keyspace: "ks1", 11656 Shard: "-", 11657 Alias: &topodatapb.TabletAlias{ 11658 Cell: "zone1", 11659 Uid: 101, 11660 }, 11661 Type: topodatapb.TabletType_REPLICA, 11662 Hostname: "ks1-replica", 11663 MysqlHostname: "11.21.31.41", 11664 }, 11665 { 11666 Keyspace: "ks1", 11667 Shard: "-", 11668 Alias: &topodatapb.TabletAlias{ 11669 Cell: "zone1", 11670 Uid: 102, 11671 }, 11672 Type: topodatapb.TabletType_RDONLY, 11673 Hostname: "ks1-rdonly", 11674 MysqlHostname: "12.22.32.42", 11675 }, 11676 } 11677 testutil.AddTablets(ctx, t, tt.ts, &testutil.AddTabletOptions{ 11678 AlsoSetShardPrimary: true, 11679 }, tablets...) 11680 }, 11681 req: &vtctldatapb.ValidateShardRequest{ 11682 Keyspace: "ks1", 11683 Shard: "-", 11684 PingTablets: true, 11685 }, 11686 expected: &vtctldatapb.ValidateShardResponse{ 11687 Results: []string{"GetReplicas(Tablet{zone1-0000000100}) failed: assert.AnError general error for testing"}, 11688 }, 11689 }, 11690 { 11691 name: "ping_tablets/no replicas", 11692 ts: memorytopo.NewServer("zone1"), 11693 tmc: &testutil.TabletManagerClient{ 11694 GetReplicasResults: map[string]struct { 11695 Replicas []string 11696 Error error 11697 }{ 11698 "zone1-0000000100": { 11699 Replicas: []string{}, 11700 }, 11701 }, 11702 PingResults: map[string]error{ 11703 "zone1-0000000100": nil, 11704 "zone1-0000000101": nil, 11705 "zone1-0000000102": nil, 11706 }, 11707 }, 11708 setup: func(t *testing.T, tt *testcase) { 11709 tablets := []*topodatapb.Tablet{ 11710 { 11711 Keyspace: "ks1", 11712 Shard: "-", 11713 Alias: &topodatapb.TabletAlias{ 11714 Cell: "zone1", 11715 Uid: 100, 11716 }, 11717 Type: topodatapb.TabletType_PRIMARY, 11718 Hostname: "ks1-primary", 11719 // note: we don't actually use this IP, we just need to 11720 // resolve _something_ for the testcase. The IPs are 11721 // used by the validateReplication function to 11722 // disambiguate/deduplicate tablets. 11723 MysqlHostname: "10.20.30.40", 11724 }, 11725 { 11726 Keyspace: "ks1", 11727 Shard: "-", 11728 Alias: &topodatapb.TabletAlias{ 11729 Cell: "zone1", 11730 Uid: 101, 11731 }, 11732 Type: topodatapb.TabletType_REPLICA, 11733 Hostname: "ks1-replica", 11734 MysqlHostname: "11.21.31.41", 11735 }, 11736 { 11737 Keyspace: "ks1", 11738 Shard: "-", 11739 Alias: &topodatapb.TabletAlias{ 11740 Cell: "zone1", 11741 Uid: 102, 11742 }, 11743 Type: topodatapb.TabletType_RDONLY, 11744 Hostname: "ks1-rdonly", 11745 MysqlHostname: "12.22.32.42", 11746 }, 11747 } 11748 testutil.AddTablets(ctx, t, tt.ts, &testutil.AddTabletOptions{ 11749 AlsoSetShardPrimary: true, 11750 }, tablets...) 11751 }, 11752 req: &vtctldatapb.ValidateShardRequest{ 11753 Keyspace: "ks1", 11754 Shard: "-", 11755 PingTablets: true, 11756 }, 11757 expected: &vtctldatapb.ValidateShardResponse{ 11758 Results: []string{"no replicas of tablet zone1-0000000100 found"}, 11759 }, 11760 }, 11761 { 11762 name: "ping_tablets/orphaned replica", 11763 ts: memorytopo.NewServer("zone1"), 11764 tmc: &testutil.TabletManagerClient{ 11765 GetReplicasResults: map[string]struct { 11766 Replicas []string 11767 Error error 11768 }{ 11769 "zone1-0000000100": { 11770 Replicas: []string{"11.21.31.41", "100.200.200.100" /* not in set of tablet addrs below */}, 11771 }, 11772 }, 11773 PingResults: map[string]error{ 11774 "zone1-0000000100": nil, 11775 "zone1-0000000101": nil, 11776 "zone1-0000000102": nil, 11777 }, 11778 }, 11779 setup: func(t *testing.T, tt *testcase) { 11780 tablets := []*topodatapb.Tablet{ 11781 { 11782 Keyspace: "ks1", 11783 Shard: "-", 11784 Alias: &topodatapb.TabletAlias{ 11785 Cell: "zone1", 11786 Uid: 100, 11787 }, 11788 Type: topodatapb.TabletType_PRIMARY, 11789 Hostname: "ks1-primary", 11790 // note: we don't actually use this IP, we just need to 11791 // resolve _something_ for the testcase. The IPs are 11792 // used by the validateReplication function to 11793 // disambiguate/deduplicate tablets. 11794 MysqlHostname: "10.20.30.40", 11795 }, 11796 { 11797 Keyspace: "ks1", 11798 Shard: "-", 11799 Alias: &topodatapb.TabletAlias{ 11800 Cell: "zone1", 11801 Uid: 101, 11802 }, 11803 Type: topodatapb.TabletType_REPLICA, 11804 Hostname: "ks1-replica", 11805 MysqlHostname: "11.21.31.41", 11806 }, 11807 { 11808 Keyspace: "ks1", 11809 Shard: "-", 11810 Alias: &topodatapb.TabletAlias{ 11811 Cell: "zone1", 11812 Uid: 102, 11813 }, 11814 Type: topodatapb.TabletType_RDONLY, 11815 Hostname: "ks1-rdonly", 11816 MysqlHostname: "12.22.32.42", 11817 }, 11818 } 11819 testutil.AddTablets(ctx, t, tt.ts, &testutil.AddTabletOptions{ 11820 AlsoSetShardPrimary: true, 11821 }, tablets...) 11822 }, 11823 req: &vtctldatapb.ValidateShardRequest{ 11824 Keyspace: "ks1", 11825 Shard: "-", 11826 PingTablets: true, 11827 }, 11828 expected: &vtctldatapb.ValidateShardResponse{ 11829 Results: []string{ 11830 "replica 100.200.200.100 not in replication graph for shard ks1/- (mysql instance without vttablet?)", 11831 "replica zone1-0000000102 not replicating: 12.22.32.42 replica list: [\"11.21.31.41\" \"100.200.200.100\"]", 11832 }, 11833 }, 11834 }, 11835 { 11836 name: "ping_tablets/Ping failed", 11837 ts: memorytopo.NewServer("zone1"), 11838 tmc: &testutil.TabletManagerClient{ 11839 GetReplicasResults: map[string]struct { 11840 Replicas []string 11841 Error error 11842 }{ 11843 "zone1-0000000100": { 11844 Replicas: []string{"11.21.31.41", "12.22.32.42"}, 11845 }, 11846 }, 11847 PingResults: map[string]error{ 11848 "zone1-0000000100": nil, 11849 "zone1-0000000101": assert.AnError, 11850 "zone1-0000000102": nil, 11851 }, 11852 }, 11853 setup: func(t *testing.T, tt *testcase) { 11854 tablets := []*topodatapb.Tablet{ 11855 { 11856 Keyspace: "ks1", 11857 Shard: "-", 11858 Alias: &topodatapb.TabletAlias{ 11859 Cell: "zone1", 11860 Uid: 100, 11861 }, 11862 Type: topodatapb.TabletType_PRIMARY, 11863 Hostname: "ks1-primary", 11864 // note: we don't actually use this IP, we just need to 11865 // resolve _something_ for the testcase. The IPs are 11866 // used by the validateReplication function to 11867 // disambiguate/deduplicate tablets. 11868 MysqlHostname: "10.20.30.40", 11869 }, 11870 { 11871 Keyspace: "ks1", 11872 Shard: "-", 11873 Alias: &topodatapb.TabletAlias{ 11874 Cell: "zone1", 11875 Uid: 101, 11876 }, 11877 Type: topodatapb.TabletType_REPLICA, 11878 Hostname: "ks1-replica", 11879 MysqlHostname: "11.21.31.41", 11880 }, 11881 { 11882 Keyspace: "ks1", 11883 Shard: "-", 11884 Alias: &topodatapb.TabletAlias{ 11885 Cell: "zone1", 11886 Uid: 102, 11887 }, 11888 Type: topodatapb.TabletType_RDONLY, 11889 Hostname: "ks1-rdonly", 11890 MysqlHostname: "12.22.32.42", 11891 }, 11892 } 11893 testutil.AddTablets(ctx, t, tt.ts, &testutil.AddTabletOptions{ 11894 AlsoSetShardPrimary: true, 11895 }, tablets...) 11896 }, 11897 req: &vtctldatapb.ValidateShardRequest{ 11898 Keyspace: "ks1", 11899 Shard: "-", 11900 PingTablets: true, 11901 }, 11902 expected: &vtctldatapb.ValidateShardResponse{ 11903 Results: []string{"Ping(zone1-0000000101) failed: assert.AnError general error for testing tablet hostname: ks1-replica"}, 11904 }, 11905 }, 11906 } 11907 11908 for _, tt := range tests { 11909 tt := tt 11910 t.Run(tt.name, func(t *testing.T) { 11911 t.Parallel() 11912 11913 if tt.setup != nil { 11914 tt.setup(t, tt) 11915 } 11916 11917 vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { 11918 return NewVtctldServer(ts) 11919 }) 11920 resp, err := vtctld.ValidateShard(ctx, tt.req) 11921 if tt.shouldErr { 11922 assert.Error(t, err) 11923 return 11924 } 11925 11926 require.NoError(t, err) 11927 assert.Equal(t, tt.expected, resp) 11928 }) 11929 } 11930 } 11931 func TestMain(m *testing.M) { 11932 _flag.ParseFlagsForTest() 11933 os.Exit(m.Run()) 11934 }