SERVER-85198 create a MongoTFixture for resmoke and enable launching mongot in a single node repl set
GitOrigin-RevId: c5f03fef09aeb16db7e27d07ddbb55005d6e83c4
This commit is contained in:
parent
253e3a8fb1
commit
bd965e368a
@ -0,0 +1,41 @@
|
||||
config_variables:
|
||||
- &keyFile jstests/with_mongot/keyfile_for_testing
|
||||
- &keyFileData Thiskeyisonlyforrunningthesuitewithauthenticationdontuseitinanytestsdirectly
|
||||
- &authOptions
|
||||
authenticationDatabase: local
|
||||
authenticationMechanism: SCRAM-SHA-256
|
||||
password: *keyFileData
|
||||
username: __system
|
||||
|
||||
test_kind: js_test
|
||||
|
||||
description: |
|
||||
This suite spins up a single node replica set (eg a single, primary node) with a mongot for
|
||||
running search integrations tests locally and on evergreen. Given that it is a single repl
|
||||
node, the suite's read preference is the primary.
|
||||
|
||||
selector:
|
||||
roots:
|
||||
- jstests/with_mongot/e2e/*.js
|
||||
|
||||
executor:
|
||||
config:
|
||||
shell_options:
|
||||
global_vars:
|
||||
TestData: &TestData
|
||||
auth: true
|
||||
authMechanism: SCRAM-SHA-256
|
||||
keyFile: *keyFile
|
||||
keyFileData: *keyFileData
|
||||
roleGraphInvalidationIsFatal: true
|
||||
eval: jsTest.authenticate(db.getMongo())
|
||||
<<: *authOptions
|
||||
fixture:
|
||||
class: ReplicaSetFixture
|
||||
auth_options: *authOptions
|
||||
launch_mongot: true
|
||||
num_nodes: 1
|
||||
mongod_options:
|
||||
keyFile: *keyFile
|
||||
set_parameters:
|
||||
enableTestCommands: 1
|
||||
@ -29,6 +29,9 @@ DEFAULT_DBTEST_EXECUTABLE = os.path.join(os.curdir, "dbtest")
|
||||
DEFAULT_MONGO_EXECUTABLE = "mongo"
|
||||
DEFAULT_MONGOD_EXECUTABLE = "mongod"
|
||||
DEFAULT_MONGOS_EXECUTABLE = "mongos"
|
||||
# TODO SERVER-85977 potentially replace with "mongot" if possible to add a symlink from raw path
|
||||
# below as part of setup-mongot-repro.
|
||||
DEFAULT_MONGOT_EXECUTABLE = "mongot-localdev/mongot"
|
||||
|
||||
DEFAULT_BENCHMARK_REPETITIONS = 3
|
||||
DEFAULT_BENCHMARK_MIN_TIME = datetime.timedelta(seconds=5)
|
||||
@ -81,6 +84,8 @@ DEFAULTS = {
|
||||
"mongod_set_parameters": [],
|
||||
"mongos_executable": None,
|
||||
"mongos_set_parameters": [],
|
||||
"mongot-localdev/mongot_executable": None,
|
||||
"mongot_set_parameters": [],
|
||||
"mongocryptd_set_parameters": [],
|
||||
"mrlog": None,
|
||||
"no_journal": False,
|
||||
@ -460,6 +465,12 @@ MONGOS_EXECUTABLE = None
|
||||
# The --setParameter options passed to mongos.
|
||||
MONGOS_SET_PARAMETERS = []
|
||||
|
||||
# The path to the mongot executable used by resmoke.py.
|
||||
MONGOT_EXECUTABLE = None
|
||||
|
||||
# The --setParameter options passed to mongot.
|
||||
MONGOT_SET_PARAMETERS = []
|
||||
|
||||
# The --setParameter options passed to mongocryptd.
|
||||
MONGOCRYPTD_SET_PARAMETERS = []
|
||||
|
||||
|
||||
@ -365,7 +365,7 @@ or explicitly pass --installDir to the run subcommand of buildscripts/resmoke.py
|
||||
# Windows PATH variable requires absolute paths.
|
||||
_config.INSTALL_DIR = os.path.abspath(_expand_user(os.path.normpath(_config.INSTALL_DIR)))
|
||||
|
||||
for binary in ["mongo", "mongod", "mongos", "dbtest"]:
|
||||
for binary in ["mongo", "mongod", "mongos", "mongot-localdev/mongot", "dbtest"]:
|
||||
keyname = binary + "_executable"
|
||||
if config.get(keyname, None) is None:
|
||||
config[keyname] = os.path.join(_config.INSTALL_DIR, binary)
|
||||
@ -414,6 +414,10 @@ or explicitly pass --installDir to the run subcommand of buildscripts/resmoke.py
|
||||
|
||||
_config.MONGOCRYPTD_SET_PARAMETERS = _merge_set_params(config.pop("mongocryptd_set_parameters"))
|
||||
|
||||
_config.MONGOT_EXECUTABLE = _expand_user(config.pop("mongot-localdev/mongot_executable"))
|
||||
mongot_set_parameters = config.pop("mongot_set_parameters")
|
||||
_config.MONGOT_SET_PARAMETERS = _merge_set_params(mongot_set_parameters)
|
||||
|
||||
_config.MRLOG = config.pop("mrlog")
|
||||
_config.NO_JOURNAL = config.pop("no_journal")
|
||||
_config.NUM_CLIENTS_PER_FIXTURE = config.pop("num_clients_per_fixture")
|
||||
|
||||
@ -208,6 +208,17 @@ def mongos_program(logger, job_num, executable=None, process_kwargs=None, mongos
|
||||
return make_process(logger, args, **process_kwargs), final_mongos_options
|
||||
|
||||
|
||||
def mongot_program(logger, job_num, executable=None, process_kwargs=None, mongot_options=None):
|
||||
"""Return a Process instance that starts a mongot."""
|
||||
args = [executable]
|
||||
mongot_options = mongot_options.copy()
|
||||
final_mongot_options = mongot_options.copy()
|
||||
# Apply the rest of the command line arguments.
|
||||
_apply_kwargs(args, mongot_options)
|
||||
process_kwargs = make_historic(utils.default_if_none(process_kwargs, {}))
|
||||
return make_process(logger, args, **process_kwargs), final_mongot_options
|
||||
|
||||
|
||||
def mongo_shell_program(logger, executable=None, connection_string=None, filename=None,
|
||||
test_filename=None, process_kwargs=None, **kwargs):
|
||||
"""Return a Process instance that starts a mongo shell.
|
||||
|
||||
@ -177,6 +177,8 @@ class ReplSetBuilder(FixtureBuilder):
|
||||
:param existing_nodes: the list of mongod fixtures
|
||||
:return: configured replica set fixture
|
||||
"""
|
||||
|
||||
launch_mongot = bool("launch_mongot" in kwargs)
|
||||
self._mutate_kwargs(kwargs)
|
||||
mixed_bin_versions, old_bin_version = _extract_multiversion_options(kwargs)
|
||||
self._validate_multiversion_options(kwargs, mixed_bin_versions)
|
||||
@ -199,15 +201,16 @@ class ReplSetBuilder(FixtureBuilder):
|
||||
|
||||
for node_index in range(replset.num_nodes):
|
||||
node = self._new_mongod(replset, node_index, mongod_executables, mongod_class,
|
||||
mongod_binary_versions[node_index], is_multiversion)
|
||||
mongod_binary_versions[node_index], is_multiversion,
|
||||
launch_mongot)
|
||||
replset.install_mongod(node)
|
||||
|
||||
if replset.start_initial_sync_node:
|
||||
if not replset.initial_sync_node:
|
||||
replset.initial_sync_node_idx = replset.num_nodes
|
||||
replset.initial_sync_node = self._new_mongod(replset, replset.initial_sync_node_idx,
|
||||
mongod_executables, mongod_class,
|
||||
BinVersionEnum.NEW, is_multiversion)
|
||||
replset.initial_sync_node = self._new_mongod(
|
||||
replset, replset.initial_sync_node_idx, mongod_executables, mongod_class,
|
||||
BinVersionEnum.NEW, is_multiversion, launch_mongot)
|
||||
|
||||
return replset
|
||||
|
||||
@ -302,7 +305,7 @@ class ReplSetBuilder(FixtureBuilder):
|
||||
@staticmethod
|
||||
def _new_mongod(replset: ReplicaSetFixture, replset_node_index: int,
|
||||
executables: Dict[str, str], _class: str, cur_version: str,
|
||||
is_multiversion: bool) -> FixtureContainer:
|
||||
is_multiversion: bool, launch_mongot: bool) -> FixtureContainer:
|
||||
"""Make a fixture container with configured mongod fixture(s) in it.
|
||||
|
||||
In non-multiversion mode only a new mongod fixture will be in the fixture container.
|
||||
@ -334,11 +337,11 @@ class ReplSetBuilder(FixtureBuilder):
|
||||
new_fixture_port = old_fixture.port
|
||||
|
||||
new_fixture_mongod_options = replset.get_options_for_mongod(replset_node_index)
|
||||
|
||||
new_fixture = make_fixture(_class, mongod_logger, replset.job_num,
|
||||
mongod_executable=executables[BinVersionEnum.NEW],
|
||||
mongod_options=new_fixture_mongod_options,
|
||||
preserve_dbpath=replset.preserve_dbpath, port=new_fixture_port)
|
||||
preserve_dbpath=replset.preserve_dbpath, port=new_fixture_port,
|
||||
launch_mongot=launch_mongot)
|
||||
|
||||
return FixtureContainer(new_fixture, old_fixture, cur_version)
|
||||
|
||||
|
||||
@ -63,6 +63,12 @@ class FixtureLib:
|
||||
return core.programs.mongos_program(logger, job_num, executable, process_kwargs,
|
||||
mongos_options)
|
||||
|
||||
def mongot_program(self, logger, job_num, executable=None, process_kwargs=None,
|
||||
mongot_options=None):
|
||||
"""Return a Process instance that starts a mongot with arguments constructed from 'kwargs'."""
|
||||
return core.programs.mongot_program(logger, job_num, executable, process_kwargs,
|
||||
mongot_options)
|
||||
|
||||
def generic_program(self, logger, args, process_kwargs=None, **kwargs):
|
||||
"""Return a Process instance that starts an arbitrary executable.
|
||||
|
||||
@ -153,6 +159,8 @@ class _FixtureConfig(object):
|
||||
self.DEFAULT_MONGOS_EXECUTABLE = config.DEFAULT_MONGOS_EXECUTABLE
|
||||
self.MONGOS_EXECUTABLE = config.MONGOS_EXECUTABLE
|
||||
self.MONGOS_SET_PARAMETERS = config.MONGOS_SET_PARAMETERS
|
||||
self.DEFAULT_MONGOT_EXECUTABLE = config.DEFAULT_MONGOT_EXECUTABLE
|
||||
self.MONGOT_EXECUTABLE = config.MONGOT_EXECUTABLE
|
||||
self.DBPATH_PREFIX = config.DBPATH_PREFIX
|
||||
self.DEFAULT_DBPATH_PREFIX = config.DEFAULT_DBPATH_PREFIX
|
||||
self.DOCKER_COMPOSE_BUILD_IMAGES = config.DOCKER_COMPOSE_BUILD_IMAGES
|
||||
|
||||
@ -243,27 +243,27 @@ class DockerComposeException(Exception):
|
||||
|
||||
class _DockerComposeInterface:
|
||||
"""
|
||||
Implement the `_all_mongo_d_s_instances` method which returns all `mongo{d,s}` instances.
|
||||
Implement the `_all_mongo_d_s_t_instances` method which returns all `mongo{d,s,t}` instances.
|
||||
|
||||
Fixtures that use this interface can programmatically generate `docker-compose.yml` configurations
|
||||
by leveraging the `all_processes` method to access the startup args.
|
||||
"""
|
||||
|
||||
def _all_mongo_d_s(self) -> List[Fixture]:
|
||||
def _all_mongo_d_s_t(self) -> List[Fixture]:
|
||||
"""
|
||||
Return a list of all mongo{d,s} `Fixture` instances in this fixture.
|
||||
Return a list of all mongo{d,s,t} `Fixture` instances in this fixture.
|
||||
|
||||
:return: A list of `mongo{d,s}` `Fixture` instances.
|
||||
:return: A list of `mongo{d,s,t}` `Fixture` instances.
|
||||
"""
|
||||
raise NotImplementedError(
|
||||
"_all_mongo_d_s_instances must be implemented by Fixture subclasses that support `docker-compose.yml` generation."
|
||||
"_all_mongo_d_s_t_instances must be implemented by Fixture subclasses that support `docker-compose.yml` generation."
|
||||
)
|
||||
|
||||
def all_processes(self) -> List['Process']:
|
||||
"""
|
||||
Return a list of all `mongo{d,s}` `Process` instances in the fixture.
|
||||
Return a list of all `mongo{d,s,t}` `Process` instances in the fixture.
|
||||
|
||||
:return: A list of mongo{d,s} processes for the current fixture.
|
||||
:return: A list of mongo{d,s,t} processes for the current fixture.
|
||||
"""
|
||||
if not self.config.DOCKER_COMPOSE_BUILD_IMAGES:
|
||||
raise DockerComposeException(
|
||||
@ -273,7 +273,7 @@ class _DockerComposeInterface:
|
||||
|
||||
# If `mongo_d_s.NOOP_MONGO_D_S_PROCESSES=True`, `mongo_d_s.setup()` will setup a dummy process
|
||||
# to extract args from instead of a real `mongo{d,s}`.
|
||||
for mongo_d_s in self._all_mongo_d_s():
|
||||
for mongo_d_s in self._all_mongo_d_s_t():
|
||||
if mongo_d_s.__class__.__name__ == "MongoDFixture":
|
||||
mongo_d_s.setup()
|
||||
processes += [mongo_d_s.mongod]
|
||||
|
||||
197
buildscripts/resmokelib/testing/fixtures/mongot.py
Normal file
197
buildscripts/resmokelib/testing/fixtures/mongot.py
Normal file
@ -0,0 +1,197 @@
|
||||
"""Mongot fixture for executing JSTests against.
|
||||
|
||||
Mongot is a MongoDB-specific process written as a wrapper around Lucene. Using Lucene, mongot indexes MongoDB databases to provide our customers with full text search capabilities.
|
||||
|
||||
Customers have the option of running mongot on Atlas or locally using a special "local-dev" binary of mongot. The local-dev binary allows mongot and mongod to speak directly on the localhost, rather than via proprietary network proxies configured by the Atlas Data Plane.
|
||||
|
||||
A resmoke suite's yml definition can enable launching mongot(s) enabled via the launch_mongot option on the ReplicaSetFixture and providing a keyfile. If enabled, the ReplicaSetFixture launches a local-dev version of mongot per mongod node. The mongot replicates directly from the co-located
|
||||
mongod via a $changeStream.
|
||||
"""
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import time
|
||||
import shutil
|
||||
import uuid
|
||||
|
||||
import yaml
|
||||
|
||||
import pymongo
|
||||
import pymongo.errors
|
||||
|
||||
from buildscripts.resmokelib.testing.fixtures import interface
|
||||
|
||||
|
||||
class MongoTFixture(interface.Fixture, interface._DockerComposeInterface):
|
||||
"""Fixture which provides JSTests with a mongot to run alongside a mongod."""
|
||||
|
||||
def __init__(self, logger, job_num, fixturelib, dbpath_prefix=None, mongot_options=None):
|
||||
interface.Fixture.__init__(self, logger, job_num, fixturelib)
|
||||
self.mongot_options = self.fixturelib.make_historic(
|
||||
self.fixturelib.default_if_none(mongot_options, {}))
|
||||
# Default to command line options if the YAML configuration is not passed in.
|
||||
self.mongot_executable = self.fixturelib.default_if_none(self.config.MONGOT_EXECUTABLE)
|
||||
self.port = self.mongot_options["port"]
|
||||
self.mongot = None
|
||||
|
||||
def setup(self):
|
||||
"""Set up and launch the mongot."""
|
||||
launcher = MongotLauncher(self.fixturelib)
|
||||
# Second return val is the port, which we ignore because we explicitly generated the port number in MongoDFixture initialization and save to MongotFixture in above initialization function.
|
||||
mongot, _ = launcher.launch_mongot_program(self.logger, self.job_num,
|
||||
executable=self.mongot_executable,
|
||||
mongot_options=self.mongot_options)
|
||||
|
||||
try:
|
||||
msg = f"Starting mongot on port { self.port } ...\n{ mongot.as_command() }"
|
||||
self.logger.info(msg)
|
||||
mongot.start()
|
||||
msg = f"mongot started on port { self.port } with pid { mongot.pid }"
|
||||
self.logger.info(msg)
|
||||
except Exception as err:
|
||||
msg = "Failed to start mongot on port {:d}: {}".format(self.port, err)
|
||||
self.logger.exception(msg)
|
||||
raise self.fixturelib.ServerFailure(msg)
|
||||
|
||||
self.mongot = mongot
|
||||
|
||||
def _all_mongo_d_s_t(self):
|
||||
"""Return the `mongot` `Process` instance."""
|
||||
return [self]
|
||||
|
||||
def pids(self):
|
||||
""":return: pids owned by this fixture if any."""
|
||||
out = [x.pid for x in [self.mongot] if x is not None]
|
||||
if not out:
|
||||
self.logger.debug('Mongot not running when gathering mongot fixture pid.')
|
||||
return out
|
||||
|
||||
def _do_teardown(self, mode=None):
|
||||
|
||||
if self.config.NOOP_MONGO_D_S_PROCESSES:
|
||||
self.logger.info(
|
||||
"This is running against an External System Under Test setup with `docker-compose.yml` -- skipping teardown."
|
||||
)
|
||||
return
|
||||
|
||||
if self.mongot is None:
|
||||
self.logger.warning("The mongot fixture has not been set up yet.")
|
||||
return # Still a success even if nothing is running.
|
||||
|
||||
if mode == interface.TeardownMode.ABORT:
|
||||
self.logger.info(
|
||||
"Attempting to send SIGABRT from resmoke to mongot on port %d with pid %d...",
|
||||
self.port, self.mongot.pid)
|
||||
else:
|
||||
self.logger.info("Stopping mongot on port %d with pid %d...", self.port,
|
||||
self.mongot.pid)
|
||||
if not self.is_running():
|
||||
exit_code = self.mongot.poll()
|
||||
msg = ("mongot on port {:d} was expected to be running, but wasn't. "
|
||||
"Process exited with code {:d}.").format(self.port, exit_code)
|
||||
self.logger.warning(msg)
|
||||
raise self.fixturelib.ServerFailure(msg)
|
||||
|
||||
self.mongot.stop(mode)
|
||||
exit_code = self.mongot.wait()
|
||||
|
||||
# Java applications return exit code of 143 when they shut down upon receiving and obeying a SIGTERM signal, which is the desired/default mode.
|
||||
if exit_code == 143 or (mode is not None and exit_code == -(mode.value)):
|
||||
self.logger.info("Successfully stopped the mongot on port {:d}.".format(self.port))
|
||||
else:
|
||||
self.logger.warning("Stopped the mongot on port {:d}. "
|
||||
"Process exited with code {:d}.".format(self.port, exit_code))
|
||||
raise self.fixturelib.ServerFailure(
|
||||
"mongot on port {:d} with pid {:d} exited with code {:d}".format(
|
||||
self.port, self.mongot.pid, exit_code))
|
||||
|
||||
def is_running(self):
|
||||
"""Return true if the mongot is still operating."""
|
||||
return self.mongot is not None and self.mongot.poll() is None
|
||||
|
||||
def get_dbpath_prefix(self):
|
||||
"""Return the _dbpath, as this is the root of the data directory."""
|
||||
return self._dbpath
|
||||
|
||||
def get_node_info(self):
|
||||
"""Return a list of NodeInfo objects."""
|
||||
if self.mongot is None:
|
||||
self.logger.warning("The mongot fixture has not been set up yet.")
|
||||
return []
|
||||
|
||||
info = interface.NodeInfo(full_name=self.logger.full_name, name=self.logger.name,
|
||||
port=self.port, pid=self.mongot.pid, router_port=self.router_port)
|
||||
return [info]
|
||||
|
||||
def get_internal_connection_string(self):
|
||||
"""Return the internal connection string."""
|
||||
return f"localhost:{self.port}"
|
||||
|
||||
def get_driver_connection_url(self):
|
||||
"""Return the driver connection URL."""
|
||||
return "mongodb://" + self.get_internal_connection_string() + "/?directConnection=true"
|
||||
|
||||
def await_ready(self):
|
||||
"""Block until the fixture can be used for testing."""
|
||||
deadline = time.time() + MongoTFixture.AWAIT_READY_TIMEOUT_SECS
|
||||
|
||||
# Wait until the mongot is accepting connections. The retry logic is necessary to support
|
||||
# versions of PyMongo <3.0 that immediately raise a ConnectionFailure if a connection cannot
|
||||
# be established.
|
||||
while True:
|
||||
# Check whether the mongot exited for some reason.
|
||||
exit_code = self.mongot.poll()
|
||||
if exit_code is not None:
|
||||
raise self.fixturelib.ServerFailure(
|
||||
"Could not connect to mongot on port {}, process ended"
|
||||
" unexpectedly with code {}.".format(self.port, exit_code))
|
||||
|
||||
try:
|
||||
# By connecting to the host and port that mongot is listening from,
|
||||
# we ensure mongot specifically is receiving the ping command.
|
||||
client = pymongo.MongoClient(self.get_driver_connection_url())
|
||||
client.admin.command("hello")
|
||||
break
|
||||
except pymongo.errors.ConnectionFailure:
|
||||
remaining = deadline - time.time()
|
||||
if remaining <= 0.0:
|
||||
raise self.fixturelib.ServerFailure(
|
||||
"Failed to connect to mongot on port {} after {} seconds".format(
|
||||
self.port, MongoTFixture.AWAIT_READY_TIMEOUT_SECS))
|
||||
|
||||
self.logger.info("Waiting to connect to mongot on port %d.", self.port)
|
||||
time.sleep(0.1) # Wait a little bit before trying again.
|
||||
|
||||
self.logger.info("Successfully contacted the mongot on port %d.", self.port)
|
||||
|
||||
|
||||
class MongotLauncher(object):
|
||||
"""Class with utilities for launching a mongot."""
|
||||
|
||||
def __init__(self, fixturelib):
|
||||
"""Initialize MongotLauncher."""
|
||||
self.fixturelib = fixturelib
|
||||
self.config = fixturelib.get_config()
|
||||
|
||||
def launch_mongot_program(self, logger, job_num, executable=None, process_kwargs=None,
|
||||
mongot_options=None):
|
||||
"""
|
||||
Return a Process instance that starts a mongot with arguments constructed from 'mongot_options'.
|
||||
|
||||
@param logger - The logger to pass into the process.
|
||||
@param executable - The mongot executable to run.
|
||||
@param process_kwargs - A dict of key-value pairs to pass to the process.
|
||||
@param mongot_options - A HistoryDict describing the various options to pass to the mongot.
|
||||
|
||||
Currently, this will launch a mongot with --port, --mongodHostAndPort, and --keyFile commandline
|
||||
options. To support launching mongot with more startup options, those new options would need to
|
||||
be added to mongot_options in MongoTFixture initialization or, if mongod needs to share/know the
|
||||
mongot startup option (like in the case of keyFile), in MongoDFixture::setup_mongot().
|
||||
"""
|
||||
|
||||
executable = self.fixturelib.default_if_none(executable,
|
||||
self.config.DEFAULT_MONGOD_EXECUTABLE)
|
||||
mongot_options = self.fixturelib.default_if_none(mongot_options, {}).copy()
|
||||
|
||||
return self.fixturelib.mongot_program(logger, job_num, executable, process_kwargs,
|
||||
mongot_options)
|
||||
@ -49,7 +49,7 @@ class ReplicaSetFixture(interface.ReplFixture, interface._DockerComposeInterface
|
||||
default_read_concern=None, default_write_concern=None, shard_logging_prefix=None,
|
||||
replicaset_logging_prefix=None, replset_name=None, config_shard=None,
|
||||
use_auto_bootstrap_procedure=None, initial_sync_uninitialized_fcv=False,
|
||||
hide_initial_sync_node_from_conn_string=False):
|
||||
hide_initial_sync_node_from_conn_string=False, launch_mongot=False):
|
||||
"""Initialize ReplicaSetFixture."""
|
||||
|
||||
interface.ReplFixture.__init__(self, logger, job_num, fixturelib,
|
||||
@ -79,7 +79,8 @@ class ReplicaSetFixture(interface.ReplFixture, interface._DockerComposeInterface
|
||||
# Used by the enhanced multiversion system to signify multiversion mode.
|
||||
# None implies no multiversion run.
|
||||
self.fcv = None
|
||||
|
||||
# Used by suites that run search integration tests.
|
||||
self.launch_mongot = launch_mongot
|
||||
# Use the values given from the command line if they exist for linear_chain and num_nodes.
|
||||
linear_chain_option = self.fixturelib.default_if_none(self.config.LINEAR_CHAIN,
|
||||
linear_chain)
|
||||
@ -263,9 +264,15 @@ class ReplicaSetFixture(interface.ReplFixture, interface._DockerComposeInterface
|
||||
self._await_secondaries()
|
||||
self._await_newly_added_removals()
|
||||
|
||||
def _all_mongo_d_s(self):
|
||||
"""Return a list of all `mongo{d,s}` `Process` instances in this fixture."""
|
||||
return sum([node._all_mongo_d_s() for node in self.nodes], [])
|
||||
if self.launch_mongot:
|
||||
# To model Atlas Search's coupled architecture, resmoke deploys a mongot for each
|
||||
# mongod node in a replica set.
|
||||
for node in self.nodes:
|
||||
node.setup_mongot()
|
||||
|
||||
def _all_mongo_d_s_t(self):
|
||||
"""Return a list of all `mongo{d,s,t}` `Process` instances in this fixture."""
|
||||
return sum([node._all_mongo_d_s_t() for node in self.nodes], [])
|
||||
|
||||
def pids(self):
|
||||
""":return: all pids owned by this fixture if any."""
|
||||
|
||||
@ -140,17 +140,17 @@ class ShardedClusterFixture(interface.Fixture, interface._DockerComposeInterface
|
||||
for shard in self.shards:
|
||||
shard.setup()
|
||||
|
||||
def _all_mongo_d_s(self):
|
||||
"""Return a list of all `mongo{d,s}` `Process` instances in this fixture."""
|
||||
def _all_mongo_d_s_t(self):
|
||||
"""Return a list of all `mongo{d,s,t}` `Process` instances in this fixture."""
|
||||
# When config_shard is None, we have an additional replset for the configsvr.
|
||||
all_nodes = [self.configsvr] if self.config_shard is None else []
|
||||
all_nodes += self.mongos
|
||||
all_nodes += self.shards
|
||||
return sum([node._all_mongo_d_s() for node in all_nodes], [])
|
||||
return sum([node._all_mongo_d_s_t() for node in all_nodes], [])
|
||||
|
||||
def get_shardsvrs(self):
|
||||
"""Return a list of the `MongodFixture`s for all of the shardsvrs in the cluster."""
|
||||
return sum([shard._all_mongo_d_s() for shard in self.shards], [])
|
||||
return sum([shard._all_mongo_d_s_t() for shard in self.shards], [])
|
||||
|
||||
def refresh_logical_session_cache(self, target):
|
||||
"""Refresh logical session cache with no timeout."""
|
||||
@ -673,7 +673,7 @@ class _MongoSFixture(interface.Fixture, interface._DockerComposeInterface):
|
||||
|
||||
self.mongos = mongos
|
||||
|
||||
def _all_mongo_d_s(self):
|
||||
def _all_mongo_d_s_t(self):
|
||||
"""Return the standalone `mongos` `Process` instance."""
|
||||
return [self]
|
||||
|
||||
|
||||
@ -10,15 +10,18 @@ import yaml
|
||||
|
||||
import pymongo
|
||||
import pymongo.errors
|
||||
|
||||
from buildscripts.resmokelib.testing.fixtures import interface
|
||||
from buildscripts.resmokelib.testing.fixtures.fixturelib import FixtureLib
|
||||
from buildscripts.resmokelib.testing.fixtures.interface import _FIXTURES
|
||||
from buildscripts.resmokelib.testing.fixtures.mongot import MongoTFixture
|
||||
|
||||
|
||||
class MongoDFixture(interface.Fixture, interface._DockerComposeInterface):
|
||||
"""Fixture which provides JSTests with a standalone mongod to run against."""
|
||||
|
||||
def __init__(self, logger, job_num, fixturelib, mongod_executable=None, mongod_options=None,
|
||||
add_feature_flags=False, dbpath_prefix=None, preserve_dbpath=False, port=None):
|
||||
add_feature_flags=False, dbpath_prefix=None, preserve_dbpath=False, port=None,
|
||||
launch_mongot=False):
|
||||
"""Initialize MongoDFixture with different options for the mongod process."""
|
||||
interface.Fixture.__init__(self, logger, job_num, fixturelib, dbpath_prefix=dbpath_prefix)
|
||||
self.mongod_options = self.fixturelib.make_historic(
|
||||
@ -56,6 +59,19 @@ class MongoDFixture(interface.Fixture, interface._DockerComposeInterface):
|
||||
self.port = port or fixturelib.get_next_port(job_num)
|
||||
self.mongod_options["port"] = self.port
|
||||
|
||||
if launch_mongot:
|
||||
self.launch_mongot = True
|
||||
self.mongot_port = fixturelib.get_next_port(job_num)
|
||||
self.mongod_options["mongotHost"] = "localhost:" + str(self.mongot_port)
|
||||
# In future architectures, this could change
|
||||
self.mongod_options["searchIndexManagementHostAndPort"] = self.mongod_options[
|
||||
"mongotHost"]
|
||||
else:
|
||||
self.launch_mongot = False
|
||||
# If a suite enables launching mongot, the MongoTFixture will be created in setup_mongot,
|
||||
# which gets called by ReplicaSetFixture::setup().
|
||||
self.mongot = None
|
||||
|
||||
self.router_port = None
|
||||
if "routerPort" in self.mongod_options:
|
||||
self.router_port = fixturelib.get_next_port(job_num)
|
||||
@ -98,7 +114,7 @@ class MongoDFixture(interface.Fixture, interface._DockerComposeInterface):
|
||||
|
||||
self.mongod = mongod
|
||||
|
||||
def _all_mongo_d_s(self):
|
||||
def _all_mongo_d_s_t(self):
|
||||
"""Return the standalone `mongod` `Process` instance."""
|
||||
return [self]
|
||||
|
||||
@ -119,6 +135,23 @@ class MongoDFixture(interface.Fixture, interface._DockerComposeInterface):
|
||||
self.logger.info("Waiting to connect to mongod on port %d.", self.port)
|
||||
time.sleep(0.1) # Wait a little bit before trying again.
|
||||
|
||||
def setup_mongot(self):
|
||||
mongot_options = {}
|
||||
mongot_options["mongodHostAndPort"] = "localhost:" + str(self.port)
|
||||
mongot_options["port"] = self.mongot_port
|
||||
|
||||
if "keyFile" not in self.mongod_options:
|
||||
raise self.fixturelib.ServerFailure("Cannot launch mongot without providing a keyfile")
|
||||
|
||||
mongot_options["keyFile"] = self.mongod_options["keyFile"]
|
||||
|
||||
mongot = self.fixturelib.make_fixture("MongoTFixture", self.logger, self.job_num,
|
||||
mongot_options=mongot_options)
|
||||
|
||||
mongot.setup()
|
||||
self.mongot = mongot
|
||||
self.mongot.await_ready()
|
||||
|
||||
def await_ready(self):
|
||||
"""Block until the fixture can be used for testing."""
|
||||
deadline = time.time() + MongoDFixture.AWAIT_READY_TIMEOUT_SECS
|
||||
@ -173,6 +206,9 @@ class MongoDFixture(interface.Fixture, interface._DockerComposeInterface):
|
||||
self.logger.warning(msg)
|
||||
raise self.fixturelib.ServerFailure(msg)
|
||||
|
||||
if self.mongot is not None:
|
||||
self.mongot._do_teardown(mode)
|
||||
|
||||
self.mongod.stop(mode)
|
||||
exit_code = self.mongod.wait()
|
||||
|
||||
@ -272,6 +308,10 @@ class MongodLauncher(object):
|
||||
if self.config.MONGOD_SET_PARAMETERS is not None:
|
||||
suite_set_parameters.update(yaml.safe_load(self.config.MONGOD_SET_PARAMETERS))
|
||||
|
||||
if "mongotHost" in mongod_options:
|
||||
suite_set_parameters["mongotHost"] = mongod_options.pop("mongotHost")
|
||||
suite_set_parameters["searchIndexManagementHostAndPort"] = mongod_options.pop(
|
||||
"searchIndexManagementHostAndPort")
|
||||
# Some storage options are both a mongod option (as in config file option and its equivalent
|
||||
# "--xyz" command line parameter) and a "--setParameter". In case of conflict, for instance
|
||||
# due to the config fuzzer adding "xyz" as a "--setParameter" when the "--xyz" option is
|
||||
|
||||
@ -10,6 +10,7 @@ ALLOWED_IMPORTS = [
|
||||
"buildscripts.resmokelib.testing.fixtures.external",
|
||||
"buildscripts.resmokelib.testing.fixtures.interface",
|
||||
"buildscripts.resmokelib.testing.fixtures.fixturelib",
|
||||
"buildscripts.resmokelib.testing.fixtures.mongot",
|
||||
"buildscripts.resmokelib.multiversionconstants",
|
||||
"buildscripts.resmokelib.utils",
|
||||
"buildscripts.resmokelib.utils.registry",
|
||||
|
||||
33
jstests/with_mongot/e2e/foo.js
Normal file
33
jstests/with_mongot/e2e/foo.js
Normal file
@ -0,0 +1,33 @@
|
||||
// This test asserts that search e2e suites were correctly configured to spin up mongot(s) by
|
||||
// checking that the mongotHost server parameter is set. A search index is created and a search
|
||||
// query is ran to assert that no errors are thrown.
|
||||
|
||||
const coll = db.foo;
|
||||
coll.drop()
|
||||
coll.insert({a: -1, size: "small"})
|
||||
coll.insert({a: -10, size: "medium", mood: "hungry"})
|
||||
coll.insert({a: 100, size: "medium", mood: "very hungry"})
|
||||
|
||||
// A sanity check.
|
||||
let result = coll.aggregate([{$match: {size: "medium"}}]).toArray();
|
||||
assert.eq(result.length, 2);
|
||||
// Confirm that mongod was launched with a connection string to mongot on localhost.
|
||||
let paramOne = assert.commandWorked(db.adminCommand({getParameter: 1, "mongotHost": -1}));
|
||||
assert(paramOne["mongotHost"].startsWith("localhost:"));
|
||||
let paramTwo = assert.commandWorked(
|
||||
db.adminCommand({getParameter: 1, "searchIndexManagementHostAndPort": -1}));
|
||||
assert.eq(paramOne["mongotHost"], paramTwo["searchIndexManagementHostAndPort"]);
|
||||
// TODO SERVER-86614 replace this with shell helper that waits for mongot index definitions to be
|
||||
// stable.
|
||||
let searchIndexResult = assert.commandWorked(db.runCommand(
|
||||
{'createSearchIndexes': "foo", 'indexes': [{'definition': {'mappings': {'dynamic': true}}}]}));
|
||||
assert.doesNotThrow(() => coll.aggregate([{"$listSearchIndexes": {}}]))
|
||||
// TODO SERVER-86616 replace this $search query with shell helper and assert that results are
|
||||
// correct.
|
||||
let searchRes = assert.doesNotThrow(() => coll.aggregate([{
|
||||
$search: {
|
||||
exists: {
|
||||
path: "mood",
|
||||
}
|
||||
}
|
||||
}]));
|
||||
1
jstests/with_mongot/keyfile_for_testing
Normal file
1
jstests/with_mongot/keyfile_for_testing
Normal file
@ -0,0 +1 @@
|
||||
Thiskeyisonlyforrunningthesuitewithauthenticationdontuseitinanytestsdirectly
|
||||
Loading…
Reference in New Issue
Block a user