diff options
22 files changed, 425 insertions, 145 deletions
diff --git a/buildscripts/evergreen_gen_fuzzer_tests.py b/buildscripts/evergreen_gen_fuzzer_tests.py index d6872ba7876..4b5f4e8ed85 100755 --- a/buildscripts/evergreen_gen_fuzzer_tests.py +++ b/buildscripts/evergreen_gen_fuzzer_tests.py @@ -1,7 +1,6 @@ #!/usr/bin/env python3 """Generate fuzzer tests to run in evergreen in parallel.""" import argparse -from collections import namedtuple from typing import Set, Optional, List, NamedTuple from shrub.v2 import ShrubProject, FunctionCall, Task, TaskDependency, BuildVariant, ExistingTask diff --git a/buildscripts/evergreen_gen_multiversion_tests.py b/buildscripts/evergreen_gen_multiversion_tests.py index dfcc36ed8d8..85ef4aa3f2c 100755 --- a/buildscripts/evergreen_gen_multiversion_tests.py +++ b/buildscripts/evergreen_gen_multiversion_tests.py @@ -19,7 +19,8 @@ import structlog from shrub.v2 import ShrubProject, FunctionCall, Task, TaskDependency, BuildVariant, ExistingTask from evergreen.api import RetryingEvergreenApi, EvergreenApi -from buildscripts.resmokelib.multiversionconstants import (LAST_LTS_MONGO_BINARY, REQUIRES_FCV_TAG) +from buildscripts.resmokelib.multiversionconstants import ( + LAST_LTS_MONGO_BINARY, LAST_CONTINUOUS_MONGO_BINARY, REQUIRES_FCV_TAG) import buildscripts.util.taskname as taskname from buildscripts.util.fileops import write_file_to_dir, read_yaml_file import buildscripts.evergreen_generate_resmoke_tasks as generate_resmoke @@ -89,10 +90,9 @@ def get_multiversion_resmoke_args(is_sharded: bool) -> str: return "--numReplSetNodes=3 --linearChain=on " -def get_backports_required_last_lts_hash(task_path_suffix: str): +def get_backports_required_hash_for_shell_version(mongo_shell_path=None): """Parse the last-lts shell binary to get the commit hash.""" - last_lts_shell_exec = os.path.join(task_path_suffix, LAST_LTS_MONGO_BINARY) - shell_version = check_output([last_lts_shell_exec, "--version"]).decode('utf-8') + shell_version = check_output([mongo_shell_path, "--version"]).decode('utf-8') for line in shell_version.splitlines(): if "gitVersion" in line: version_line = line.split(':')[1] @@ -101,7 +101,7 @@ def get_backports_required_last_lts_hash(task_path_suffix: str): if result: commit_hash = result.group().strip('"') if not commit_hash.isalnum(): - raise ValueError(f"Error parsing last-lts commit hash. Expected an " + raise ValueError(f"Error parsing commit hash. Expected an " f"alpha-numeric string but got: {commit_hash}") return commit_hash else: @@ -109,15 +109,15 @@ def get_backports_required_last_lts_hash(task_path_suffix: str): raise ValueError("Could not find a valid commit hash from the last-lts mongo binary.") -def get_last_lts_yaml(last_lts_commit_hash): +def get_last_lts_yaml(commit_hash): """Download BACKPORTS_REQUIRED_FILE from the last LTS commit and return the yaml.""" - LOGGER.info(f"Downloading file from commit hash of last-lts branch {last_lts_commit_hash}") + LOGGER.info(f"Downloading file from commit hash of last-lts branch {commit_hash}") response = requests.get( - f'{BACKPORTS_REQUIRED_BASE_URL}/{last_lts_commit_hash}/{ETC_DIR}/{BACKPORTS_REQUIRED_FILE}') + f'{BACKPORTS_REQUIRED_BASE_URL}/{commit_hash}/{ETC_DIR}/{BACKPORTS_REQUIRED_FILE}') # If the response was successful, no exception will be raised. response.raise_for_status() - last_lts_file = f"{last_lts_commit_hash}_{BACKPORTS_REQUIRED_FILE}" + last_lts_file = f"{commit_hash}_{BACKPORTS_REQUIRED_FILE}" temp_dir = tempfile.mkdtemp() with open(os.path.join(temp_dir, last_lts_file), "w") as fileh: fileh.write(response.text) @@ -375,11 +375,9 @@ def run_generate_tasks(expansion_file: str, evergreen_config: Optional[str] = No @main.command("generate-exclude-tags") -@click.option("--task-path-suffix", type=str, required=True, - help="The directory in which multiversion binaries are stored.") @click.option("--output", type=str, default=os.path.join(CONFIG_DIR, EXCLUDE_TAGS_FILE), show_default=True, help="Where to output the generated tags.") -def generate_exclude_yaml(task_path_suffix: str, output: str) -> None: +def generate_exclude_yaml(output: str) -> None: # pylint: disable=too-many-locals """ Create a tag file associating multiversion tests to tags for exclusion. @@ -400,7 +398,8 @@ def generate_exclude_yaml(task_path_suffix: str, output: str) -> None: # Get the state of the backports_required_for_multiversion_tests.yml file for the last-lts # binary we are running tests against. We do this by using the commit hash from the last-lts # mongo shell executable. - last_lts_commit_hash = get_backports_required_last_lts_hash(task_path_suffix) + last_lts_commit_hash = get_backports_required_hash_for_shell_version( + mongo_shell_path=LAST_LTS_MONGO_BINARY) # Get the yaml contents from the last-lts commit. backports_required_last_lts = get_last_lts_yaml(last_lts_commit_hash) diff --git a/buildscripts/resmokelib/multiversionconstants.py b/buildscripts/resmokelib/multiversionconstants.py index 5442059b301..e623d4159f6 100644 --- a/buildscripts/resmokelib/multiversionconstants.py +++ b/buildscripts/resmokelib/multiversionconstants.py @@ -1,10 +1,17 @@ """FCV and Server binary version constants used for multiversion testing.""" - LAST_LTS_BIN_VERSION = "4.4" +LAST_CONTINUOUS_BIN_VERSION = "5.0" + LAST_LTS_FCV = "4.4" +LAST_CONTINUOUS_FCV = "5.0" LATEST_FCV = "5.0" +LAST_CONTINUOUS_MONGO_BINARY = "mongo-" + LAST_CONTINUOUS_BIN_VERSION +LAST_CONTINUOUS_MONGOD_BINARY = "mongod-" + LAST_CONTINUOUS_BIN_VERSION +LAST_CONTINUOUS_MONGOS_BINARY = "mongos-" + LAST_CONTINUOUS_BIN_VERSION + LAST_LTS_MONGO_BINARY = "mongo-" + LAST_LTS_BIN_VERSION LAST_LTS_MONGOD_BINARY = "mongod-" + LAST_LTS_BIN_VERSION LAST_LTS_MONGOS_BINARY = "mongos-" + LAST_LTS_BIN_VERSION + REQUIRES_FCV_TAG = "requires_fcv_47,requires_fcv_48,requires_fcv_49,requires_fcv_50" diff --git a/buildscripts/resmokelib/testing/fixtures/__init__.py b/buildscripts/resmokelib/testing/fixtures/__init__.py index ea85dadbc7c..49293d4fe4a 100644 --- a/buildscripts/resmokelib/testing/fixtures/__init__.py +++ b/buildscripts/resmokelib/testing/fixtures/__init__.py @@ -2,7 +2,7 @@ from buildscripts.resmokelib.testing.fixtures.external import ExternalFixture as _ExternalFixture from buildscripts.resmokelib.testing.fixtures.interface import NoOpFixture as _NoOpFixture -from buildscripts.resmokelib.testing.fixtures.interface import make_fixture +from buildscripts.resmokelib.testing.fixtures._builder import make_fixture from buildscripts.resmokelib.utils import autoloader as _autoloader EXTERNAL_FIXTURE_CLASS = _ExternalFixture.REGISTERED_NAME diff --git a/buildscripts/resmokelib/testing/fixtures/_builder.py b/buildscripts/resmokelib/testing/fixtures/_builder.py new file mode 100644 index 00000000000..685791a84ca --- /dev/null +++ b/buildscripts/resmokelib/testing/fixtures/_builder.py @@ -0,0 +1,190 @@ +"""Utilities for constructing fixtures that may span multiple versions.""" +import io +import os +import threading +from abc import ABC, abstractmethod +from git import Repo + +import buildscripts.resmokelib.utils.registry as registry +import buildscripts.resmokelib.config as config +from buildscripts.resmokelib import errors, multiversionconstants +from buildscripts.resmokelib.utils import default_if_none +from buildscripts.resmokelib.utils import autoloader +from buildscripts.resmokelib.testing.fixtures.fixturelib import FixtureLib +from buildscripts.resmokelib.testing.fixtures.interface import _FIXTURES + +MONGO_REPO_LOCATION = "." +FIXTURE_DIR = "buildscripts/resmokelib/testing/fixtures" +RETRIEVE_DIR = "build/multiversionfixtures" +RETRIEVE_LOCK = threading.Lock() + +USE_LEGACY_MULTIVERSION = True + +_BUILDERS = {} # type: ignore + + +def make_fixture(class_name, logger, job_num, *args, **kwargs): + """Provide factory function for creating Fixture instances.""" + + fixturelib = FixtureLib() + + if class_name in _BUILDERS: + builder = _BUILDERS[class_name]() + return builder.build_fixture(logger, job_num, fixturelib, *args, **kwargs) + + if class_name not in _FIXTURES: + raise ValueError("Unknown fixture class '%s'" % class_name) + return _FIXTURES[class_name](logger, job_num, fixturelib, *args, **kwargs) + + +class FixtureBuilder(ABC, metaclass=registry.make_registry_metaclass(_BUILDERS, type(ABC))): # pylint: disable=invalid-metaclass + """ + ABC for fixture builders. + + If any fixture has special logic for assembling different components + (e.g. for multiversion), define a builder to handle it. + """ + + # For any subclass, set a REGISTERED_NAME corresponding to the fixture the class builds. + REGISTERED_NAME = "Builder" + + @abstractmethod + def build_fixture(self, logger, job_num, fixturelib, *args, **kwargs): + """Abstract method to build a replica set.""" + return + + +class ReplSetBuilder(FixtureBuilder): + """Builder class for fixtures support replication.""" + + REGISTERED_NAME = "ReplicaSetFixture" + + def build_fixture(self, logger, job_num, fixturelib, *args, **kwargs): # pylint: disable=too-many-locals + """Build a replica set.""" + # We hijack the mixed_bin_versions passed to the fixture. + mixed_bin_versions = kwargs.pop("mixed_bin_versions", config.MIXED_BIN_VERSIONS) + if USE_LEGACY_MULTIVERSION: + # We mark the use of the legacy multiversion system by allowing + # access to mixed_bin_versions. + kwargs["mixed_bin_versions"] = mixed_bin_versions + + # We also hijack the num_nodes because we need it here. + num_nodes = kwargs.pop("num_nodes", 2) + num_replset_nodes = config.NUM_REPLSET_NODES + num_nodes = num_replset_nodes if num_replset_nodes else num_nodes + kwargs["num_nodes"] = num_nodes + + replset_config_options = kwargs.get("replset_config_options", {}) + mongod_executable = default_if_none( + kwargs.get("mongod_executable"), config.MONGOD_EXECUTABLE, + config.DEFAULT_MONGOD_EXECUTABLE) + kwargs["mongod_executable"] = mongod_executable + num_nodes = kwargs["num_nodes"] + latest_mongod = mongod_executable + latest_class = "MongoDFixture" + executables = [] + classes = [] + fcv = None + + lts_class_suffix = "_last_lts" + + if mixed_bin_versions is None: + executables = [latest_mongod for x in range(num_nodes)] + classes = [latest_class for x in range(num_nodes)] + else: + + is_config_svr = "configsvr" in replset_config_options and replset_config_options[ + "configsvr"] + if USE_LEGACY_MULTIVERSION: + executables = [ + latest_mongod if (x == "new") else multiversionconstants.LAST_LTS_MONGOD_BINARY + for x in mixed_bin_versions + ] + classes = [latest_class for x in range(num_nodes)] + else: + load_version(version_path_suffix=lts_class_suffix, + shell_path=multiversionconstants.LAST_LTS_MONGO_BINARY) + + if not is_config_svr: + executables = [ + latest_mongod if + (x == "new") else multiversionconstants.LAST_LTS_MONGOD_BINARY + for x in mixed_bin_versions + ] + classes = [ + latest_class if (x == "new") else f"{latest_class}{lts_class_suffix}" + for x in mixed_bin_versions + ] + if is_config_svr: + # Our documented recommended path for upgrading shards lets us assume that config + # server nodes will always be fully upgraded before shard nodes. + executables = [latest_mongod, latest_mongod] + classes = [latest_class, latest_class] + + num_versions = len(mixed_bin_versions) + fcv = multiversionconstants.LAST_LTS_FCV + + if num_versions != num_nodes and not is_config_svr: + msg = (("The number of binary versions specified: {} do not match the number of"\ + " nodes in the replica set: {}.")).format(num_versions, num_nodes) + raise errors.ServerFailure(msg) + + replset = _FIXTURES[self.REGISTERED_NAME](logger, job_num, fixturelib, *args, **kwargs) + + replset.set_fcv(fcv) + for i in range(replset.num_nodes): + node = self._new_mongod(replset, i, executables[i], classes[i]) + replset.install_mongod(node) + + if replset.start_initial_sync_node: + if not replset.initial_sync_node: + replset.initial_sync_node_idx = replset.num_nodes + # TODO: This adds the linear chain and steady state param now, is that ok? + replset.initial_sync_node = self._new_mongod(replset, replset.initial_sync_node_idx, + latest_mongod, latest_class) + + return replset + + @classmethod + def _new_mongod(cls, replset, index, executable, mongod_class): # TODO Not a class method + """Return a standalone.MongoDFixture configured to be used as replica-set member.""" + mongod_logger = replset.get_logger_for_mongod(index) + mongod_options = replset.get_options_for_mongod(index) + + return make_fixture(mongod_class, mongod_logger, replset.job_num, + mongod_executable=executable, mongod_options=mongod_options, + preserve_dbpath=replset.preserve_dbpath) + + +def load_version(version_path_suffix=None, shell_path=None): + """Load the last_lts fixtures.""" + with RETRIEVE_LOCK, registry.suffix(version_path_suffix): + # Only one thread needs to retrieve the fixtures. + retrieve_dir = os.path.relpath(os.path.join(RETRIEVE_DIR, version_path_suffix)) + if not os.path.exists(retrieve_dir): + try: + # Avoud circular import + import buildscripts.evergreen_gen_multiversion_tests as gen_tests + commit = gen_tests.get_backports_required_hash_for_shell_version( + mongo_shell_path=shell_path) + except FileNotFoundError as err: + print("Error running the mongo shell, please ensure it's in your $PATH: ", err) + raise + retrieve_fixtures(retrieve_dir, commit) + + package_name = retrieve_dir.replace('/', '.') + autoloader.load_all_modules(name=package_name, path=[retrieve_dir]) # type: ignore + + +def retrieve_fixtures(directory, commit): + """Populate a directory with the fixture files corresponding to a commit.""" + repo = Repo(MONGO_REPO_LOCATION) + real_commit = repo.commit(commit) + tree = real_commit.tree / FIXTURE_DIR + + os.makedirs(directory, exist_ok=True) + + for blob in tree.blobs: + output = os.path.join(directory, blob.name) + with io.BytesIO(blob.data_stream.read()) as retrieved, open(output, "w") as file: + file.write(retrieved.read().decode("utf-8")) diff --git a/buildscripts/resmokelib/testing/fixtures/fixturelib.py b/buildscripts/resmokelib/testing/fixtures/fixturelib.py index 7ce01b6cbee..e163340f3a6 100644 --- a/buildscripts/resmokelib/testing/fixtures/fixturelib.py +++ b/buildscripts/resmokelib/testing/fixtures/fixturelib.py @@ -5,8 +5,9 @@ from buildscripts.resmokelib import core from buildscripts.resmokelib import errors from buildscripts.resmokelib import utils from buildscripts.resmokelib import logging +from buildscripts.resmokelib.core import network from buildscripts.resmokelib.utils.history import make_historic as _make_historic -from buildscripts.resmokelib.testing.fixtures import interface +from buildscripts.resmokelib.testing.fixtures import _builder from buildscripts.resmokelib.multiversionconstants import LAST_LTS_MONGOD_BINARY, LAST_LTS_MONGOS_BINARY @@ -39,6 +40,10 @@ class FixtureLib(object): # Programs # ############ + def make_fixture(self, class_name, logger, job_num, *args, **kwargs): + """Build fixtures by calling builder API.""" + return _builder.make_fixture(class_name, logger, job_num, *args, **kwargs) + def mongod_program(self, logger, job_num, executable, process_kwargs, mongod_options): # pylint: disable=too-many-arguments """ Return a Process instance that starts mongod arguments constructed from 'mongod_options'. @@ -86,6 +91,10 @@ class FixtureLib(object): """Return an objects whose attributes are fixture config values.""" return _FixtureConfig() + def get_next_port(self, job_num): + """Return the next available port that fixture can use.""" + return network.PortAllocator.next_fixture_port(job_num) + class _FixtureConfig(object): # pylint: disable=too-many-instance-attributes """Class that stores fixture configuration info.""" @@ -120,3 +129,6 @@ class _FixtureConfig(object): # pylint: disable=too-many-instance-attributes self.MONGOS_SET_PARAMETERS = config.MONGOS_SET_PARAMETERS self.DBPATH_PREFIX = config.DBPATH_PREFIX self.DEFAULT_DBPATH_PREFIX = config.DEFAULT_DBPATH_PREFIX + # Config servers have to be upgraded first. We hardcode the value here since there's + # no way to set it on the command line. + self.CONFIG_SVR_MIXED_BIN_VERSIONS = ["new", "new"] diff --git a/buildscripts/resmokelib/testing/fixtures/interface.py b/buildscripts/resmokelib/testing/fixtures/interface.py index 4237ca803b1..86def51322a 100644 --- a/buildscripts/resmokelib/testing/fixtures/interface.py +++ b/buildscripts/resmokelib/testing/fixtures/interface.py @@ -11,14 +11,49 @@ import pymongo.errors import buildscripts.resmokelib.multiversionconstants as multiversion import buildscripts.resmokelib.utils.registry as registry -from buildscripts.resmokelib.testing.fixtures.fixturelib import FixtureLib -_FIXTURES = {} # type: ignore +_VERSIONS = {} # type: ignore -# Represents the version of the API presented by interface.py, +# FIXTURE_API_VERSION versions the API presented by interface.py, # registry.py, and fixturelib.py. Increment this when making -# possibly-breaking changes to them. -FIXTURE_API_VERSION = 0.1 +# changes to them. Follow semantic versioning so that back-branch +# fixtures are compatible when new features are added. + + +# Note for multiversion fixtures: The formal API version here describes what +# features a fixture can expect resmokelib to provide (e.g. new_fixture_node_logger +# in fixturelib). It also provides a definition of _minimum_ functionality +# (defined in the Fixture base class) that resmoke at large can expect from a fixture. +# It is possible for fixtures to define additional public members beyond the minimum +# in the Fixture base class, for use in hooks, builders, etc. (e.g. the initial +# sync node in ReplicaSetFixture). These form an informal API of their own, which has +# less of a need to be formalized because we expect major changes to it to occur on the +# current master, allowing backwards-compatibility. On the other hand, the +# interface.py and fixturelib API establishes forward-compatibility of fixture files. +# If the informal API becomes heavily used and needs forward-compatibility, +# consider adding it to the formal API. +class APIVersion(object, metaclass=registry.make_registry_metaclass(_VERSIONS)): # pylint: disable=invalid-metaclass + """Class storing fixture API version info.""" + + REGISTERED_NAME = "APIVersion" + + FIXTURE_API_VERSION = "0.1.0" + + @classmethod + def check_api_version(cls, actual): + """Check that we are compatible with the actual API version.""" + + def to_major(version): + return int(version.split(".")[0]) + + def to_minor(version): + return int(version.split(".")[1]) + + expected = cls.FIXTURE_API_VERSION + return to_major(expected) == to_major(actual) and to_minor(expected) <= to_minor(actual) + + +_FIXTURES = {} # type: ignore class TeardownMode(Enum): @@ -34,16 +69,6 @@ class TeardownMode(Enum): ABORT = 6 -def make_fixture(class_name, logger, job_num, *args, **kwargs): - """Provide factory function for creating Fixture instances.""" - - fixturelib = FixtureLib() - - if class_name not in _FIXTURES: - raise ValueError("Unknown fixture class '%s'" % class_name) - return _FIXTURES[class_name](logger, job_num, fixturelib, *args, **kwargs) - - class Fixture(object, metaclass=registry.make_registry_metaclass(_FIXTURES)): # pylint: disable=invalid-metaclass """Base class for all fixtures.""" diff --git a/buildscripts/resmokelib/testing/fixtures/replicaset.py b/buildscripts/resmokelib/testing/fixtures/replicaset.py index e9771f0925f..7c8ac94bc6b 100644 --- a/buildscripts/resmokelib/testing/fixtures/replicaset.py +++ b/buildscripts/resmokelib/testing/fixtures/replicaset.py @@ -48,19 +48,20 @@ class ReplicaSetFixture(interface.ReplFixture): # pylint: disable=too-many-inst self.use_replica_set_connection_string = use_replica_set_connection_string self.default_read_concern = default_read_concern self.default_write_concern = default_write_concern - self.mixed_bin_versions = self.fixturelib.default_if_none(mixed_bin_versions, - self.config.MIXED_BIN_VERSIONS) - self.mixed_bin_versions_config = self.mixed_bin_versions + self.mixed_bin_versions = mixed_bin_versions self.shard_logging_prefix = shard_logging_prefix self.replicaset_logging_prefix = replicaset_logging_prefix + self.num_nodes = num_nodes + # Used by the enhanced multiversion system to signify multiversion mode. + # None implies no multiversion run. + self.fcv = None # 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) self.linear_chain = linear_chain_option if linear_chain_option else linear_chain - num_replset_nodes = self.config.NUM_REPLSET_NODES - self.num_nodes = num_replset_nodes if num_replset_nodes else num_nodes + # Legacy multiversion line if self.mixed_bin_versions is not None: mongod_executable = self.fixturelib.default_if_none( self.mongod_executable, self.config.MONGOD_EXECUTABLE, @@ -110,43 +111,29 @@ class ReplicaSetFixture(interface.ReplFixture): # pylint: disable=too-many-inst self._dbpath_prefix = os.path.join(self._dbpath_prefix, self.config.FIXTURE_SUBDIR) self.nodes = [] - self.replset_name = None + self.replset_name = self.mongod_options.setdefault("replSet", "rs") self.initial_sync_node = None self.initial_sync_node_idx = -1 def setup(self): # pylint: disable=too-many-branches,too-many-statements,too-many-locals """Set up the replica set.""" - self.replset_name = self.mongod_options.get("replSet", "rs") - if not self.nodes: - for i in range(self.num_nodes): - node = self._new_mongod(i, self.replset_name) - self.nodes.append(node) + # Version-agnostic options for mongod/s can be set here. + # Version-specific options should be set in get_version_specific_options_for_mongod() + # to avoid options for old versions being applied to new Replicaset fixtures. for i in range(self.num_nodes): - steady_state_constraint_param = "oplogApplicationEnforcesSteadyStateConstraints" - # TODO (SERVER-52985): Set steady state constraint parameters on last-lts nodes. - if (steady_state_constraint_param not in self.nodes[i].mongod_options["set_parameters"] - and self.mixed_bin_versions is not None - and self.mixed_bin_versions[i] == "new"): - self.nodes[i].mongod_options["set_parameters"][steady_state_constraint_param] = True - if self.linear_chain and i > 0: - self.nodes[i].mongod_options["set_parameters"][ - "failpoint.forceSyncSourceCandidate"] = self.fixturelib.make_historic({ - "mode": "alwaysOn", - "data": {"hostAndPort": self.nodes[i - 1].get_internal_connection_string()} - }) self.nodes[i].setup() - if self.start_initial_sync_node: - if not self.initial_sync_node: - self.initial_sync_node_idx = len(self.nodes) - self.initial_sync_node = self._new_mongod(self.initial_sync_node_idx, - self.replset_name) + if self.initial_sync_node: self.initial_sync_node.setup() self.initial_sync_node.await_ready() + # Legacy multiversion line + # TODO (SERVER-57255): Don't delete steady state constraint options when backporting to 5.0. if self.mixed_bin_versions: for i in range(self.num_nodes): + print("node[i] version: " + self.nodes[i].mongod_executable + + "mixed_bin_version[i]: " + self.mixed_bin_versions[i]) if self.nodes[i].mongod_executable != self.mixed_bin_versions[i]: msg = (f"Executable of node{i}: {self.nodes[i].mongod_executable} does not " f"match the executable assigned by mixedBinVersions: " @@ -213,23 +200,13 @@ class ReplicaSetFixture(interface.ReplFixture): # pylint: disable=too-many-inst self._initiate_repl_set(client, repl_config) self._await_primary() - if self.mixed_bin_versions is not None: - if self.mixed_bin_versions[0] == "new": - fcv_response = client.admin.command( - {"getParameter": 1, "featureCompatibilityVersion": 1}) - fcv = fcv_response["featureCompatibilityVersion"]["version"] - if fcv != ReplicaSetFixture._LATEST_FCV: - msg = (("Server returned FCV{} when we expected FCV{}.").format( - fcv, ReplicaSetFixture._LATEST_FCV)) - raise self.fixturelib.ServerFailure(msg) - + if self.fcv is not None: # Initiating a replica set with a single node will use "latest" FCV. This will # cause IncompatibleServerVersion errors if additional "last-lts" binary version # nodes are subsequently added to the set, since such nodes cannot set their FCV to # "latest". Therefore, we make sure the primary is "last-lts" FCV before adding in # nodes of different binary versions to the replica set. - client.admin.command( - {"setFeatureCompatibilityVersion": ReplicaSetFixture._LAST_LTS_FCV}) + client.admin.command({"setFeatureCompatibilityVersion": self.fcv}) if self.nodes[1:]: # Wait to connect to each of the secondaries before running the replSetReconfig @@ -581,21 +558,32 @@ class ReplicaSetFixture(interface.ReplFixture): # pylint: disable=too-many-inst """Return initial sync node from the replica set.""" return self.initial_sync_node - def _new_mongod(self, index, replset_name): - """Return a standalone.MongoDFixture configured to be used as replica-set member.""" - mongod_executable = (self.mongod_executable - if self.mixed_bin_versions is None else self.mixed_bin_versions[index]) - mongod_logger = self._get_logger_for_mongod(index) + def set_fcv(self, fcv): + """Set the fcv used by this fixtures.""" + self.fcv = fcv + + def install_mongod(self, mongod): + """Install a mongod node. Called by a builder.""" + self.nodes.append(mongod) + + def get_options_for_mongod(self, index): + """Return options that may be passed to a mongod.""" mongod_options = self.mongod_options.copy() - mongod_options["replSet"] = replset_name - mongod_options["dbpath"] = os.path.join(self._dbpath_prefix, "node{}".format(index)) - mongod_options["set_parameters"] = mongod_options.get("set_parameters", {}).copy() - return interface.make_fixture( - "MongoDFixture", mongod_logger, self.job_num, mongod_executable=mongod_executable, - mongod_options=mongod_options, preserve_dbpath=self.preserve_dbpath) + mongod_options["dbpath"] = os.path.join(self._dbpath_prefix, "node{}".format(index)) + mongod_options["set_parameters"] = mongod_options.get("set_parameters", + self.fixturelib.make_historic( + {})).copy() + + if self.linear_chain and index > 0: + self.mongod_options["set_parameters"][ + "failpoint.forceSyncSourceCandidate"] = self.fixturelib.make_historic({ + "mode": "alwaysOn", + "data": {"hostAndPort": self.nodes[index - 1].get_internal_connection_string()} + }) + return mongod_options - def _get_logger_for_mongod(self, index): + def get_logger_for_mongod(self, index): """Return a new logging.Logger instance. The instance is used as the primary, secondary, or initial sync member of a replica-set. @@ -624,9 +612,6 @@ class ReplicaSetFixture(interface.ReplFixture): # pylint: disable=too-many-inst def get_internal_connection_string(self): """Return the internal connection string.""" - if self.replset_name is None: - raise ValueError("Must call setup() before calling get_internal_connection_string()") - conn_strs = [node.get_internal_connection_string() for node in self.nodes] if self.initial_sync_node: conn_strs.append(self.initial_sync_node.get_internal_connection_string()) @@ -643,9 +628,6 @@ class ReplicaSetFixture(interface.ReplFixture): # pylint: disable=too-many-inst def get_driver_connection_url(self): """Return the driver connection URL.""" - if self.replset_name is None: - raise ValueError("Must call setup() before calling get_driver_connection_url()") - if self.use_replica_set_connection_string: # We use a replica set connection string when all nodes are electable because we # anticipate the client will want to gracefully handle any failovers. @@ -658,6 +640,10 @@ class ReplicaSetFixture(interface.ReplFixture): # pylint: disable=too-many-inst # electable because we want the client to error out if a stepdown occurs. return self.nodes[0].get_driver_connection_url() + def write_historic(self, obj): + """Convert the obj to a record to track history.""" + self.fixturelib.make_historic(obj) + def get_last_optime(client, fixturelib): """Get the latest optime. diff --git a/buildscripts/resmokelib/testing/fixtures/shardedcluster.py b/buildscripts/resmokelib/testing/fixtures/shardedcluster.py index 10fbfadd697..738f271176e 100644 --- a/buildscripts/resmokelib/testing/fixtures/shardedcluster.py +++ b/buildscripts/resmokelib/testing/fixtures/shardedcluster.py @@ -294,11 +294,16 @@ class ShardedClusterFixture(interface.Fixture): # pylint: disable=too-many-inst mongod_options["dbpath"] = os.path.join(self._dbpath_prefix, "config") mongod_options["replSet"] = ShardedClusterFixture._CONFIGSVR_REPLSET_NAME mongod_options["storageEngine"] = "wiredTiger" + config_svr_mixed_bin_version = None + if self.mixed_bin_versions is not None: + config_svr_mixed_bin_version = self.fixturelib.get_config( + ).CONFIG_SVR_MIXED_BIN_VERSIONS - return interface.make_fixture( + return self.fixturelib.make_fixture( "ReplicaSetFixture", mongod_logger, self.job_num, mongod_options=mongod_options, mongod_executable=self.mongod_executable, preserve_dbpath=preserve_dbpath, - num_nodes=num_nodes, auth_options=auth_options, mixed_bin_versions=None, + num_nodes=num_nodes, auth_options=auth_options, + mixed_bin_versions=config_svr_mixed_bin_version, replset_config_options=replset_config_options, shard_logging_prefix=shard_logging_prefix, **configsvr_options) @@ -331,7 +336,7 @@ class ShardedClusterFixture(interface.Fixture): # pylint: disable=too-many-inst mongod_options["dbpath"] = os.path.join(self._dbpath_prefix, "shard{}".format(index)) mongod_options["replSet"] = ShardedClusterFixture._SHARD_REPLSET_NAME_PREFIX + str(index) - return interface.make_fixture( + return self.fixturelib.make_fixture( "ReplicaSetFixture", mongod_logger, self.job_num, mongod_executable=self.mongod_executable, mongod_options=mongod_options, preserve_dbpath=preserve_dbpath, num_nodes=num_rs_nodes_per_shard, @@ -402,7 +407,9 @@ class _MongoSFixture(interface.Fixture): self.fixturelib.default_if_none(mongos_options, {})).copy() self.mongos = None - self.port = None + self.port = fixturelib.get_next_port(job_num) + self.mongos_options["port"] = self.port + self._dbpath_prefix = dbpath_prefix def setup(self): @@ -413,9 +420,9 @@ class _MongoSFixture(interface.Fixture): self.mongos_options["logappend"] = "" launcher = MongosLauncher(self.fixturelib) - mongos, self.port = launcher.launch_mongos_program(self.logger, self.job_num, - executable=self.mongos_executable, - mongos_options=self.mongos_options) + mongos, _ = launcher.launch_mongos_program(self.logger, self.job_num, + executable=self.mongos_executable, + mongos_options=self.mongos_options) self.mongos_options["port"] = self.port try: self.logger.info("Starting mongos on port %d...\n%s", self.port, mongos.as_command()) @@ -507,9 +514,6 @@ class _MongoSFixture(interface.Fixture): def get_internal_connection_string(self): """Return the internal connection string.""" - if self.mongos is None: - raise ValueError("Must call setup() before calling get_internal_connection_string()") - return "localhost:%d" % self.port def get_driver_connection_url(self): diff --git a/buildscripts/resmokelib/testing/fixtures/standalone.py b/buildscripts/resmokelib/testing/fixtures/standalone.py index de53305ce9a..9dcf1a31389 100644 --- a/buildscripts/resmokelib/testing/fixtures/standalone.py +++ b/buildscripts/resmokelib/testing/fixtures/standalone.py @@ -19,7 +19,6 @@ class MongoDFixture(interface.Fixture): self, logger, job_num, fixturelib, mongod_executable=None, mongod_options=None, dbpath_prefix=None, preserve_dbpath=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( self.fixturelib.default_if_none(mongod_options, {})) @@ -49,7 +48,8 @@ class MongoDFixture(interface.Fixture): self.preserve_dbpath = preserve_dbpath self.mongod = None - self.port = None + self.port = fixturelib.get_next_port(job_num) + self.mongod_options["port"] = self.port def setup(self): """Set up the mongod.""" @@ -63,10 +63,11 @@ class MongoDFixture(interface.Fixture): pass launcher = MongodLauncher(self.fixturelib) - mongod, self.port = launcher.launch_mongod_program(self.logger, self.job_num, - executable=self.mongod_executable, - mongod_options=self.mongod_options) - self.mongod_options["port"] = self.port + # Second return val is the port, which we ignore because we explicitly created the port above. + # The port is used to set other mongod_option's here: https://github.com/mongodb/mongo/blob/532a6a8ae7b8e7ab5939e900759c00794862963d/buildscripts/resmokelib/testing/fixtures/replicaset.py#L136 + mongod, _ = launcher.launch_mongod_program(self.logger, self.job_num, + executable=self.mongod_executable, + mongod_options=self.mongod_options) try: self.logger.info("Starting mongod on port %d...\n%s", self.port, mongod.as_command()) mongod.start() @@ -166,9 +167,6 @@ class MongoDFixture(interface.Fixture): def get_internal_connection_string(self): """Return the internal connection string.""" - if self.mongod is None: - raise ValueError("Must call setup() before calling get_internal_connection_string()") - return "localhost:%d" % self.port def get_driver_connection_url(self): diff --git a/buildscripts/resmokelib/testing/fixtures/tenant_migration.py b/buildscripts/resmokelib/testing/fixtures/tenant_migration.py index fd0dd9c5f2d..9431c2116b4 100644 --- a/buildscripts/resmokelib/testing/fixtures/tenant_migration.py +++ b/buildscripts/resmokelib/testing/fixtures/tenant_migration.py @@ -49,21 +49,6 @@ class TenantMigrationFixture(interface.Fixture): # pylint: disable=too-many-ins self.replica_sets = [] - # The ReplicaSetFixture for the replica set that starts out owning the data (i.e. the - # replica set that driver should connect to when running commands). - self.replica_set_with_tenant = None - - def pids(self): - """:return: pids owned by this fixture if any.""" - out = [] - for replica_set in self.replica_sets: - out.extend(replica_set.pids()) - if not out: - self.logger.debug('No replica sets when gathering multi replicaset fixture pids.') - return out - - def setup(self): - """Set up the replica sets.""" if not self.replica_sets: for i in range(self.num_replica_sets): rs_name = f"rs{i}" @@ -73,7 +58,7 @@ class TenantMigrationFixture(interface.Fixture): # pylint: disable=too-many-ins mongod_options["replSet"] = rs_name self.replica_sets.append( - interface.make_fixture( + self.fixturelib.make_fixture( "ReplicaSetFixture", self.logger, self.job_num, mongod_options=mongod_options, preserve_dbpath=self.preserve_dbpath, num_nodes=self.num_nodes_per_replica_set, auth_options=self.auth_options, @@ -83,9 +68,21 @@ class TenantMigrationFixture(interface.Fixture): # pylint: disable=too-many-ins use_replica_set_connection_string=self.use_replica_set_connection_string, all_nodes_electable=self.all_nodes_electable)) - self.replica_set_with_tenant = self.replica_sets[0] + # The ReplicaSetFixture for the replica set that starts out owning the data (i.e. the + # replica set that driver should connect to when running commands). + self.replica_set_with_tenant = self.replica_sets[0] - # Start up each of the replica sets + def pids(self): + """:return: pids owned by this fixture if any.""" + out = [] + for replica_set in self.replica_sets: + out.extend(replica_set.pids()) + if not out: + self.logger.debug('No replica sets when gathering multi replicaset fixture pids.') + return out + + def setup(self): + """Set up the replica sets.""" for replica_set in self.replica_sets: replica_set.setup() self._create_tenant_migration_donor_and_recipient_roles(replica_set) diff --git a/buildscripts/resmokelib/utils/registry.py b/buildscripts/resmokelib/utils/registry.py index 47b51b051c5..d8230938681 100644 --- a/buildscripts/resmokelib/utils/registry.py +++ b/buildscripts/resmokelib/utils/registry.py @@ -6,6 +6,8 @@ This pattern enables the associated class to be looked up later by using its name. """ +import threading +from contextlib import contextmanager from buildscripts.resmokelib.utils import default_if_none # Specifying 'LEAVE_UNREGISTERED' as the "REGISTERED_NAME" attribute will cause the class to be @@ -13,6 +15,27 @@ from buildscripts.resmokelib.utils import default_if_none # or common functionality, and aren't intended to be constructed explicitly. LEAVE_UNREGISTERED = object() +GLOBAL_SUFFIX = "" +SUFFIX_LOCK = threading.Lock() + + +@contextmanager +def suffix(suf): + """ + Set a global suffix that's postpended to registered names. + + This is used to enable dynamically imported classes from other branches for + multiversion tests. These classes need a unique suffix to not conflict with + corresponding classes on master (and possibly other) branches. The suffix has to + be set at runtime for the duration of the import, which is why this + contextmanager + global runtime variable is used. + """ + global GLOBAL_SUFFIX # pylint: disable=global-statement + GLOBAL_SUFFIX = suf + with SUFFIX_LOCK: + yield suf + GLOBAL_SUFFIX = "" + def make_registry_metaclass(registry_store, base_metaclass=None): """Return a new Registry metaclass.""" @@ -47,11 +70,12 @@ def make_registry_metaclass(registry_store, base_metaclass=None): cls = base_metaclass.__new__(mcs, class_name, base_classes, class_dict) if registered_name is not LEAVE_UNREGISTERED: - if registered_name in registry_store: + name_to_register = f"{registered_name}{GLOBAL_SUFFIX}" + if name_to_register in registry_store: raise ValueError( "The name %s is already registered; a different value for the" " 'REGISTERED_NAME' attribute must be chosen" % (registered_name)) - registry_store[registered_name] = cls + registry_store[name_to_register] = cls return cls diff --git a/buildscripts/tests/resmokelib/testing/fixtures/test_api_adherence.py b/buildscripts/tests/resmokelib/testing/fixtures/test_api_adherence.py index 0ea3ca2acb3..7f0e1110a32 100644 --- a/buildscripts/tests/resmokelib/testing/fixtures/test_api_adherence.py +++ b/buildscripts/tests/resmokelib/testing/fixtures/test_api_adherence.py @@ -14,7 +14,9 @@ ALLOWED_IMPORTS = [ "buildscripts.resmokelib.utils.registry", ] FIXTURE_PATH = os.path.normpath("buildscripts/resmokelib/testing/fixtures") -IGNORED_FILES = ["__init__.py", "fixturelib.py"] + +# These files are not part of the fixure API. +IGNORED_FILES = ["__init__.py", "fixturelib.py", "_builder.py"] class AdherenceChecker(ast.NodeVisitor): diff --git a/buildscripts/tests/resmokelib/testing/fixtures/test_builder.py b/buildscripts/tests/resmokelib/testing/fixtures/test_builder.py new file mode 100644 index 00000000000..9717086a06f --- /dev/null +++ b/buildscripts/tests/resmokelib/testing/fixtures/test_builder.py @@ -0,0 +1,24 @@ +"""Unit tests for the resmokelib.testing.fixtures.multiversion module.""" + +import unittest +import filecmp +import os + +from buildscripts.resmokelib.testing.fixtures import _builder + +TEST_COMMIT = "9fbf58e9f1bee720d270cfa9621f581a0212e5ff" + + +class TestFixtureBuilder(unittest.TestCase): + """Class that test retrieve_fixtures methods.""" + + def test_retrieve_fixtures(self): + """function to test retrieve_fixtures""" + dirpath = os.path.join("build", "multiversionfixture") + expected_standalone = os.path.join("buildscripts", "tests", "resmokelib", "testing", + "fixtures", "retrieved_fixture.txt") + _builder.retrieve_fixtures(dirpath, TEST_COMMIT) + retrieved_standalone = os.path.join(dirpath, "standalone.py") + self.assertTrue( + filecmp.cmpfiles(retrieved_standalone, expected_standalone, + ["standalone.py", "retrieved_fixture.txt"], shallow=False)) diff --git a/buildscripts/tests/test_burn_in_tests_multiversion.py b/buildscripts/tests/test_burn_in_tests_multiversion.py index 9cd304a7bf0..ac9fda0974c 100644 --- a/buildscripts/tests/test_burn_in_tests_multiversion.py +++ b/buildscripts/tests/test_burn_in_tests_multiversion.py @@ -138,7 +138,9 @@ class TestCreateMultiversionGenerateTasksConfig(unittest.TestCase): # We should not generate any tasks that are not part of the burn_in_multiversion suite. self.assertEqual(0, len(evg_config_dict["tasks"])) - @patch("buildscripts.evergreen_gen_multiversion_tests.get_backports_required_last_lts_hash") + @patch( + "buildscripts.evergreen_gen_multiversion_tests.get_backports_required_hash_for_shell_version" + ) def test_one_task_one_test(self, mock_hash): mock_hash.return_value = MONGO_4_2_HASH n_tasks = 1 @@ -155,7 +157,9 @@ class TestCreateMultiversionGenerateTasksConfig(unittest.TestCase): tasks = evg_config_dict["tasks"] self.assertEqual(len(tasks), NUM_REPL_MIXED_VERSION_CONFIGS * n_tests) - @patch("buildscripts.evergreen_gen_multiversion_tests.get_backports_required_last_lts_hash") + @patch( + "buildscripts.evergreen_gen_multiversion_tests.get_backports_required_hash_for_shell_version" + ) def test_n_task_one_test(self, mock_hash): mock_hash.return_value = MONGO_4_2_HASH n_tasks = 2 @@ -174,7 +178,9 @@ class TestCreateMultiversionGenerateTasksConfig(unittest.TestCase): len(tasks), (NUM_REPL_MIXED_VERSION_CONFIGS + NUM_SHARDED_MIXED_VERSION_CONFIGS) * n_tests) - @patch("buildscripts.evergreen_gen_multiversion_tests.get_backports_required_last_lts_hash") + @patch( + "buildscripts.evergreen_gen_multiversion_tests.get_backports_required_hash_for_shell_version" + ) def test_one_task_n_test(self, mock_hash): mock_hash.return_value = MONGO_4_2_HASH n_tasks = 1 @@ -191,7 +197,9 @@ class TestCreateMultiversionGenerateTasksConfig(unittest.TestCase): tasks = evg_config_dict["tasks"] self.assertEqual(len(tasks), NUM_REPL_MIXED_VERSION_CONFIGS * n_tests) - @patch("buildscripts.evergreen_gen_multiversion_tests.get_backports_required_last_lts_hash") + @patch( + "buildscripts.evergreen_gen_multiversion_tests.get_backports_required_hash_for_shell_version" + ) def test_n_task_m_test(self, mock_hash): mock_hash.return_value = MONGO_4_2_HASH n_tasks = 2 diff --git a/buildscripts/tests/test_evergreen_gen_multiversion_tests.py b/buildscripts/tests/test_evergreen_gen_multiversion_tests.py index 5b6072651dc..1cb4930460c 100644 --- a/buildscripts/tests/test_evergreen_gen_multiversion_tests.py +++ b/buildscripts/tests/test_evergreen_gen_multiversion_tests.py @@ -84,7 +84,7 @@ class TestGenerateExcludeYaml(unittest.TestCase): Helper to patch and run the test. """ mock_multiversion_methods = { - 'get_backports_required_last_lts_hash': MagicMock(), + 'get_backports_required_hash_for_shell_version': MagicMock(), 'get_last_lts_yaml': MagicMock(return_value=last_lts) } @@ -95,14 +95,12 @@ class TestGenerateExcludeYaml(unittest.TestCase): output = os.path.join(self._tmpdir.name, under_test.EXCLUDE_TAGS_FILE) runner = CliRunner() - result = runner.invoke( - under_test.generate_exclude_yaml, - [f"--output={output}", '--task-path-suffix=/data/multiversion']) + result = runner.invoke(under_test.generate_exclude_yaml, [f"--output={output}"]) self.assertEqual(result.exit_code, 0, result) mock_read_yaml.assert_called_once() mock_multiversion_methods[ - 'get_backports_required_last_lts_hash'].assert_called_once() + 'get_backports_required_hash_for_shell_version'].assert_called_once() mock_multiversion_methods['get_last_lts_yaml'].assert_called_once() def test_create_yaml_suite1(self): diff --git a/evergreen/burn_in_tests.sh b/evergreen/burn_in_tests.sh index 2ef93e46958..66330a0386d 100755 --- a/evergreen/burn_in_tests.sh +++ b/evergreen/burn_in_tests.sh @@ -8,7 +8,8 @@ set -o verbose activate_venv # Multiversion exclusions can be used when selecting tests. -$python buildscripts/evergreen_gen_multiversion_tests.py generate-exclude-tags --task-path-suffix=/data/multiversion --output=multiversion_exclude_tags.yml +PATH="$PATH:/data/multiversion" +$python buildscripts/evergreen_gen_multiversion_tests.py generate-exclude-tags --output=multiversion_exclude_tags.yml # Capture a list of new and modified tests. The expansion macro burn_in_tests_build_variant # is used to for finding the associated tasks from a different build varaint than the diff --git a/evergreen/burn_in_tests_generate.sh b/evergreen/burn_in_tests_generate.sh index 248144e968f..0419795c1a0 100644 --- a/evergreen/burn_in_tests_generate.sh +++ b/evergreen/burn_in_tests_generate.sh @@ -8,6 +8,7 @@ set -o errexit activate_venv # Multiversion exclusions can be used when selecting tests. -$python buildscripts/evergreen_gen_multiversion_tests.py generate-exclude-tags --task-path-suffix=/data/multiversion --output=multiversion_exclude_tags.yml +PATH="$PATH:/data/multiversion" +$python buildscripts/evergreen_gen_multiversion_tests.py generate-exclude-tags --output=multiversion_exclude_tags.yml PATH=$PATH:$HOME $python buildscripts/burn_in_tags.py --expansion-file ../expansions.yml diff --git a/evergreen/burn_in_tests_multiversion.sh b/evergreen/burn_in_tests_multiversion.sh index eddb223b8b4..90597f6114d 100755 --- a/evergreen/burn_in_tests_multiversion.sh +++ b/evergreen/burn_in_tests_multiversion.sh @@ -18,4 +18,5 @@ fi burn_in_args="$burn_in_args" # Evergreen executable is in $HOME. PATH="$PATH:$HOME" eval $python buildscripts/burn_in_tests_multiversion.py --task_id=${task_id} --project=${project} $build_variant_opts --distro=${distro_id} --generate-tasks-file=burn_in_tests_multiversion_gen.json $burn_in_args --verbose -$python buildscripts/evergreen_gen_multiversion_tests.py generate-exclude-tags --task-path-suffix=/data/multiversion +PATH="$PATH:/data/multiversion" +$python buildscripts/evergreen_gen_multiversion_tests.py generate-exclude-tags diff --git a/evergreen/explicit_multiversion_tasks_generate.sh b/evergreen/explicit_multiversion_tasks_generate.sh index e5b692bd715..eefbc68fd40 100644 --- a/evergreen/explicit_multiversion_tasks_generate.sh +++ b/evergreen/explicit_multiversion_tasks_generate.sh @@ -6,5 +6,7 @@ cd src set -o errexit activate_venv + +PATH="$PATH:/data/multiversion" $python buildscripts/evergreen_generate_resmoke_tasks.py --expansion-file ../expansions.yml --verbose -$python buildscripts/evergreen_gen_multiversion_tests.py generate-exclude-tags --task-path-suffix=${use_multiversion} +$python buildscripts/evergreen_gen_multiversion_tests.py generate-exclude-tags diff --git a/evergreen/implicit_multiversions_tasks_generate.sh b/evergreen/implicit_multiversions_tasks_generate.sh index f4d7a235e09..b4df4d1105b 100644 --- a/evergreen/implicit_multiversions_tasks_generate.sh +++ b/evergreen/implicit_multiversions_tasks_generate.sh @@ -6,5 +6,6 @@ cd src set -o errexit activate_venv +PATH="$PATH:/data/multiversion" $python buildscripts/evergreen_gen_multiversion_tests.py run --expansion-file ../expansions.yml -$python buildscripts/evergreen_gen_multiversion_tests.py generate-exclude-tags --task-path-suffix=${task_path_suffix} +$python buildscripts/evergreen_gen_multiversion_tests.py generate-exclude-tags diff --git a/evergreen/randomized_multiversion_tasks_generate_exclude_tags.sh b/evergreen/randomized_multiversion_tasks_generate_exclude_tags.sh index 5adfabd3b4f..77524a187b4 100644 --- a/evergreen/randomized_multiversion_tasks_generate_exclude_tags.sh +++ b/evergreen/randomized_multiversion_tasks_generate_exclude_tags.sh @@ -7,4 +7,5 @@ set -o errexit set -o verbose activate_venv -$python buildscripts/evergreen_gen_multiversion_tests.py generate-exclude-tags --task-path-suffix=${use_multiversion} +PATH="$PATH:/data/multiversion" +$python buildscripts/evergreen_gen_multiversion_tests.py generate-exclude-tags |