vitess.io/vitess@v0.16.2/go/vt/vtctl/grpcvtctldserver/testutil/test_tmclient.go (about) 1 /* 2 Copyright 2021 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package testutil 18 19 import ( 20 "context" 21 "fmt" 22 "os" 23 "path" 24 "sync" 25 "testing" 26 "time" 27 28 "github.com/spf13/pflag" 29 "github.com/stretchr/testify/assert" 30 31 "vitess.io/vitess/go/timer" 32 hk "vitess.io/vitess/go/vt/hook" 33 "vitess.io/vitess/go/vt/log" 34 "vitess.io/vitess/go/vt/logutil" 35 "vitess.io/vitess/go/vt/servenv" 36 "vitess.io/vitess/go/vt/sqlparser" 37 "vitess.io/vitess/go/vt/topo" 38 "vitess.io/vitess/go/vt/topo/topoproto" 39 "vitess.io/vitess/go/vt/topotools" 40 "vitess.io/vitess/go/vt/vtctl/internal/grpcshim" 41 "vitess.io/vitess/go/vt/vttablet/tmclient" 42 43 logutilpb "vitess.io/vitess/go/vt/proto/logutil" 44 querypb "vitess.io/vitess/go/vt/proto/query" 45 replicationdatapb "vitess.io/vitess/go/vt/proto/replicationdata" 46 tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" 47 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 48 vtctlservicepb "vitess.io/vitess/go/vt/proto/vtctlservice" 49 "vitess.io/vitess/go/vt/proto/vttime" 50 ) 51 52 var ( 53 tmclientLock sync.Mutex 54 tmclientFactoryLock sync.Mutex 55 tmclients = map[string]tmclient.TabletManagerClient{} 56 tmclientFactories = map[string]func() tmclient.TabletManagerClient{} 57 ) 58 59 // NewVtctldServerWithTabletManagerClient returns a new 60 // grpcvtctldserver.VtctldServer configured with the given topo server and 61 // tmclient.TabletManagerClient implementation for testing. 62 // 63 // It synchronizes on private locks to prevent multiple goroutines from stepping 64 // on each other during VtctldServer initialization, but still run the rest of 65 // the test in parallel. 66 // 67 // NOTE, THE FIRST: It is only safe to use in parallel with other tests using 68 // this method of creating a VtctldServer, or with tests that do not depend on a 69 // VtctldServer's tmclient.TabletManagerClient implementation. 70 // 71 // NOTE, THE SECOND: It needs to register a unique name to the tmclient factory 72 // registry, so we keep a shadow map of factories registered for "protocols" by 73 // this function. That way, if we happen to have multiple tests with the same 74 // name, we can swap out the return value for the factory and allow both tests 75 // to run, rather than the second test failing when it attempts to register a 76 // second factory for the same "protocol" name. 77 // 78 // NOTE, THE THIRD: we take a "new" func to produce a valid 79 // vtctlservicepb.VtctldServer implementation, rather than constructing directly 80 // ourselves with grpcvtctldserver.NewVtctldServer. This is to prevent an import 81 // cycle between this package and package grpcvtctldserver. Further, because the 82 // return type of NewVtctldServer is the struct type 83 // (*grpcvtctldserver.VtctldServer) and not the interface type 84 // vtctlservicepb.VtctldServer, tests will need to indirect that call through an 85 // extra layer rather than passing the function identifier directly, e.g.: 86 // 87 // vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &testutil.TabletManagerClient{ 88 // ... 89 // }, func(ts *topo.Server) vtctlservicepb.VtctldServer { return NewVtctldServer(ts) }) 90 func NewVtctldServerWithTabletManagerClient(t testing.TB, ts *topo.Server, tmc tmclient.TabletManagerClient, newVtctldServerFn func(ts *topo.Server) vtctlservicepb.VtctldServer) vtctlservicepb.VtctldServer { 91 tmclientFactoryLock.Lock() 92 defer tmclientFactoryLock.Unlock() 93 94 protocol := t.Name() 95 96 if _, alreadyRegisteredFactory := tmclientFactories[protocol]; !alreadyRegisteredFactory { 97 factory := func() tmclient.TabletManagerClient { 98 tmclientLock.Lock() 99 defer tmclientLock.Unlock() 100 101 client, ok := tmclients[protocol] 102 if !ok { 103 t.Fatal("Test managed to register a factory for a client value that never got set; this should be impossible") 104 } 105 106 return client 107 } 108 109 tmclient.RegisterTabletManagerClientFactory(protocol, factory) 110 tmclientFactories[protocol] = factory 111 } 112 113 // Always swap in the new client return value for the given protocol name. 114 // We cannot defer the unlock here, because grpcvtctldserver.NewVtctldServer 115 // eventually will call into the factory we registered above, and we will 116 // deadlock ourselves. 117 tmclientLock.Lock() 118 tmclients[protocol] = tmc 119 tmclientLock.Unlock() 120 121 // Be (mostly, we can't help concurrent goroutines not using this function) 122 // atomic with our mutation of the global TabletManagerProtocol pointer. 123 reset := setTMClientProtocol(protocol) 124 defer reset() 125 126 return newVtctldServerFn(ts) 127 } 128 129 const ( 130 fsName = "go.vt.vtctl.grpcvtctldserver.testutil" 131 tmclientProtocolFlagName = "tablet_manager_protocol" 132 ) 133 134 var fs *pflag.FlagSet 135 136 // N.B. we cannot use tmclienttest.SetProtocol because it trips the race 137 // detector because of how many grpcvtctldserver tests run in parallel. 138 func setTMClientProtocol(protocol string) (reset func()) { 139 switch oldVal, err := fs.GetString(tmclientProtocolFlagName); err { 140 case nil: 141 reset = func() { setTMClientProtocol(oldVal) } 142 default: 143 log.Errorf("failed to get string value for flag %q: %v", tmclientProtocolFlagName, err) 144 reset = func() {} 145 } 146 147 if err := fs.Set(tmclientProtocolFlagName, protocol); err != nil { 148 msg := "failed to set flag %q to %q: %v" 149 log.Errorf(msg, tmclientProtocolFlagName, protocol, err) 150 reset = func() {} 151 } 152 153 return reset 154 } 155 156 func init() { 157 var tmp []string 158 tmp, os.Args = os.Args[:], []string{fsName} 159 defer func() { os.Args = tmp }() 160 161 // do this once at import-time before any tests run. 162 servenv.OnParseFor(fsName, func(_fs *pflag.FlagSet) { 163 fs = _fs 164 if fs.Lookup(tmclientProtocolFlagName) != nil { 165 return 166 } 167 168 tmclient.RegisterFlags(fs) 169 }) 170 servenv.ParseFlags(fsName) 171 } 172 173 // TabletManagerClient implements the tmclient.TabletManagerClient interface 174 // with mock delays and response values, for use in unit tests. 175 type TabletManagerClient struct { 176 tmclient.TabletManagerClient 177 // TopoServer is used for certain TabletManagerClient rpcs that update topo 178 // information, e.g. ChangeType. To force an error result for those rpcs in 179 // a test, set tmc.TopoServer = nil. 180 TopoServer *topo.Server 181 Backups map[string]struct { 182 Events []*logutilpb.Event 183 EventInterval time.Duration 184 EventJitter time.Duration 185 ErrorAfter time.Duration 186 } 187 // keyed by tablet alias. 188 ChangeTabletTypeResult map[string]error 189 // keyed by tablet alias. 190 DemotePrimaryDelays map[string]time.Duration 191 // keyed by tablet alias. 192 DemotePrimaryResults map[string]struct { 193 Status *replicationdatapb.PrimaryStatus 194 Error error 195 } 196 // keyed by tablet alias. 197 ExecuteFetchAsAppDelays map[string]time.Duration 198 // keyed by tablet alias. 199 ExecuteFetchAsAppResults map[string]struct { 200 Response *querypb.QueryResult 201 Error error 202 } 203 // keyed by tablet alias. 204 ExecuteFetchAsDbaDelays map[string]time.Duration 205 // keyed by tablet alias. 206 ExecuteFetchAsDbaResults map[string]struct { 207 Response *querypb.QueryResult 208 Error error 209 } 210 // keyed by tablet alias. 211 ExecuteHookDelays map[string]time.Duration 212 // keyed by tablet alias. 213 ExecuteHookResults map[string]struct { 214 Response *hk.HookResult 215 Error error 216 } 217 // FullStatus result 218 FullStatusResult *replicationdatapb.FullStatus 219 // keyed by tablet alias. 220 GetPermissionsDelays map[string]time.Duration 221 // keyed by tablet alias. 222 GetPermissionsResults map[string]struct { 223 Permissions *tabletmanagerdatapb.Permissions 224 Error error 225 } 226 // keyed by tablet alias. 227 GetReplicasResults map[string]struct { 228 Replicas []string 229 Error error 230 } 231 // keyed by tablet alias. 232 GetSchemaDelays map[string]time.Duration 233 // keyed by tablet alias. 234 GetSchemaResults map[string]struct { 235 Schema *tabletmanagerdatapb.SchemaDefinition 236 Error error 237 } 238 // keyed by tablet alias. 239 InitPrimaryDelays map[string]time.Duration 240 // keyed by tablet alias. injects a sleep to the end of the function 241 // regardless of parent context timeout or error result. 242 InitPrimaryPostDelays map[string]time.Duration 243 // keyed by tablet alias. 244 InitPrimaryResults map[string]struct { 245 Result string 246 Error error 247 } 248 // keyed by tablet alias. 249 PrimaryPositionDelays map[string]time.Duration 250 // keyed by tablet alias. 251 PrimaryPositionResults map[string]struct { 252 Position string 253 Error error 254 } 255 // keyed by tablet alias 256 PingDelays map[string]time.Duration 257 // keyed by tablet alias 258 PingResults map[string]error 259 // keyed by tablet alias. 260 PopulateReparentJournalDelays map[string]time.Duration 261 // keyed by tablet alias 262 PopulateReparentJournalResults map[string]error 263 // keyed by tablet alias. 264 PromoteReplicaDelays map[string]time.Duration 265 // keyed by tablet alias. injects a sleep to the end of the function 266 // regardless of parent context timeout or error result. 267 PromoteReplicaPostDelays map[string]time.Duration 268 // keyed by tablet alias. 269 PromoteReplicaResults map[string]struct { 270 Result string 271 Error error 272 } 273 // keyed by tablet alias. 274 RefreshStateResults map[string]error 275 // keyed by `<tablet_alias>/<wait_pos>`. 276 ReloadSchemaDelays map[string]time.Duration 277 // keyed by `<tablet_alias>/<wait_pos>`. 278 ReloadSchemaResults map[string]error 279 ReplicationStatusDelays map[string]time.Duration 280 ReplicationStatusResults map[string]struct { 281 Position *replicationdatapb.Status 282 Error error 283 } 284 RestoreFromBackupResults map[string]struct { 285 Events []*logutilpb.Event 286 EventInterval time.Duration 287 EventJitter time.Duration 288 ErrorAfter time.Duration 289 } 290 // keyed by tablet alias 291 RunHealthCheckDelays map[string]time.Duration 292 // keyed by tablet alias 293 RunHealthCheckResults map[string]error 294 // keyed by tablet alias. 295 SetReplicationSourceDelays map[string]time.Duration 296 // keyed by tablet alias. 297 SetReplicationSourceResults map[string]error 298 // keyed by tablet alias. 299 SetReplicationSourceSemiSync map[string]bool 300 // keyed by tablet alias 301 SetReadOnlyDelays map[string]time.Duration 302 // keyed by tablet alias 303 SetReadOnlyResults map[string]error 304 // keyed by tablet alias. 305 SetReadWriteDelays map[string]time.Duration 306 // keyed by tablet alias. 307 SetReadWriteResults map[string]error 308 // keyed by tablet alias 309 SleepDelays map[string]time.Duration 310 // keyed by tablet alias 311 SleepResults map[string]error 312 // keyed by tablet alias 313 StartReplicationDelays map[string]time.Duration 314 // keyed by tablet alias 315 StartReplicationResults map[string]error 316 // keyed by tablet alias 317 StopReplicationDelays map[string]time.Duration 318 // keyed by tablet alias 319 StopReplicationResults map[string]error 320 // keyed by tablet alias. 321 StopReplicationAndGetStatusDelays map[string]time.Duration 322 // keyed by tablet alias. 323 StopReplicationAndGetStatusResults map[string]struct { 324 StopStatus *replicationdatapb.StopReplicationStatus 325 Error error 326 } 327 // keyed by tablet alias. 328 UndoDemotePrimaryDelays map[string]time.Duration 329 // keyed by tablet alias 330 UndoDemotePrimaryResults map[string]error 331 // tablet alias => duration 332 VReplicationExecDelays map[string]time.Duration 333 // tablet alias => query string => result 334 VReplicationExecResults map[string]map[string]struct { 335 Result *querypb.QueryResult 336 Error error 337 } 338 // keyed by tablet alias. 339 WaitForPositionDelays map[string]time.Duration 340 // keyed by tablet alias. injects a sleep to the end of the function 341 // regardless of parent context timeout or error result. 342 WaitForPositionPostDelays map[string]time.Duration 343 // WaitForPosition(tablet *topodatapb.Tablet, position string) error, so we 344 // key by tablet alias and then by position. 345 WaitForPositionResults map[string]map[string]error 346 } 347 348 type backupStreamAdapter struct { 349 *grpcshim.BidiStream 350 ch chan *logutilpb.Event 351 } 352 353 func (stream *backupStreamAdapter) Recv() (*logutilpb.Event, error) { 354 select { 355 case <-stream.Context().Done(): 356 return nil, stream.Context().Err() 357 case err := <-stream.ErrCh: 358 return nil, err 359 case msg := <-stream.ch: 360 return msg, nil 361 case <-stream.Closed(): 362 return nil, stream.CloseErr() 363 } 364 } 365 366 func (stream *backupStreamAdapter) Send(msg *logutilpb.Event) error { 367 select { 368 case <-stream.Context().Done(): 369 return stream.Context().Err() 370 case <-stream.Closed(): 371 return grpcshim.ErrStreamClosed 372 case stream.ch <- msg: 373 return nil 374 } 375 } 376 377 // Backup is part of the tmclient.TabletManagerClient interface. 378 func (fake *TabletManagerClient) Backup(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.BackupRequest) (logutil.EventStream, error) { 379 if tablet.Type == topodatapb.TabletType_PRIMARY && !req.AllowPrimary { 380 return nil, fmt.Errorf("cannot backup primary with allowPrimary=false") 381 } 382 383 key := topoproto.TabletAliasString(tablet.Alias) 384 testdata, ok := fake.Backups[key] 385 if !ok { 386 return nil, fmt.Errorf("no Backup fake result set for %s", key) 387 } 388 389 stream := &backupStreamAdapter{ 390 BidiStream: grpcshim.NewBidiStream(ctx), 391 ch: make(chan *logutilpb.Event, len(testdata.Events)), 392 } 393 go func() { 394 if testdata.EventInterval == 0 { 395 testdata.EventInterval = 10 * time.Millisecond 396 log.Warningf("testutil.TabletManagerClient.Backup faked with no event interval for %s, defaulting to %s", key, testdata.EventInterval) 397 } 398 399 if testdata.EventJitter == 0 { 400 testdata.EventJitter = time.Millisecond 401 log.Warningf("testutil.TabletManagerClient.Backup faked with no event jitter for %s, defaulting to %s", key, testdata.EventJitter) 402 } 403 404 errCtx, errCancel := context.WithCancel(context.Background()) 405 switch testdata.ErrorAfter { 406 case 0: 407 // no error to send, cancel the error context immediately 408 errCancel() 409 default: 410 go func() { 411 timer := time.NewTimer(testdata.ErrorAfter) 412 defer func() { // Stop the timer and drain the channel. 413 if !timer.Stop() { 414 <-timer.C 415 } 416 }() 417 defer errCancel() 418 419 <-timer.C 420 stream.ErrCh <- fmt.Errorf("error triggered after %s", testdata.ErrorAfter) 421 }() 422 } 423 424 ticker := timer.NewRandTicker(testdata.EventInterval, testdata.EventJitter) 425 426 defer ticker.Stop() 427 defer stream.CloseWithError(nil) 428 429 for _, event := range testdata.Events { 430 stream.ch <- event 431 <-ticker.C 432 } 433 434 // Wait for the error goroutine to finish. Note that if ErrorAfter 435 // is zero, we never start the goroutine and cancel this context 436 // immediately. 437 // 438 // The reason for this select is so that the error goroutine does 439 // not attempt to send to stream.errCh after the call to CloseSend(). 440 <-errCtx.Done() 441 }() 442 443 return stream, nil 444 } 445 446 // ChangeType is part of the tmclient.TabletManagerClient interface. 447 func (fake *TabletManagerClient) ChangeType(ctx context.Context, tablet *topodatapb.Tablet, newType topodatapb.TabletType, semiSync bool) error { 448 if result, ok := fake.ChangeTabletTypeResult[topoproto.TabletAliasString(tablet.Alias)]; ok { 449 return result 450 } 451 452 if fake.TopoServer == nil { 453 return assert.AnError 454 } 455 456 _, err := topotools.ChangeType(ctx, fake.TopoServer, tablet.Alias, newType, &vttime.Time{}) 457 return err 458 } 459 460 // DemotePrimary is part of the tmclient.TabletManagerClient interface. 461 func (fake *TabletManagerClient) DemotePrimary(ctx context.Context, tablet *topodatapb.Tablet) (*replicationdatapb.PrimaryStatus, error) { 462 if fake.DemotePrimaryResults == nil { 463 return nil, assert.AnError 464 } 465 466 if tablet.Alias == nil { 467 return nil, assert.AnError 468 } 469 470 key := topoproto.TabletAliasString(tablet.Alias) 471 472 if fake.DemotePrimaryDelays != nil { 473 if delay, ok := fake.DemotePrimaryDelays[key]; ok { 474 select { 475 case <-ctx.Done(): 476 return nil, ctx.Err() 477 case <-time.After(delay): 478 // proceed to results 479 } 480 } 481 } 482 483 if result, ok := fake.DemotePrimaryResults[key]; ok { 484 return result.Status, result.Error 485 } 486 487 return nil, assert.AnError 488 } 489 490 // ExecuteFetchAsApp is part of the tmclient.TabletManagerClient interface. 491 func (fake *TabletManagerClient) ExecuteFetchAsApp(ctx context.Context, tablet *topodatapb.Tablet, usePool bool, req *tabletmanagerdatapb.ExecuteFetchAsAppRequest) (*querypb.QueryResult, error) { 492 if fake.ExecuteFetchAsAppResults == nil { 493 return nil, fmt.Errorf("%w: no ExecuteFetchAsApp results on fake TabletManagerClient", assert.AnError) 494 } 495 496 key := topoproto.TabletAliasString(tablet.Alias) 497 if fake.ExecuteFetchAsAppDelays != nil { 498 if delay, ok := fake.ExecuteFetchAsAppDelays[key]; ok { 499 select { 500 case <-ctx.Done(): 501 return nil, ctx.Err() 502 case <-time.After(delay): 503 // proceed to results 504 } 505 } 506 } 507 if result, ok := fake.ExecuteFetchAsAppResults[key]; ok { 508 return result.Response, result.Error 509 } 510 511 return nil, fmt.Errorf("%w: no ExecuteFetchAsApp result set for tablet %s", assert.AnError, key) 512 } 513 514 // ExecuteFetchAsDba is part of the tmclient.TabletManagerClient interface. 515 func (fake *TabletManagerClient) ExecuteFetchAsDba(ctx context.Context, tablet *topodatapb.Tablet, usePool bool, req *tabletmanagerdatapb.ExecuteFetchAsDbaRequest) (*querypb.QueryResult, error) { 516 if fake.ExecuteFetchAsDbaResults == nil { 517 return nil, fmt.Errorf("%w: no ExecuteFetchAsDba results on fake TabletManagerClient", assert.AnError) 518 } 519 520 key := topoproto.TabletAliasString(tablet.Alias) 521 if fake.ExecuteFetchAsDbaDelays != nil { 522 if delay, ok := fake.ExecuteFetchAsDbaDelays[key]; ok { 523 select { 524 case <-ctx.Done(): 525 return nil, ctx.Err() 526 case <-time.After(delay): 527 // proceed to results 528 } 529 } 530 } 531 if result, ok := fake.ExecuteFetchAsDbaResults[key]; ok { 532 return result.Response, result.Error 533 } 534 535 return nil, fmt.Errorf("%w: no ExecuteFetchAsDba result set for tablet %s", assert.AnError, key) 536 } 537 538 // ExecuteHook is part of the tmclient.TabletManagerClient interface. 539 func (fake *TabletManagerClient) ExecuteHook(ctx context.Context, tablet *topodatapb.Tablet, hook *hk.Hook) (*hk.HookResult, error) { 540 if fake.ExecuteHookResults == nil { 541 return nil, fmt.Errorf("%w: no ExecuteHook results on fake TabletManagerClient", assert.AnError) 542 } 543 544 key := topoproto.TabletAliasString(tablet.Alias) 545 if fake.ExecuteHookDelays != nil { 546 if delay, ok := fake.ExecuteHookDelays[key]; ok { 547 select { 548 case <-ctx.Done(): 549 return nil, ctx.Err() 550 case <-time.After(delay): 551 // proceed to results 552 } 553 } 554 } 555 if result, ok := fake.ExecuteHookResults[key]; ok { 556 return result.Response, result.Error 557 } 558 559 return nil, fmt.Errorf("%w: no ExecuteHook result set for tablet %s", assert.AnError, key) 560 } 561 562 // FullStatus is part of the tmclient.TabletManagerClient interface. 563 func (fake *TabletManagerClient) FullStatus(ctx context.Context, tablet *topodatapb.Tablet) (*replicationdatapb.FullStatus, error) { 564 if fake.FullStatusResult != nil { 565 return fake.FullStatusResult, nil 566 } 567 568 if fake.TopoServer == nil { 569 return nil, assert.AnError 570 } 571 572 return nil, fmt.Errorf("no output set for FullStatus") 573 } 574 575 // GetPermission is part of the tmclient.TabletManagerClient interface. 576 func (fake *TabletManagerClient) GetPermissions(ctx context.Context, tablet *topodatapb.Tablet) (*tabletmanagerdatapb.Permissions, error) { 577 if fake.GetPermissionsResults == nil { 578 return nil, assert.AnError 579 } 580 581 if tablet.Alias == nil { 582 return nil, assert.AnError 583 } 584 585 key := topoproto.TabletAliasString(tablet.Alias) 586 587 if fake.GetPermissionsDelays != nil { 588 if delay, ok := fake.GetPermissionsDelays[key]; ok { 589 select { 590 case <-ctx.Done(): 591 return nil, ctx.Err() 592 case <-time.After(delay): 593 // proceed to results 594 } 595 } 596 } 597 598 if result, ok := fake.GetPermissionsResults[key]; ok { 599 return result.Permissions, result.Error 600 } 601 602 return nil, fmt.Errorf("%w: no permissions for %s", assert.AnError, key) 603 } 604 605 // GetReplicas is part of the tmclient.TabletManagerClient interface. 606 func (fake *TabletManagerClient) GetReplicas(ctx context.Context, tablet *topodatapb.Tablet) ([]string, error) { 607 if fake.GetReplicasResults == nil { 608 return nil, fmt.Errorf("no results set on fake") 609 } 610 611 key := topoproto.TabletAliasString(tablet.Alias) 612 if result, ok := fake.GetReplicasResults[key]; ok { 613 return result.Replicas, result.Error 614 } 615 616 return nil, fmt.Errorf("no result set for %v", key) 617 } 618 619 // GetSchema is part of the tmclient.TabletManagerClient interface. 620 func (fake *TabletManagerClient) GetSchema(ctx context.Context, tablet *topodatapb.Tablet, request *tabletmanagerdatapb.GetSchemaRequest) (*tabletmanagerdatapb.SchemaDefinition, error) { 621 if fake.GetSchemaResults == nil { 622 return nil, assert.AnError 623 } 624 625 if tablet.Alias == nil { 626 return nil, assert.AnError 627 } 628 629 key := topoproto.TabletAliasString(tablet.Alias) 630 631 if fake.GetSchemaDelays != nil { 632 if delay, ok := fake.GetSchemaDelays[key]; ok { 633 select { 634 case <-ctx.Done(): 635 return nil, ctx.Err() 636 case <-time.After(delay): 637 // proceed to results 638 } 639 } 640 } 641 642 if result, ok := fake.GetSchemaResults[key]; ok { 643 return result.Schema, result.Error 644 } 645 646 return nil, fmt.Errorf("%w: no schemas for %s", assert.AnError, key) 647 } 648 649 // InitPrimary is part of the tmclient.TabletManagerClient interface. 650 func (fake *TabletManagerClient) InitPrimary(ctx context.Context, tablet *topodatapb.Tablet, semiSync bool) (string, error) { 651 if fake.InitPrimaryResults == nil { 652 return "", assert.AnError 653 } 654 655 key := topoproto.TabletAliasString(tablet.Alias) 656 657 defer func() { 658 if fake.InitPrimaryPostDelays == nil { 659 return 660 } 661 662 if delay, ok := fake.InitPrimaryPostDelays[key]; ok { 663 time.Sleep(delay) 664 } 665 }() 666 667 if fake.InitPrimaryDelays != nil { 668 if delay, ok := fake.InitPrimaryDelays[key]; ok { 669 select { 670 case <-ctx.Done(): 671 return "", ctx.Err() 672 case <-time.After(delay): 673 // proceed to results 674 } 675 } 676 } 677 678 if result, ok := fake.InitPrimaryResults[key]; ok { 679 return result.Result, result.Error 680 } 681 682 return "", assert.AnError 683 } 684 685 // PrimaryPosition is part of the tmclient.TabletManagerClient interface. 686 func (fake *TabletManagerClient) PrimaryPosition(ctx context.Context, tablet *topodatapb.Tablet) (string, error) { 687 if fake.PrimaryPositionResults == nil { 688 return "", assert.AnError 689 } 690 691 if tablet.Alias == nil { 692 return "", assert.AnError 693 } 694 695 key := topoproto.TabletAliasString(tablet.Alias) 696 697 if fake.PrimaryPositionDelays != nil { 698 if delay, ok := fake.PrimaryPositionDelays[key]; ok { 699 select { 700 case <-ctx.Done(): 701 return "", ctx.Err() 702 case <-time.After(delay): 703 // proceed to results 704 } 705 } 706 } 707 708 if result, ok := fake.PrimaryPositionResults[key]; ok { 709 return result.Position, result.Error 710 } 711 712 return "", assert.AnError 713 } 714 715 // Ping is part of the tmclient.TabletManagerClient interface. 716 func (fake *TabletManagerClient) Ping(ctx context.Context, tablet *topodatapb.Tablet) error { 717 if fake.PingResults == nil { 718 return assert.AnError 719 } 720 721 if tablet.Alias == nil { 722 return assert.AnError 723 } 724 725 key := topoproto.TabletAliasString(tablet.Alias) 726 727 if fake.PingDelays != nil { 728 if delay, ok := fake.PingDelays[key]; ok { 729 select { 730 case <-ctx.Done(): 731 return ctx.Err() 732 case <-time.After(delay): 733 // proceed to results 734 } 735 } 736 } 737 738 if err, ok := fake.PingResults[key]; ok { 739 return err 740 } 741 742 return fmt.Errorf("%w: no result for key %s", assert.AnError, key) 743 } 744 745 // PopulateReparentJournal is part of the tmclient.TabletManagerClient 746 // interface. 747 func (fake *TabletManagerClient) PopulateReparentJournal(ctx context.Context, tablet *topodatapb.Tablet, timeCreatedNS int64, actionName string, primaryAlias *topodatapb.TabletAlias, pos string) error { 748 if fake.PopulateReparentJournalResults == nil { 749 return assert.AnError 750 } 751 752 key := topoproto.TabletAliasString(tablet.Alias) 753 754 if fake.PopulateReparentJournalDelays != nil { 755 if delay, ok := fake.PopulateReparentJournalDelays[key]; ok { 756 select { 757 case <-ctx.Done(): 758 return ctx.Err() 759 case <-time.After(delay): 760 // proceed to results 761 } 762 } 763 } 764 if result, ok := fake.PopulateReparentJournalResults[key]; ok { 765 return result 766 } 767 768 return assert.AnError 769 } 770 771 // PromoteReplica is part of the tmclient.TabletManagerClient interface. 772 func (fake *TabletManagerClient) PromoteReplica(ctx context.Context, tablet *topodatapb.Tablet, semiSync bool) (string, error) { 773 if fake.PromoteReplicaResults == nil { 774 return "", assert.AnError 775 } 776 777 key := topoproto.TabletAliasString(tablet.Alias) 778 779 defer func() { 780 if fake.PromoteReplicaPostDelays == nil { 781 return 782 } 783 784 if delay, ok := fake.PromoteReplicaPostDelays[key]; ok { 785 time.Sleep(delay) 786 } 787 }() 788 789 if fake.PromoteReplicaDelays != nil { 790 if delay, ok := fake.PromoteReplicaDelays[key]; ok { 791 select { 792 case <-ctx.Done(): 793 return "", ctx.Err() 794 case <-time.After(delay): 795 // proceed to results 796 } 797 } 798 } 799 800 if result, ok := fake.PromoteReplicaResults[key]; ok { 801 return result.Result, result.Error 802 } 803 804 return "", assert.AnError 805 } 806 807 // RefreshState is part of the tmclient.TabletManagerClient interface. 808 func (fake *TabletManagerClient) RefreshState(ctx context.Context, tablet *topodatapb.Tablet) error { 809 if fake.RefreshStateResults == nil { 810 return fmt.Errorf("%w: no RefreshState results on fake TabletManagerClient", assert.AnError) 811 } 812 813 key := topoproto.TabletAliasString(tablet.Alias) 814 if err, ok := fake.RefreshStateResults[key]; ok { 815 return err 816 } 817 818 return fmt.Errorf("%w: no RefreshState result set for tablet %s", assert.AnError, key) 819 } 820 821 // ReloadSchema is part of the tmclient.TabletManagerClient interface. 822 func (fake *TabletManagerClient) ReloadSchema(ctx context.Context, tablet *topodatapb.Tablet, waitPosition string) error { 823 if fake.ReloadSchemaResults == nil { 824 return fmt.Errorf("%w: no ReloadSchema results on fake TabletManagerClient", assert.AnError) 825 } 826 827 key := path.Join(topoproto.TabletAliasString(tablet.Alias), waitPosition) 828 829 if fake.ReloadSchemaDelays != nil { 830 if delay, ok := fake.ReloadSchemaDelays[key]; ok { 831 select { 832 case <-ctx.Done(): 833 return ctx.Err() 834 case <-time.After(delay): 835 // proceed to results 836 } 837 } 838 } 839 840 if err, ok := fake.ReloadSchemaResults[key]; ok { 841 return err 842 } 843 844 return fmt.Errorf("%w: no ReloadSchema result set for tablet %s", assert.AnError, key) 845 } 846 847 // ReplicationStatus is part of the tmclient.TabletManagerClient interface. 848 func (fake *TabletManagerClient) ReplicationStatus(ctx context.Context, tablet *topodatapb.Tablet) (*replicationdatapb.Status, error) { 849 if fake.ReplicationStatusResults == nil { 850 return nil, assert.AnError 851 } 852 853 key := topoproto.TabletAliasString(tablet.Alias) 854 855 if fake.ReplicationStatusDelays != nil { 856 if delay, ok := fake.ReplicationStatusDelays[key]; ok { 857 select { 858 case <-ctx.Done(): 859 return nil, ctx.Err() 860 case <-time.After(delay): 861 // proceed to results 862 } 863 } 864 } 865 866 if result, ok := fake.ReplicationStatusResults[key]; ok { 867 return result.Position, result.Error 868 } 869 870 return nil, assert.AnError 871 } 872 873 type backupRestoreStreamAdapter struct { 874 *grpcshim.BidiStream 875 ch chan *logutilpb.Event 876 } 877 878 func (stream *backupRestoreStreamAdapter) Recv() (*logutilpb.Event, error) { 879 select { 880 case <-stream.Context().Done(): 881 return nil, stream.Context().Err() 882 case err := <-stream.ErrCh: 883 return nil, err 884 case msg := <-stream.ch: 885 return msg, nil 886 case <-stream.Closed(): 887 return nil, stream.CloseErr() 888 } 889 } 890 891 func (stream *backupRestoreStreamAdapter) Send(msg *logutilpb.Event) error { 892 select { 893 case <-stream.Context().Done(): 894 return stream.Context().Err() 895 case <-stream.Closed(): 896 return grpcshim.ErrStreamClosed 897 case stream.ch <- msg: 898 return nil 899 } 900 } 901 902 // RestoreFromBackup is part of the tmclient.TabletManagerClient interface. 903 func (fake *TabletManagerClient) RestoreFromBackup(ctx context.Context, tablet *topodatapb.Tablet, req *tabletmanagerdatapb.RestoreFromBackupRequest) (logutil.EventStream, error) { 904 key := topoproto.TabletAliasString(tablet.Alias) 905 testdata, ok := fake.RestoreFromBackupResults[key] 906 if !ok { 907 return nil, fmt.Errorf("no RestoreFromBackup fake result set for %s", key) 908 } 909 910 stream := &backupRestoreStreamAdapter{ 911 BidiStream: grpcshim.NewBidiStream(ctx), 912 ch: make(chan *logutilpb.Event, len(testdata.Events)), 913 } 914 go func() { 915 if testdata.EventInterval == 0 { 916 testdata.EventInterval = 10 * time.Millisecond 917 log.Warningf("testutil.TabletManagerClient.RestoreFromBackup faked with no event interval for %s, defaulting to %s", key, testdata.EventInterval) 918 } 919 920 if testdata.EventJitter == 0 { 921 testdata.EventJitter = time.Millisecond 922 log.Warningf("testutil.TabletManagerClient.RestoreFromBackup faked with no event jitter for %s, defaulting to %s", key, testdata.EventJitter) 923 } 924 925 errCtx, errCancel := context.WithCancel(context.Background()) 926 switch testdata.ErrorAfter { 927 case 0: 928 // no error to send, cancel the error context immediately 929 errCancel() 930 default: 931 go func() { 932 timer := time.NewTimer(testdata.ErrorAfter) 933 defer func() { // Stop the timer and drain the channel. 934 if !timer.Stop() { 935 <-timer.C 936 } 937 }() 938 defer errCancel() 939 940 <-timer.C 941 stream.ErrCh <- fmt.Errorf("error triggered after %s", testdata.ErrorAfter) 942 }() 943 } 944 945 ticker := timer.NewRandTicker(testdata.EventInterval, testdata.EventJitter) 946 947 defer ticker.Stop() 948 defer stream.CloseWithError(nil) 949 950 for _, event := range testdata.Events { 951 stream.ch <- event 952 <-ticker.C 953 } 954 955 // Wait for the error goroutine to finish. Note that if ErrorAfter 956 // is zero, we never start the goroutine and cancel this context 957 // immediately. 958 // 959 // The reason for this select is so that the error goroutine does 960 // not attempt to send to stream.errCh after the call to CloseSend(). 961 <-errCtx.Done() 962 }() 963 964 return stream, nil 965 } 966 967 // RunHealthCheck is part of the tmclient.TabletManagerClient interface. 968 func (fake *TabletManagerClient) RunHealthCheck(ctx context.Context, tablet *topodatapb.Tablet) error { 969 if fake.RunHealthCheckResults == nil { 970 return assert.AnError 971 } 972 973 if tablet.Alias == nil { 974 return assert.AnError 975 } 976 977 key := topoproto.TabletAliasString(tablet.Alias) 978 979 if fake.RunHealthCheckDelays != nil { 980 if delay, ok := fake.RunHealthCheckDelays[key]; ok { 981 select { 982 case <-ctx.Done(): 983 return ctx.Err() 984 case <-time.After(delay): 985 // proceed to results 986 } 987 } 988 } 989 990 if err, ok := fake.RunHealthCheckResults[key]; ok { 991 return err 992 } 993 994 return fmt.Errorf("%w: no result for key %s", assert.AnError, key) 995 } 996 997 // SetReplicationSource is part of the tmclient.TabletManagerClient interface. 998 func (fake *TabletManagerClient) SetReplicationSource(ctx context.Context, tablet *topodatapb.Tablet, parent *topodatapb.TabletAlias, timeCreatedNS int64, waitPosition string, forceStartReplication bool, semiSync bool) error { 999 if fake.SetReplicationSourceResults == nil { 1000 return assert.AnError 1001 } 1002 1003 key := topoproto.TabletAliasString(tablet.Alias) 1004 1005 if fake.SetReplicationSourceDelays != nil { 1006 if delay, ok := fake.SetReplicationSourceDelays[key]; ok { 1007 select { 1008 case <-ctx.Done(): 1009 return ctx.Err() 1010 case <-time.After(delay): 1011 // proceed to results 1012 } 1013 } 1014 } 1015 1016 if fake.SetReplicationSourceSemiSync != nil { 1017 if semiSyncRequirement, ok := fake.SetReplicationSourceSemiSync[key]; ok { 1018 if semiSyncRequirement != semiSync { 1019 return fmt.Errorf("semi-sync settings incorrect") 1020 } 1021 } 1022 } 1023 1024 if result, ok := fake.SetReplicationSourceResults[key]; ok { 1025 return result 1026 } 1027 1028 return assert.AnError 1029 } 1030 1031 // SetReadOnly is part of the tmclient.TabletManagerClient interface. 1032 func (fake *TabletManagerClient) SetReadOnly(ctx context.Context, tablet *topodatapb.Tablet) error { 1033 if fake.SetReadOnlyResults == nil { 1034 return assert.AnError 1035 } 1036 1037 if tablet.Alias == nil { 1038 return assert.AnError 1039 } 1040 1041 key := topoproto.TabletAliasString(tablet.Alias) 1042 1043 if fake.SetReadOnlyDelays != nil { 1044 if delay, ok := fake.SetReadOnlyDelays[key]; ok { 1045 select { 1046 case <-ctx.Done(): 1047 return ctx.Err() 1048 case <-time.After(delay): 1049 // proceed to results 1050 } 1051 } 1052 } 1053 1054 if err, ok := fake.SetReadOnlyResults[key]; ok { 1055 return err 1056 } 1057 1058 return fmt.Errorf("%w: no result for key %s", assert.AnError, key) 1059 } 1060 1061 // SetReadWrite is part of the tmclient.TabletManagerClient interface. 1062 func (fake *TabletManagerClient) SetReadWrite(ctx context.Context, tablet *topodatapb.Tablet) error { 1063 if fake.SetReadWriteResults == nil { 1064 return assert.AnError 1065 } 1066 1067 if tablet.Alias == nil { 1068 return assert.AnError 1069 } 1070 1071 key := topoproto.TabletAliasString(tablet.Alias) 1072 1073 if fake.SetReadWriteDelays != nil { 1074 if delay, ok := fake.SetReadWriteDelays[key]; ok { 1075 select { 1076 case <-ctx.Done(): 1077 return ctx.Err() 1078 case <-time.After(delay): 1079 // proceed to results 1080 } 1081 } 1082 } 1083 1084 if err, ok := fake.SetReadWriteResults[key]; ok { 1085 return err 1086 } 1087 1088 return fmt.Errorf("%w: no result for key %s", assert.AnError, key) 1089 } 1090 1091 // Sleep is part of the tmclient.TabletManagerClient interface. 1092 func (fake *TabletManagerClient) Sleep(ctx context.Context, tablet *topodatapb.Tablet, duration time.Duration) error { 1093 if fake.SleepResults == nil { 1094 return assert.AnError 1095 } 1096 1097 if tablet.Alias == nil { 1098 return assert.AnError 1099 } 1100 1101 key := topoproto.TabletAliasString(tablet.Alias) 1102 1103 if fake.SleepDelays != nil { 1104 if delay, ok := fake.SleepDelays[key]; ok { 1105 select { 1106 case <-ctx.Done(): 1107 return ctx.Err() 1108 case <-time.After(delay): 1109 // proceed to results 1110 } 1111 } 1112 } 1113 1114 if err, ok := fake.SleepResults[key]; ok { 1115 time.Sleep(duration) 1116 return err 1117 } 1118 1119 return fmt.Errorf("%w: no result for key %s", assert.AnError, key) 1120 } 1121 1122 // StartReplication is part of the tmclient.TabletManagerClient interface. 1123 func (fake *TabletManagerClient) StartReplication(ctx context.Context, tablet *topodatapb.Tablet, semiSync bool) error { 1124 if fake.StartReplicationResults == nil { 1125 return assert.AnError 1126 } 1127 1128 if tablet.Alias == nil { 1129 return assert.AnError 1130 } 1131 1132 key := topoproto.TabletAliasString(tablet.Alias) 1133 1134 if fake.StartReplicationDelays != nil { 1135 if delay, ok := fake.StartReplicationDelays[key]; ok { 1136 select { 1137 case <-ctx.Done(): 1138 return ctx.Err() 1139 case <-time.After(delay): 1140 // proceed to results 1141 } 1142 } 1143 } 1144 1145 if err, ok := fake.StartReplicationResults[key]; ok { 1146 return err 1147 } 1148 1149 return fmt.Errorf("%w: no result for key %s", assert.AnError, key) 1150 } 1151 1152 // StopReplication is part of the tmclient.TabletManagerClient interface. 1153 func (fake *TabletManagerClient) StopReplication(ctx context.Context, tablet *topodatapb.Tablet) error { 1154 if fake.StopReplicationResults == nil { 1155 return assert.AnError 1156 } 1157 1158 if tablet.Alias == nil { 1159 return assert.AnError 1160 } 1161 1162 key := topoproto.TabletAliasString(tablet.Alias) 1163 1164 if fake.StopReplicationDelays != nil { 1165 if delay, ok := fake.StopReplicationDelays[key]; ok { 1166 select { 1167 case <-ctx.Done(): 1168 return ctx.Err() 1169 case <-time.After(delay): 1170 // proceed to results 1171 } 1172 } 1173 } 1174 1175 if err, ok := fake.StopReplicationResults[key]; ok { 1176 return err 1177 } 1178 1179 return fmt.Errorf("%w: no result for key %s", assert.AnError, key) 1180 } 1181 1182 // StopReplicationAndGetStatus is part of the tmclient.TabletManagerClient 1183 // interface. 1184 func (fake *TabletManagerClient) StopReplicationAndGetStatus(ctx context.Context, tablet *topodatapb.Tablet, mode replicationdatapb.StopReplicationMode) (*replicationdatapb.StopReplicationStatus, error) { 1185 if fake.StopReplicationAndGetStatusResults == nil { 1186 return nil, assert.AnError 1187 } 1188 1189 if tablet.Alias == nil { 1190 return nil, assert.AnError 1191 } 1192 1193 key := topoproto.TabletAliasString(tablet.Alias) 1194 1195 if fake.StopReplicationAndGetStatusDelays != nil { 1196 if delay, ok := fake.StopReplicationAndGetStatusDelays[key]; ok { 1197 select { 1198 case <-ctx.Done(): 1199 return nil, ctx.Err() 1200 case <-time.After(delay): 1201 // proceed to results 1202 } 1203 } 1204 } 1205 1206 if result, ok := fake.StopReplicationAndGetStatusResults[key]; ok { 1207 return result.StopStatus, result.Error 1208 } 1209 1210 return nil, assert.AnError 1211 } 1212 1213 // WaitForPosition is part of the tmclient.TabletManagerClient interface. 1214 func (fake *TabletManagerClient) WaitForPosition(ctx context.Context, tablet *topodatapb.Tablet, position string) error { 1215 tabletKey := topoproto.TabletAliasString(tablet.Alias) 1216 1217 defer func() { 1218 if fake.WaitForPositionPostDelays == nil { 1219 return 1220 } 1221 1222 if delay, ok := fake.WaitForPositionPostDelays[tabletKey]; ok { 1223 time.Sleep(delay) 1224 } 1225 }() 1226 1227 if fake.WaitForPositionDelays != nil { 1228 if delay, ok := fake.WaitForPositionDelays[tabletKey]; ok { 1229 select { 1230 case <-ctx.Done(): 1231 return ctx.Err() 1232 case <-time.After(delay): 1233 // proceed to results 1234 } 1235 } 1236 } 1237 1238 if fake.WaitForPositionResults == nil { 1239 return assert.AnError 1240 } 1241 1242 tabletResultsByPosition, ok := fake.WaitForPositionResults[tabletKey] 1243 if !ok { 1244 return assert.AnError 1245 } 1246 1247 result, ok := tabletResultsByPosition[position] 1248 if !ok { 1249 return assert.AnError 1250 } 1251 1252 return result 1253 } 1254 1255 // UndoDemotePrimary is part of the tmclient.TabletManagerClient interface. 1256 func (fake *TabletManagerClient) UndoDemotePrimary(ctx context.Context, tablet *topodatapb.Tablet, semiSync bool) error { 1257 if fake.UndoDemotePrimaryResults == nil { 1258 return assert.AnError 1259 } 1260 1261 if tablet.Alias == nil { 1262 return assert.AnError 1263 } 1264 1265 key := topoproto.TabletAliasString(tablet.Alias) 1266 1267 if fake.UndoDemotePrimaryDelays != nil { 1268 if delay, ok := fake.UndoDemotePrimaryDelays[key]; ok { 1269 select { 1270 case <-ctx.Done(): 1271 return ctx.Err() 1272 case <-time.After(delay): 1273 // proceed to results 1274 } 1275 } 1276 } 1277 1278 if result, ok := fake.UndoDemotePrimaryResults[key]; ok { 1279 return result 1280 } 1281 1282 return assert.AnError 1283 } 1284 1285 // VReplicationExec is part of the tmclient.TabletManagerCLient interface. 1286 func (fake *TabletManagerClient) VReplicationExec(ctx context.Context, tablet *topodatapb.Tablet, query string) (*querypb.QueryResult, error) { 1287 if fake.VReplicationExecResults == nil { 1288 return nil, assert.AnError 1289 } 1290 1291 if tablet.Alias == nil { 1292 return nil, assert.AnError 1293 } 1294 1295 key := topoproto.TabletAliasString(tablet.Alias) 1296 1297 if fake.VReplicationExecDelays != nil { 1298 if delay, ok := fake.VReplicationExecDelays[key]; ok { 1299 select { 1300 case <-ctx.Done(): 1301 return nil, ctx.Err() 1302 case <-time.After(delay): 1303 // proceed to results 1304 } 1305 } 1306 } 1307 1308 if resultsForTablet, ok := fake.VReplicationExecResults[key]; ok { 1309 // Round trip the expected query both to ensure it's valid and to 1310 // standardize on capitalization and formatting. 1311 stmt, err := sqlparser.Parse(query) 1312 if err != nil { 1313 return nil, err 1314 } 1315 1316 buf := sqlparser.NewTrackedBuffer(nil) 1317 buf.Myprintf("%v", stmt) 1318 1319 parsedQuery := buf.ParsedQuery().Query 1320 1321 // Now do the map lookup. 1322 if result, ok := resultsForTablet[parsedQuery]; ok { 1323 return result.Result, result.Error 1324 } 1325 } 1326 1327 return nil, assert.AnError 1328 }