github.com/m3db/m3@v1.5.0/src/dbnode/integration/setup.go (about)

     1  // Copyright (c) 2016 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package integration
    22  
    23  import (
    24  	"errors"
    25  	"flag"
    26  	"fmt"
    27  	"io/ioutil"
    28  	"os"
    29  	"os/exec"
    30  	"strings"
    31  	"sync"
    32  	"testing"
    33  	"time"
    34  
    35  	"github.com/stretchr/testify/require"
    36  	"github.com/uber-go/tally"
    37  	"github.com/uber/tchannel-go"
    38  	"go.uber.org/zap"
    39  	"go.uber.org/zap/zapcore"
    40  
    41  	"github.com/m3db/m3/src/cluster/services"
    42  	"github.com/m3db/m3/src/cluster/shard"
    43  	"github.com/m3db/m3/src/dbnode/client"
    44  	"github.com/m3db/m3/src/dbnode/generated/thrift/rpc"
    45  	"github.com/m3db/m3/src/dbnode/integration/fake"
    46  	"github.com/m3db/m3/src/dbnode/integration/generate"
    47  	"github.com/m3db/m3/src/dbnode/namespace"
    48  	"github.com/m3db/m3/src/dbnode/persist/fs"
    49  	"github.com/m3db/m3/src/dbnode/persist/fs/commitlog"
    50  	"github.com/m3db/m3/src/dbnode/retention"
    51  	"github.com/m3db/m3/src/dbnode/runtime"
    52  	"github.com/m3db/m3/src/dbnode/server"
    53  	"github.com/m3db/m3/src/dbnode/sharding"
    54  	"github.com/m3db/m3/src/dbnode/storage"
    55  	"github.com/m3db/m3/src/dbnode/storage/block"
    56  	"github.com/m3db/m3/src/dbnode/storage/bootstrap"
    57  	"github.com/m3db/m3/src/dbnode/storage/bootstrap/bootstrapper"
    58  	bcl "github.com/m3db/m3/src/dbnode/storage/bootstrap/bootstrapper/commitlog"
    59  	bfs "github.com/m3db/m3/src/dbnode/storage/bootstrap/bootstrapper/fs"
    60  	"github.com/m3db/m3/src/dbnode/storage/bootstrap/bootstrapper/uninitialized"
    61  	"github.com/m3db/m3/src/dbnode/storage/cluster"
    62  	"github.com/m3db/m3/src/dbnode/storage/index"
    63  	"github.com/m3db/m3/src/dbnode/storage/series"
    64  	"github.com/m3db/m3/src/dbnode/testdata/prototest"
    65  	"github.com/m3db/m3/src/dbnode/topology"
    66  	"github.com/m3db/m3/src/dbnode/ts"
    67  	"github.com/m3db/m3/src/x/clock"
    68  	"github.com/m3db/m3/src/x/ident"
    69  	"github.com/m3db/m3/src/x/instrument"
    70  	xsync "github.com/m3db/m3/src/x/sync"
    71  	xtime "github.com/m3db/m3/src/x/time"
    72  )
    73  
    74  var (
    75  	id                  = flag.String("id", "", "Node host ID")
    76  	httpClusterAddr     = flag.String("clusterhttpaddr", "127.0.0.1:9000", "Cluster HTTP server address")
    77  	tchannelClusterAddr = flag.String("clustertchanneladdr", "127.0.0.1:9001", "Cluster TChannel server address")
    78  	httpNodeAddr        = flag.String("nodehttpaddr", "127.0.0.1:9002", "Node HTTP server address")
    79  	tchannelNodeAddr    = flag.String("nodetchanneladdr", "127.0.0.1:9003", "Node TChannel server address")
    80  	httpDebugAddr       = flag.String("debughttpaddr", "127.0.0.1:9004", "HTTP debug server address")
    81  
    82  	errServerStartTimedOut = errors.New("server took too long to start")
    83  	errServerStopTimedOut  = errors.New("server took too long to stop")
    84  	testNamespaces         = []ident.ID{ident.StringID("testNs1"), ident.StringID("testNs2")}
    85  
    86  	testSchemaHistory = prototest.NewSchemaHistory()
    87  	testSchema        = prototest.NewMessageDescriptor(testSchemaHistory)
    88  	testProtoMessages = prototest.NewProtoTestMessages(testSchema)
    89  	testProtoIter     = prototest.NewProtoMessageIterator(testProtoMessages)
    90  )
    91  
    92  // nowSetterFn is the function that sets the current time
    93  type nowSetterFn func(t xtime.UnixNano)
    94  
    95  type assertTestDataEqual func(t *testing.T, expected, actual []generate.TestValue) bool
    96  
    97  var _ topology.MapProvider = &testSetup{}
    98  
    99  type testSetup struct {
   100  	t         *testing.T
   101  	opts      TestOptions
   102  	schemaReg namespace.SchemaRegistry
   103  
   104  	logger *zap.Logger
   105  	scope  tally.TestScope
   106  
   107  	db                cluster.Database
   108  	storageOpts       storage.Options
   109  	instrumentOpts    instrument.Options
   110  	serverStorageOpts server.StorageOptions
   111  	fsOpts            fs.Options
   112  	blockLeaseManager block.LeaseManager
   113  	hostID            string
   114  	origin            topology.Host
   115  	topoInit          topology.Initializer
   116  	shardSet          sharding.ShardSet
   117  	getNowFn          xNowFn
   118  	clockNowFn        clock.NowFn
   119  	setNowFn          nowSetterFn
   120  	tchannelClient    *TestTChannelClient
   121  	m3dbClient        client.Client
   122  	// We need two distinct clients where one has the origin set to the same ID as the
   123  	// node itself (I.E) the client will behave exactly as if it is the node itself
   124  	// making requests, and another client with the origin set to an ID different than
   125  	// the node itself so that we can make requests from the perspective of a "different"
   126  	// M3DB node for verification purposes in some of the tests.
   127  	m3dbAdminClient             client.AdminClient
   128  	m3dbVerificationAdminClient client.AdminClient
   129  	workerPool                  xsync.WorkerPool
   130  
   131  	// compare expected with actual data function
   132  	assertEqual assertTestDataEqual
   133  
   134  	// things that need to be cleaned up
   135  	channel        *tchannel.Channel
   136  	filePathPrefix string
   137  	namespaces     []namespace.Metadata
   138  
   139  	// signals
   140  	doneCh   chan struct{}
   141  	closedCh chan struct{}
   142  }
   143  
   144  type xNowFn func() xtime.UnixNano
   145  
   146  // TestSetup is a test setup.
   147  type TestSetup interface {
   148  	topology.MapProvider
   149  
   150  	Opts() TestOptions
   151  	SetOpts(TestOptions)
   152  	FilesystemOpts() fs.Options
   153  	AssertEqual(*testing.T, []generate.TestValue, []generate.TestValue) bool
   154  	DB() cluster.Database
   155  	Scope() tally.TestScope
   156  	M3DBClient() client.Client
   157  	M3DBVerificationAdminClient() client.AdminClient
   158  	TChannelClient() *TestTChannelClient
   159  	Namespaces() []namespace.Metadata
   160  	TopologyInitializer() topology.Initializer
   161  	SetTopologyInitializer(topology.Initializer)
   162  	Fetch(req *rpc.FetchRequest) ([]generate.TestValue, error)
   163  	FilePathPrefix() string
   164  	StorageOpts() storage.Options
   165  	SetStorageOpts(storage.Options)
   166  	SetServerStorageOpts(server.StorageOptions)
   167  	Origin() topology.Host
   168  	ServerIsBootstrapped() bool
   169  	StopServer() error
   170  	StopServerAndVerifyOpenFilesAreClosed() error
   171  	StartServer() error
   172  	StartServerDontWaitBootstrap() error
   173  	NowFn() xNowFn
   174  	ClockNowFn() clock.NowFn
   175  	SetNowFn(xtime.UnixNano)
   176  	Close()
   177  	WriteBatch(ident.ID, generate.SeriesBlock) error
   178  	ShouldBeEqual() bool
   179  	// *NOTE*: This method is deprecated and should not be used in future tests.
   180  	// Also, we should migrate existing tests when we touch them away from using this.
   181  	SleepFor10xTickMinimumInterval()
   182  	BlockLeaseManager() block.LeaseManager
   183  	ShardSet() sharding.ShardSet
   184  	SetShardSet(sharding.ShardSet)
   185  	GeneratorOptions(retention.Options) generate.Options
   186  	MaybeResetClients() error
   187  	SchemaRegistry() namespace.SchemaRegistry
   188  	NamespaceMetadataOrFail(ident.ID) namespace.Metadata
   189  	MustSetTickMinimumInterval(time.Duration)
   190  	WaitUntilServerIsBootstrapped() error
   191  	WaitUntilServerIsUp() error
   192  	WaitUntilServerIsDown() error
   193  	Truncate(*rpc.TruncateRequest) (int64, error)
   194  	InitializeBootstrappers(opts InitializeBootstrappersOptions) error
   195  }
   196  
   197  // StorageOption is a reference to storage options function.
   198  type StorageOption func(storage.Options) storage.Options
   199  
   200  // NewTestSetup returns a new test setup for non-dockerized integration tests.
   201  func NewTestSetup(
   202  	t *testing.T,
   203  	opts TestOptions,
   204  	fsOpts fs.Options,
   205  	storageOptFns ...StorageOption,
   206  ) (TestSetup, error) {
   207  	if opts == nil {
   208  		opts = NewTestOptions(t)
   209  	}
   210  
   211  	nsInit := opts.NamespaceInitializer()
   212  	if nsInit == nil {
   213  		nsInit = namespace.NewStaticInitializer(opts.Namespaces())
   214  	}
   215  
   216  	zapConfig := zap.NewDevelopmentConfig()
   217  	zapConfig.DisableCaller = true
   218  	zapConfig.Level = zap.NewAtomicLevelAt(zapcore.InfoLevel)
   219  	if level := os.Getenv("LOG_LEVEL"); level != "" {
   220  		var parsedLevel zap.AtomicLevel
   221  		if err := parsedLevel.UnmarshalText([]byte(level)); err != nil {
   222  			return nil, fmt.Errorf("unable to parse log level: %v", err)
   223  		}
   224  		zapConfig.Level = parsedLevel
   225  	}
   226  	logger, err := zapConfig.Build()
   227  	if err != nil {
   228  		return nil, err
   229  	}
   230  
   231  	// Schema registry is shared between database and admin client.
   232  	schemaReg := namespace.NewSchemaRegistry(opts.ProtoEncoding(), nil)
   233  
   234  	blockLeaseManager := block.NewLeaseManager(nil)
   235  	storageOpts := storage.NewOptions().
   236  		SetNamespaceInitializer(nsInit).
   237  		SetSchemaRegistry(schemaReg).
   238  		SetBlockLeaseManager(blockLeaseManager)
   239  
   240  	if opts.ProtoEncoding() {
   241  		blockOpts := storageOpts.DatabaseBlockOptions().
   242  			SetEncoderPool(prototest.ProtoPools.EncoderPool).
   243  			SetReaderIteratorPool(prototest.ProtoPools.ReaderIterPool).
   244  			SetMultiReaderIteratorPool(prototest.ProtoPools.MultiReaderIterPool)
   245  		storageOpts = storageOpts.
   246  			SetDatabaseBlockOptions(blockOpts).
   247  			SetEncoderPool(prototest.ProtoPools.EncoderPool).
   248  			SetReaderIteratorPool(prototest.ProtoPools.ReaderIterPool).
   249  			SetMultiReaderIteratorPool(prototest.ProtoPools.MultiReaderIterPool)
   250  	}
   251  
   252  	if strings.ToLower(os.Getenv("TEST_DEBUG_LOG")) == "true" {
   253  		zapConfig.Level = zap.NewAtomicLevelAt(zapcore.DebugLevel)
   254  		logger, err = zapConfig.Build()
   255  		if err != nil {
   256  			return nil, err
   257  		}
   258  		storageOpts = storageOpts.SetInstrumentOptions(
   259  			storageOpts.InstrumentOptions().SetLogger(logger))
   260  	}
   261  
   262  	scope := tally.NewTestScope("", nil)
   263  	storageOpts = storageOpts.SetInstrumentOptions(
   264  		storageOpts.InstrumentOptions().SetMetricsScope(scope))
   265  
   266  	// Use specified series cache policy from environment if set.
   267  	seriesCachePolicy := strings.ToLower(os.Getenv("TEST_SERIES_CACHE_POLICY"))
   268  	if seriesCachePolicy != "" {
   269  		value, err := series.ParseCachePolicy(seriesCachePolicy)
   270  		if err != nil {
   271  			return nil, err
   272  		}
   273  		storageOpts = storageOpts.SetSeriesCachePolicy(value)
   274  	}
   275  
   276  	fields := []zapcore.Field{
   277  		zap.Stringer("cache-policy", storageOpts.SeriesCachePolicy()),
   278  	}
   279  	logger = logger.With(fields...)
   280  	instrumentOpts := storageOpts.InstrumentOptions().SetLogger(logger)
   281  	storageOpts = storageOpts.SetInstrumentOptions(instrumentOpts)
   282  
   283  	indexMode := index.InsertSync
   284  	if opts.WriteNewSeriesAsync() {
   285  		indexMode = index.InsertAsync
   286  	}
   287  
   288  	plCache, err := index.NewPostingsListCache(10, index.PostingsListCacheOptions{
   289  		InstrumentOptions: instrumentOpts,
   290  	})
   291  	if err != nil {
   292  		return nil, fmt.Errorf("unable to create postings list cache: %v", err)
   293  	}
   294  	// Ok to run immediately since it just closes the background reporting loop. Only ok because
   295  	// this is a test setup, in production we would want the metrics.
   296  	plCache.Start()()
   297  
   298  	indexOpts := storageOpts.IndexOptions().
   299  		SetInsertMode(indexMode).
   300  		SetPostingsListCache(plCache)
   301  	storageOpts = storageOpts.SetIndexOptions(indexOpts)
   302  
   303  	runtimeOptsMgr := storageOpts.RuntimeOptionsManager()
   304  	runtimeOpts := runtimeOptsMgr.Get().
   305  		SetTickMinimumInterval(opts.TickMinimumInterval()).
   306  		SetTickCancellationCheckInterval(opts.TickCancellationCheckInterval()).
   307  		SetMaxWiredBlocks(opts.MaxWiredBlocks()).
   308  		SetWriteNewSeriesAsync(opts.WriteNewSeriesAsync())
   309  	if err := runtimeOptsMgr.Update(runtimeOpts); err != nil {
   310  		return nil, err
   311  	}
   312  
   313  	// Set up shard set.
   314  	shardSet, err := newTestShardSet(opts.NumShards(), opts.ShardSetOptions())
   315  	if err != nil {
   316  		return nil, err
   317  	}
   318  
   319  	id := *id
   320  	if id == "" {
   321  		id = opts.ID()
   322  	}
   323  
   324  	tchannelNodeAddr := *tchannelNodeAddr
   325  	if addr := opts.TChannelNodeAddr(); addr != "" {
   326  		tchannelNodeAddr = addr
   327  	}
   328  
   329  	topoInit := opts.ClusterDatabaseTopologyInitializer()
   330  	if topoInit == nil {
   331  		topoInit, err = newTopologyInitializerForShardSet(id, tchannelNodeAddr, shardSet)
   332  		if err != nil {
   333  			return nil, err
   334  		}
   335  	}
   336  
   337  	adminClient, verificationAdminClient, err := newClients(topoInit, opts,
   338  		schemaReg, id, tchannelNodeAddr, instrumentOpts)
   339  	if err != nil {
   340  		return nil, err
   341  	}
   342  
   343  	// Set up tchannel client
   344  	tchanClient, err := NewTChannelClient("integration-test", tchannelNodeAddr)
   345  	if err != nil {
   346  		return nil, err
   347  	}
   348  
   349  	// Set up worker pool
   350  	workerPool := xsync.NewWorkerPool(opts.WorkerPoolSize())
   351  	workerPool.Init()
   352  
   353  	// BlockSizes are specified per namespace, make best effort at finding
   354  	// a value to align `now` for all of them.
   355  	truncateSize, guess := guessBestTruncateBlockSize(opts.Namespaces())
   356  	if guess {
   357  		logger.Warn("unable to find a single blockSize from known retention periods",
   358  			zap.String("guessing", truncateSize.String()))
   359  	}
   360  
   361  	// Set up getter and setter for now
   362  	var lock sync.RWMutex
   363  	now := xtime.Now().Truncate(truncateSize)
   364  	getNowFn := func() xtime.UnixNano {
   365  		lock.RLock()
   366  		t := now
   367  		lock.RUnlock()
   368  		return t
   369  	}
   370  	clockNowFn := func() time.Time {
   371  		return getNowFn().ToTime()
   372  	}
   373  	setNowFn := func(t xtime.UnixNano) {
   374  		lock.Lock()
   375  		now = t
   376  		lock.Unlock()
   377  	}
   378  	if overrideTimeNow := opts.NowFn(); overrideTimeNow != nil {
   379  		// Allow overriding the frozen time
   380  		storageOpts = storageOpts.SetClockOptions(
   381  			storageOpts.ClockOptions().SetNowFn(overrideTimeNow))
   382  	} else {
   383  		storageOpts = storageOpts.SetClockOptions(
   384  			storageOpts.ClockOptions().SetNowFn(clockNowFn))
   385  	}
   386  
   387  	// Set up file path prefix
   388  	filePathPrefix := opts.FilePathPrefix()
   389  	if filePathPrefix == "" {
   390  		var err error
   391  		filePathPrefix, err = ioutil.TempDir("", "integration-test")
   392  		if err != nil {
   393  			return nil, err
   394  		}
   395  	}
   396  
   397  	if fsOpts == nil {
   398  		fsOpts = fs.NewOptions().
   399  			SetFilePathPrefix(filePathPrefix).
   400  			SetClockOptions(storageOpts.ClockOptions())
   401  	}
   402  
   403  	storageOpts = storageOpts.SetCommitLogOptions(
   404  		storageOpts.CommitLogOptions().
   405  			SetFilesystemOptions(fsOpts))
   406  
   407  	// Set up persistence manager
   408  	pm, err := fs.NewPersistManager(fsOpts)
   409  	if err != nil {
   410  		return nil, err
   411  	}
   412  	storageOpts = storageOpts.SetPersistManager(pm)
   413  
   414  	// Set up index claims manager
   415  	icm, err := fs.NewIndexClaimsManager(fsOpts)
   416  	if err != nil {
   417  		return nil, err
   418  	}
   419  	storageOpts = storageOpts.SetIndexClaimsManager(icm)
   420  
   421  	// Set up repair options
   422  	storageOpts = storageOpts.
   423  		SetRepairOptions(storageOpts.RepairOptions().
   424  			SetAdminClients([]client.AdminClient{adminClient}))
   425  
   426  	// Set up block retriever manager
   427  	if mgr := opts.DatabaseBlockRetrieverManager(); mgr != nil {
   428  		storageOpts = storageOpts.SetDatabaseBlockRetrieverManager(mgr)
   429  	} else {
   430  		switch storageOpts.SeriesCachePolicy() {
   431  		case series.CacheAll:
   432  			// Do not need a block retriever for CacheAll policy
   433  		default:
   434  			blockRetrieverMgr := block.NewDatabaseBlockRetrieverManager(
   435  				func(md namespace.Metadata, shardSet sharding.ShardSet) (block.DatabaseBlockRetriever, error) {
   436  					retrieverOpts := fs.NewBlockRetrieverOptions().
   437  						SetBlockLeaseManager(blockLeaseManager)
   438  					retriever, err := fs.NewBlockRetriever(retrieverOpts, fsOpts)
   439  					if err != nil {
   440  						return nil, err
   441  					}
   442  
   443  					if err := retriever.Open(md, shardSet); err != nil {
   444  						return nil, err
   445  					}
   446  					return retriever, nil
   447  				})
   448  			storageOpts = storageOpts.
   449  				SetDatabaseBlockRetrieverManager(blockRetrieverMgr)
   450  		}
   451  	}
   452  
   453  	// Set up wired list if required
   454  	if storageOpts.SeriesCachePolicy() == series.CacheLRU {
   455  		wiredList := block.NewWiredList(block.WiredListOptions{
   456  			RuntimeOptionsManager: runtimeOptsMgr,
   457  			InstrumentOptions:     storageOpts.InstrumentOptions(),
   458  			ClockOptions:          storageOpts.ClockOptions(),
   459  			// Use a small event channel size to stress-test the implementation
   460  			EventsChannelSize: 1,
   461  		})
   462  		blockOpts := storageOpts.DatabaseBlockOptions().SetWiredList(wiredList)
   463  		blockPool := block.NewDatabaseBlockPool(nil)
   464  		// Have to manually set the blockpool because the default one uses a constructor
   465  		// function that doesn't have the updated blockOpts.
   466  		blockPool.Init(func() block.DatabaseBlock {
   467  			return block.NewDatabaseBlock(0, 0, ts.Segment{}, blockOpts, namespace.Context{})
   468  		})
   469  		blockOpts = blockOpts.SetDatabaseBlockPool(blockPool)
   470  		storageOpts = storageOpts.SetDatabaseBlockOptions(blockOpts)
   471  	}
   472  
   473  	storageOpts = storageOpts.SetInstrumentOptions(
   474  		storageOpts.InstrumentOptions().SetReportInterval(opts.ReportInterval()))
   475  
   476  	// Set debugging options if environment vars set
   477  	if debugFilePrefix := os.Getenv("TEST_DEBUG_FILE_PREFIX"); debugFilePrefix != "" {
   478  		opts = opts.SetVerifySeriesDebugFilePathPrefix(debugFilePrefix)
   479  	}
   480  
   481  	for _, fn := range storageOptFns {
   482  		if fn != nil {
   483  			storageOpts = fn(storageOpts)
   484  		}
   485  	}
   486  	if storageOpts != nil && storageOpts.AdminClient() == nil {
   487  		storageOpts = storageOpts.SetAdminClient(adminClient)
   488  	}
   489  
   490  	return &testSetup{
   491  		t:                           t,
   492  		opts:                        opts,
   493  		schemaReg:                   schemaReg,
   494  		logger:                      logger,
   495  		scope:                       scope,
   496  		storageOpts:                 storageOpts,
   497  		blockLeaseManager:           blockLeaseManager,
   498  		instrumentOpts:              instrumentOpts,
   499  		fsOpts:                      fsOpts,
   500  		hostID:                      id,
   501  		origin:                      newOrigin(id, tchannelNodeAddr),
   502  		topoInit:                    topoInit,
   503  		shardSet:                    shardSet,
   504  		getNowFn:                    getNowFn,
   505  		clockNowFn:                  clockNowFn,
   506  		setNowFn:                    setNowFn,
   507  		tchannelClient:              tchanClient,
   508  		m3dbClient:                  adminClient.(client.Client),
   509  		m3dbAdminClient:             adminClient,
   510  		m3dbVerificationAdminClient: verificationAdminClient,
   511  		workerPool:                  workerPool,
   512  		filePathPrefix:              filePathPrefix,
   513  		namespaces:                  opts.Namespaces(),
   514  		doneCh:                      make(chan struct{}),
   515  		closedCh:                    make(chan struct{}),
   516  		assertEqual:                 opts.AssertTestDataEqual(),
   517  	}, nil
   518  }
   519  
   520  // guestBestTruncateBlockSize guesses for the best block size to truncate testSetup's nowFn
   521  func guessBestTruncateBlockSize(mds []namespace.Metadata) (time.Duration, bool) {
   522  	// gcd of a pair of numbers
   523  	gcd := func(a, b int64) int64 {
   524  		for b > 0 {
   525  			a, b = b, a%b
   526  		}
   527  		return a
   528  	}
   529  	lcm := func(a, b int64) int64 {
   530  		return a * b / gcd(a, b)
   531  	}
   532  
   533  	// default guess
   534  	if len(mds) == 0 {
   535  		return time.Hour, true
   536  	}
   537  
   538  	// get all known blocksizes
   539  	blockSizes := make(map[int64]struct{})
   540  	for _, md := range mds {
   541  		bs := md.Options().RetentionOptions().BlockSize().Nanoseconds() / int64(time.Millisecond)
   542  		blockSizes[bs] = struct{}{}
   543  	}
   544  
   545  	first := true
   546  	var l int64
   547  	for i := range blockSizes {
   548  		if first {
   549  			l = i
   550  			first = false
   551  		} else {
   552  			l = lcm(l, i)
   553  		}
   554  	}
   555  
   556  	guess := time.Duration(l) * time.Millisecond
   557  	// if there's only a single value, we are not guessing
   558  	if len(blockSizes) == 1 {
   559  		return guess, false
   560  	}
   561  
   562  	// otherwise, we are guessing
   563  	return guess, true
   564  }
   565  
   566  func (ts *testSetup) ShouldBeEqual() bool {
   567  	return ts.assertEqual == nil
   568  }
   569  
   570  func (ts *testSetup) AssertEqual(t *testing.T, a, b []generate.TestValue) bool {
   571  	return ts.assertEqual(t, a, b)
   572  }
   573  
   574  func (ts *testSetup) DB() cluster.Database {
   575  	return ts.db
   576  }
   577  
   578  func (ts *testSetup) Scope() tally.TestScope {
   579  	return ts.scope
   580  }
   581  
   582  func (ts *testSetup) M3DBClient() client.Client {
   583  	return ts.m3dbClient
   584  }
   585  
   586  func (ts *testSetup) M3DBVerificationAdminClient() client.AdminClient {
   587  	return ts.m3dbVerificationAdminClient
   588  }
   589  
   590  func (ts *testSetup) Namespaces() []namespace.Metadata {
   591  	return ts.namespaces
   592  }
   593  
   594  func (ts *testSetup) NowFn() xNowFn {
   595  	return ts.getNowFn
   596  }
   597  
   598  func (ts *testSetup) ClockNowFn() clock.NowFn {
   599  	return ts.clockNowFn
   600  }
   601  
   602  func (ts *testSetup) SetNowFn(t xtime.UnixNano) {
   603  	ts.setNowFn(t)
   604  }
   605  
   606  func (ts *testSetup) FilesystemOpts() fs.Options {
   607  	return ts.fsOpts
   608  }
   609  
   610  func (ts *testSetup) Opts() TestOptions {
   611  	return ts.opts
   612  }
   613  
   614  func (ts *testSetup) SetOpts(opts TestOptions) {
   615  	ts.opts = opts
   616  }
   617  
   618  func (ts *testSetup) Origin() topology.Host {
   619  	return ts.origin
   620  }
   621  
   622  func (ts *testSetup) FilePathPrefix() string {
   623  	return ts.filePathPrefix
   624  }
   625  
   626  func (ts *testSetup) StorageOpts() storage.Options {
   627  	return ts.storageOpts
   628  }
   629  
   630  func (ts *testSetup) SetStorageOpts(opts storage.Options) {
   631  	ts.storageOpts = opts
   632  }
   633  
   634  func (ts *testSetup) SetServerStorageOpts(opts server.StorageOptions) {
   635  	ts.serverStorageOpts = opts
   636  }
   637  
   638  func (ts *testSetup) TopologyInitializer() topology.Initializer {
   639  	return ts.topoInit
   640  }
   641  
   642  func (ts *testSetup) SetTopologyInitializer(init topology.Initializer) {
   643  	ts.topoInit = init
   644  }
   645  
   646  func (ts *testSetup) BlockLeaseManager() block.LeaseManager {
   647  	return ts.blockLeaseManager
   648  }
   649  
   650  func (ts *testSetup) ShardSet() sharding.ShardSet {
   651  	return ts.shardSet
   652  }
   653  
   654  func (ts *testSetup) SetShardSet(shardSet sharding.ShardSet) {
   655  	ts.shardSet = shardSet
   656  }
   657  
   658  func (ts *testSetup) NamespaceMetadataOrFail(id ident.ID) namespace.Metadata {
   659  	for _, md := range ts.namespaces {
   660  		if md.ID().Equal(id) {
   661  			return md
   662  		}
   663  	}
   664  	require.FailNow(ts.t, "unable to find namespace", id.String())
   665  	return nil
   666  }
   667  
   668  func (ts *testSetup) GeneratorOptions(ropts retention.Options) generate.Options {
   669  	var (
   670  		storageOpts = ts.storageOpts
   671  		fsOpts      = storageOpts.CommitLogOptions().FilesystemOptions()
   672  		opts        = generate.NewOptions()
   673  		co          = opts.ClockOptions().SetNowFn(ts.clockNowFn)
   674  	)
   675  
   676  	return opts.
   677  		SetClockOptions(co).
   678  		SetRetentionPeriod(ropts.RetentionPeriod()).
   679  		SetBlockSize(ropts.BlockSize()).
   680  		SetFilePathPrefix(fsOpts.FilePathPrefix()).
   681  		SetNewFileMode(fsOpts.NewFileMode()).
   682  		SetNewDirectoryMode(fsOpts.NewDirectoryMode()).
   683  		SetWriterBufferSize(fsOpts.WriterBufferSize()).
   684  		SetEncoderPool(storageOpts.EncoderPool())
   685  }
   686  
   687  func (ts *testSetup) ServerIsBootstrapped() bool {
   688  	resp, err := ts.health()
   689  	return err == nil && resp.Bootstrapped
   690  }
   691  
   692  func (ts *testSetup) ServerIsUp() bool {
   693  	_, err := ts.health()
   694  	return err == nil
   695  }
   696  
   697  func (ts *testSetup) ServerIsDown() bool {
   698  	return !ts.ServerIsUp()
   699  }
   700  
   701  func (ts *testSetup) WaitUntilServerIsBootstrapped() error {
   702  	if waitUntil(ts.ServerIsBootstrapped, ts.opts.ServerStateChangeTimeout()) {
   703  		return nil
   704  	}
   705  	return errServerStartTimedOut
   706  }
   707  
   708  func (ts *testSetup) WaitUntilServerIsUp() error {
   709  	if waitUntil(ts.ServerIsUp, ts.opts.ServerStateChangeTimeout()) {
   710  		return nil
   711  	}
   712  	return errServerStopTimedOut
   713  }
   714  
   715  func (ts *testSetup) WaitUntilServerIsDown() error {
   716  	if waitUntil(ts.ServerIsDown, ts.opts.ServerStateChangeTimeout()) {
   717  		return nil
   718  	}
   719  	return errServerStopTimedOut
   720  }
   721  
   722  func (ts *testSetup) StartServerDontWaitBootstrap() error {
   723  	return ts.startServerBase(false)
   724  }
   725  
   726  func (ts *testSetup) StartServer() error {
   727  	return ts.startServerBase(true)
   728  }
   729  
   730  func (ts *testSetup) startServerBase(waitForBootstrap bool) error {
   731  	ts.logger.Info("starting server")
   732  
   733  	var (
   734  		resultCh = make(chan error, 1)
   735  		err      error
   736  	)
   737  
   738  	topo, err := ts.topoInit.Init()
   739  	if err != nil {
   740  		return fmt.Errorf("error initializing topology: %v", err)
   741  	}
   742  
   743  	topoWatch, err := topo.Watch()
   744  	if err != nil {
   745  		return fmt.Errorf("error watching topology: %v", err)
   746  	}
   747  
   748  	ts.db, err = cluster.NewDatabase(ts.hostID, topo, topoWatch, ts.storageOpts)
   749  	if err != nil {
   750  		return err
   751  	}
   752  
   753  	leaseVerifier := storage.NewLeaseVerifier(ts.db)
   754  	if err := ts.blockLeaseManager.SetLeaseVerifier(leaseVerifier); err != nil {
   755  		return err
   756  	}
   757  
   758  	// Check if clients were closed by StopServer and need to be re-created.
   759  	ts.MaybeResetClients()
   760  
   761  	go func() {
   762  		if err := openAndServe(
   763  			ts.httpClusterAddr(), ts.tchannelClusterAddr(),
   764  			ts.httpNodeAddr(), ts.tchannelNodeAddr(), ts.httpDebugAddr(),
   765  			ts.db, ts.m3dbClient, ts.storageOpts, ts.serverStorageOpts, ts.doneCh,
   766  		); err != nil {
   767  			select {
   768  			case resultCh <- err:
   769  			default:
   770  			}
   771  		}
   772  
   773  		ts.closedCh <- struct{}{}
   774  	}()
   775  
   776  	waitFn := ts.WaitUntilServerIsUp
   777  	if waitForBootstrap {
   778  		waitFn = ts.WaitUntilServerIsBootstrapped
   779  	}
   780  	go func() {
   781  		select {
   782  		case resultCh <- waitFn():
   783  		default:
   784  		}
   785  	}()
   786  
   787  	err = <-resultCh
   788  	if err == nil {
   789  		ts.logger.Info("started server")
   790  	} else {
   791  		ts.logger.Error("start server error", zap.Error(err))
   792  	}
   793  	return err
   794  }
   795  
   796  func (ts *testSetup) StopServer() error {
   797  	ts.doneCh <- struct{}{}
   798  
   799  	// NB(bodu): Need to reset the global counter of index claims managers after
   800  	// we've stopped the test server. This covers the restart server case.
   801  	fs.ResetIndexClaimsManagersUnsafe()
   802  
   803  	if ts.m3dbClient.DefaultSessionActive() {
   804  		session, err := ts.m3dbClient.DefaultSession()
   805  		if err != nil {
   806  			return err
   807  		}
   808  		ts.m3dbClient = nil
   809  		ts.m3dbAdminClient = nil
   810  		ts.m3dbVerificationAdminClient = nil
   811  		defer session.Close()
   812  	}
   813  
   814  	if err := ts.WaitUntilServerIsDown(); err != nil {
   815  		return err
   816  	}
   817  
   818  	// Wait for graceful server close
   819  	<-ts.closedCh
   820  	return nil
   821  }
   822  
   823  func (ts *testSetup) StopServerAndVerifyOpenFilesAreClosed() error {
   824  	if err := ts.DB().Close(); err != nil {
   825  		return err
   826  	}
   827  
   828  	openDataFiles := openFiles(ts.filePathPrefix + "/data/")
   829  	require.Empty(ts.t, openDataFiles)
   830  
   831  	return ts.StopServer()
   832  }
   833  
   834  // counts open/locked files inside parent dir.
   835  func openFiles(parentDir string) []string {
   836  	cmd := exec.Command("lsof", "+D", parentDir) // nolint:gosec
   837  
   838  	out, _ := cmd.Output()
   839  	if len(out) == 0 {
   840  		return nil
   841  	}
   842  
   843  	return strings.Split(string(out), "\n")
   844  }
   845  
   846  func (ts *testSetup) TChannelClient() *TestTChannelClient {
   847  	return ts.tchannelClient
   848  }
   849  
   850  func (ts *testSetup) WriteBatch(namespace ident.ID, seriesList generate.SeriesBlock) error {
   851  	if ts.opts.UseTChannelClientForWriting() {
   852  		return ts.tchannelClient.TChannelClientWriteBatch(
   853  			ts.opts.WriteRequestTimeout(), namespace, seriesList)
   854  	}
   855  	return m3dbClientWriteBatch(ts.m3dbClient, ts.workerPool, namespace, seriesList)
   856  }
   857  
   858  func (ts *testSetup) Fetch(req *rpc.FetchRequest) ([]generate.TestValue, error) {
   859  	if ts.opts.UseTChannelClientForReading() {
   860  		fetched, err := ts.tchannelClient.TChannelClientFetch(ts.opts.ReadRequestTimeout(), req)
   861  		if err != nil {
   862  			return nil, err
   863  		}
   864  		dp := toDatapoints(fetched)
   865  		return dp, nil
   866  	}
   867  	return m3dbClientFetch(ts.m3dbClient, req)
   868  }
   869  
   870  func (ts *testSetup) Truncate(req *rpc.TruncateRequest) (int64, error) {
   871  	if ts.opts.UseTChannelClientForTruncation() {
   872  		return ts.tchannelClient.TChannelClientTruncate(ts.opts.TruncateRequestTimeout(), req)
   873  	}
   874  	return m3dbClientTruncate(ts.m3dbClient, req)
   875  }
   876  
   877  func (ts *testSetup) health() (*rpc.NodeHealthResult_, error) {
   878  	return ts.tchannelClient.TChannelClientHealth(5 * time.Second)
   879  }
   880  
   881  func (ts *testSetup) Close() {
   882  	if ts.channel != nil {
   883  		ts.channel.Close()
   884  	}
   885  	if ts.filePathPrefix != "" {
   886  		os.RemoveAll(ts.filePathPrefix)
   887  	}
   888  
   889  	// This could get called more than once in the multi node integration test case
   890  	// but this is fine since the reset always sets the counter to 0.
   891  	fs.ResetIndexClaimsManagersUnsafe()
   892  }
   893  
   894  func (ts *testSetup) MustSetTickMinimumInterval(tickMinInterval time.Duration) {
   895  	runtimeMgr := ts.storageOpts.RuntimeOptionsManager()
   896  	existingOptions := runtimeMgr.Get()
   897  	newOptions := existingOptions.SetTickMinimumInterval(tickMinInterval)
   898  	err := runtimeMgr.Update(newOptions)
   899  	if err != nil {
   900  		panic(fmt.Sprintf("err setting tick minimum interval: %v", err))
   901  	}
   902  }
   903  
   904  // convenience wrapper used to ensure a tick occurs
   905  func (ts *testSetup) SleepFor10xTickMinimumInterval() {
   906  	// Check the runtime options manager instead of relying on ts.opts
   907  	// because the tick interval can change at runtime.
   908  	runtimeMgr := ts.storageOpts.RuntimeOptionsManager()
   909  	opts := runtimeMgr.Get()
   910  	time.Sleep(opts.TickMinimumInterval() * 10)
   911  }
   912  
   913  func (ts *testSetup) httpClusterAddr() string {
   914  	if addr := ts.opts.HTTPClusterAddr(); addr != "" {
   915  		return addr
   916  	}
   917  	return *httpClusterAddr
   918  }
   919  
   920  func (ts *testSetup) httpNodeAddr() string {
   921  	if addr := ts.opts.HTTPNodeAddr(); addr != "" {
   922  		return addr
   923  	}
   924  	return *httpNodeAddr
   925  }
   926  
   927  func (ts *testSetup) tchannelClusterAddr() string {
   928  	if addr := ts.opts.TChannelClusterAddr(); addr != "" {
   929  		return addr
   930  	}
   931  	return *tchannelClusterAddr
   932  }
   933  
   934  func (ts *testSetup) tchannelNodeAddr() string {
   935  	if addr := ts.opts.TChannelNodeAddr(); addr != "" {
   936  		return addr
   937  	}
   938  	return *tchannelNodeAddr
   939  }
   940  
   941  func (ts *testSetup) httpDebugAddr() string {
   942  	if addr := ts.opts.HTTPDebugAddr(); addr != "" {
   943  		return addr
   944  	}
   945  	return *httpDebugAddr
   946  }
   947  
   948  func (ts *testSetup) MaybeResetClients() error {
   949  	if ts.m3dbClient == nil {
   950  		// Recreate the clients as their session was destroyed by StopServer()
   951  		adminClient, verificationAdminClient, err := newClients(ts.topoInit,
   952  			ts.opts, ts.schemaReg, ts.hostID, ts.tchannelNodeAddr(),
   953  			ts.instrumentOpts)
   954  		if err != nil {
   955  			return err
   956  		}
   957  		ts.m3dbClient = adminClient.(client.Client)
   958  		ts.m3dbAdminClient = adminClient
   959  		ts.m3dbVerificationAdminClient = verificationAdminClient
   960  	}
   961  
   962  	return nil
   963  }
   964  
   965  func (ts *testSetup) SchemaRegistry() namespace.SchemaRegistry {
   966  	return ts.schemaReg
   967  }
   968  
   969  // InitializeBootstrappersOptions supplies options for bootstrapper initialization.
   970  type InitializeBootstrappersOptions struct {
   971  	CommitLogOptions commitlog.Options
   972  	WithCommitLog    bool
   973  	WithFileSystem   bool
   974  }
   975  
   976  func (o InitializeBootstrappersOptions) validate() error {
   977  	if o.WithCommitLog && o.CommitLogOptions == nil {
   978  		return errors.New("commit log options required when initializing a commit log bootstrapper")
   979  	}
   980  	return nil
   981  }
   982  
   983  func (ts *testSetup) InitializeBootstrappers(opts InitializeBootstrappersOptions) error {
   984  	var err error
   985  	if err := opts.validate(); err != nil {
   986  		return err
   987  	}
   988  
   989  	bs := bootstrapper.NewNoOpAllBootstrapperProvider()
   990  	storageOpts := ts.StorageOpts()
   991  	bsOpts := newDefaulTestResultOptions(storageOpts)
   992  	fsOpts := storageOpts.CommitLogOptions().FilesystemOptions()
   993  	if opts.WithCommitLog {
   994  		bclOpts := bcl.NewOptions().
   995  			SetResultOptions(bsOpts).
   996  			SetCommitLogOptions(opts.CommitLogOptions).
   997  			SetRuntimeOptionsManager(runtime.NewOptionsManager())
   998  		bs, err = bcl.NewCommitLogBootstrapperProvider(
   999  			bclOpts, mustInspectFilesystem(fsOpts), bs)
  1000  		if err != nil {
  1001  			return err
  1002  		}
  1003  	}
  1004  
  1005  	if opts.WithFileSystem {
  1006  		persistMgr, err := fs.NewPersistManager(fsOpts)
  1007  		if err != nil {
  1008  			return err
  1009  		}
  1010  		storageIdxOpts := storageOpts.IndexOptions()
  1011  		compactor, err := newCompactorWithErr(storageIdxOpts)
  1012  		if err != nil {
  1013  			return err
  1014  		}
  1015  		bfsOpts := bfs.NewOptions().
  1016  			SetResultOptions(bsOpts).
  1017  			SetFilesystemOptions(fsOpts).
  1018  			SetIndexOptions(storageIdxOpts).
  1019  			SetPersistManager(persistMgr).
  1020  			SetIndexClaimsManager(storageOpts.IndexClaimsManager()).
  1021  			SetCompactor(compactor).
  1022  			SetInstrumentOptions(storageOpts.InstrumentOptions())
  1023  		bs, err = bfs.NewFileSystemBootstrapperProvider(bfsOpts, bs)
  1024  		if err != nil {
  1025  			return err
  1026  		}
  1027  	}
  1028  
  1029  	processOpts := bootstrap.NewProcessOptions().
  1030  		SetTopologyMapProvider(ts).
  1031  		SetOrigin(ts.Origin())
  1032  	process, err := bootstrap.NewProcessProvider(bs, processOpts, bsOpts, fsOpts)
  1033  	if err != nil {
  1034  		return err
  1035  	}
  1036  	ts.SetStorageOpts(storageOpts.SetBootstrapProcessProvider(process))
  1037  
  1038  	return nil
  1039  }
  1040  
  1041  // Implements topology.MapProvider, and makes sure that the topology
  1042  // map provided always comes from the most recent database in the testSetup
  1043  // since they get\ recreated everytime StartServer/StopServer is called and
  1044  // are not available (nil value) after creation but before the first call
  1045  // to StartServer.
  1046  func (ts *testSetup) TopologyMap() (topology.Map, error) {
  1047  	return ts.db.TopologyMap()
  1048  }
  1049  
  1050  func newOrigin(id string, tchannelNodeAddr string) topology.Host {
  1051  	return topology.NewHost(id, tchannelNodeAddr)
  1052  }
  1053  
  1054  func newClients(
  1055  	topoInit topology.Initializer,
  1056  	opts TestOptions,
  1057  	schemaReg namespace.SchemaRegistry,
  1058  	id, tchannelNodeAddr string,
  1059  	instrumentOpts instrument.Options,
  1060  ) (client.AdminClient, client.AdminClient, error) {
  1061  	var (
  1062  		clientOpts = defaultClientOptions(topoInit).SetClusterConnectTimeout(
  1063  			opts.ClusterConnectionTimeout()).
  1064  			SetFetchRequestTimeout(opts.FetchRequestTimeout()).
  1065  			SetWriteConsistencyLevel(opts.WriteConsistencyLevel()).
  1066  			SetTopologyInitializer(topoInit).
  1067  			SetUseV2BatchAPIs(true).
  1068  			SetInstrumentOptions(instrumentOpts)
  1069  
  1070  		origin             = newOrigin(id, tchannelNodeAddr)
  1071  		verificationOrigin = newOrigin(id+"-verification", tchannelNodeAddr)
  1072  
  1073  		adminOpts = clientOpts.(client.AdminOptions).SetOrigin(origin).SetSchemaRegistry(schemaReg)
  1074  
  1075  		verificationAdminOpts = adminOpts.SetOrigin(verificationOrigin).SetSchemaRegistry(schemaReg)
  1076  	)
  1077  
  1078  	if opts.ProtoEncoding() {
  1079  		adminOpts = adminOpts.SetEncodingProto(prototest.ProtoPools.EncodingOpt).(client.AdminOptions)
  1080  		verificationAdminOpts = verificationAdminOpts.SetEncodingProto(prototest.ProtoPools.EncodingOpt).(client.AdminOptions)
  1081  	}
  1082  
  1083  	for _, opt := range opts.CustomClientAdminOptions() {
  1084  		adminOpts = opt(adminOpts)
  1085  		verificationAdminOpts = opt(verificationAdminOpts)
  1086  	}
  1087  
  1088  	// Set up m3db client
  1089  	adminClient, err := m3dbAdminClient(adminOpts)
  1090  	if err != nil {
  1091  		return nil, nil, err
  1092  	}
  1093  
  1094  	// Set up m3db verification client
  1095  	verificationAdminClient, err := m3dbAdminClient(verificationAdminOpts)
  1096  	if err != nil {
  1097  		return nil, nil, err
  1098  	}
  1099  
  1100  	return adminClient, verificationAdminClient, nil
  1101  }
  1102  
  1103  type testSetups []TestSetup
  1104  
  1105  func (ts testSetups) parallel(fn func(s TestSetup)) {
  1106  	var wg sync.WaitGroup
  1107  	for _, setup := range ts {
  1108  		s := setup
  1109  		wg.Add(1)
  1110  		go func() {
  1111  			fn(s)
  1112  			wg.Done()
  1113  		}()
  1114  	}
  1115  	wg.Wait()
  1116  }
  1117  
  1118  // node generates service instances with reasonable defaults
  1119  func node(t *testing.T, n int, shards shard.Shards) services.ServiceInstance {
  1120  	require.True(t, n < 250) // keep ports sensible
  1121  	return services.NewServiceInstance().
  1122  		SetInstanceID(fmt.Sprintf("testhost%v", n)).
  1123  		SetEndpoint(fmt.Sprintf("127.0.0.1:%v", multiAddrPortStart+multiAddrPortEach*n)).
  1124  		SetShards(shards)
  1125  }
  1126  
  1127  // newNodes creates a set of testSetups with reasonable defaults
  1128  func newNodes(
  1129  	t *testing.T,
  1130  	numShards int,
  1131  	instances []services.ServiceInstance,
  1132  	nspaces []namespace.Metadata,
  1133  	asyncInserts bool,
  1134  ) (testSetups, topology.Initializer, closeFn) {
  1135  	var (
  1136  		log  = zap.L()
  1137  		opts = NewTestOptions(t).
  1138  			SetNamespaces(nspaces).
  1139  			SetTickMinimumInterval(3 * time.Second).
  1140  			SetWriteNewSeriesAsync(asyncInserts).
  1141  			SetNumShards(numShards)
  1142  		// NB(bl): We set replication to 3 to mimic production. This can be made
  1143  		// into a variable if needed.
  1144  		svc = fake.NewM3ClusterService().
  1145  			SetInstances(instances).
  1146  			SetReplication(services.NewServiceReplication().SetReplicas(3)).
  1147  			SetSharding(services.NewServiceSharding().SetNumShards(numShards))
  1148  
  1149  		svcs = fake.NewM3ClusterServices()
  1150  	)
  1151  	svcs.RegisterService("m3db", svc)
  1152  
  1153  	topoOpts := topology.NewDynamicOptions().
  1154  		SetConfigServiceClient(fake.NewM3ClusterClient(svcs, nil))
  1155  	topoInit := topology.NewDynamicInitializer(topoOpts)
  1156  
  1157  	nodeOpt := BootstrappableTestSetupOptions{
  1158  		DisablePeersBootstrapper: true,
  1159  		FinalBootstrapper:        uninitialized.UninitializedTopologyBootstrapperName,
  1160  		TopologyInitializer:      topoInit,
  1161  	}
  1162  
  1163  	nodeOpts := make([]BootstrappableTestSetupOptions, len(instances))
  1164  	for i := range instances {
  1165  		nodeOpts[i] = nodeOpt
  1166  	}
  1167  
  1168  	nodes, closeFn := NewDefaultBootstrappableTestSetups(t, opts, nodeOpts)
  1169  
  1170  	nodeClose := func() { // Clean up running servers at end of test
  1171  		log.Debug("servers closing")
  1172  		nodes.parallel(func(s TestSetup) {
  1173  			if s.ServerIsBootstrapped() {
  1174  				require.NoError(t, s.StopServer())
  1175  			}
  1176  		})
  1177  		closeFn()
  1178  		log.Debug("servers are now down")
  1179  	}
  1180  
  1181  	return nodes, topoInit, nodeClose
  1182  }
  1183  
  1184  func mustInspectFilesystem(fsOpts fs.Options) fs.Inspection {
  1185  	inspection, err := fs.InspectFilesystem(fsOpts)
  1186  	if err != nil {
  1187  		panic(err)
  1188  	}
  1189  
  1190  	return inspection
  1191  }