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  }