diff options
24 files changed, 246 insertions, 82 deletions
diff --git a/SConstruct b/SConstruct index 1d93db8371d..ffbf1280250 100644 --- a/SConstruct +++ b/SConstruct @@ -5381,24 +5381,6 @@ if has_option("cache"): addNoCacheEmitter(env['BUILDERS']['SharedLibrary']) addNoCacheEmitter(env['BUILDERS']['LoadableModule']) - -# We need to be explicit about including $DESTDIR here, unlike most -# other places. Normally, auto_install_binaries will take care of -# injecting DESTDIR for us, but we aren't using that now. -resmoke_install_dir = env.subst("$DESTDIR/$PREFIX_BINDIR") -resmoke_install_dir = os.path.normpath(resmoke_install_dir).replace("\\", r"\\") - -# Much blood sweat and tears were shed getting to this point. Any version of -# this that uses SCons builders and a scanner will either not regenerate when it -# should, cause everything to rebuild, or conflict with ninja. Sometimes all -# three. So we've decided it's best to just write this file here every time -# because it's the only solution that always works. -with open("resmoke.ini", "w") as resmoke_config: - resmoke_config.write(""" -[resmoke] -install_dir = {install_dir} -""".format(install_dir=resmoke_install_dir)) - env.SConscript( dirs=[ 'src', diff --git a/buildscripts/burn_in_tags.py b/buildscripts/burn_in_tags.py index 1aba5eab457..5acb2e75861 100644 --- a/buildscripts/burn_in_tags.py +++ b/buildscripts/burn_in_tags.py @@ -132,7 +132,8 @@ def _generate_evg_build_variant( # pylint: disable=too-many-arguments,too-many-locals def _generate_evg_tasks(evergreen_api: EvergreenApi, shrub_project: ShrubProject, task_expansions: Dict[str, Any], build_variant_map: Dict[str, str], - repos: List[Repo], evg_conf: EvergreenProjectConfig) -> None: + repos: List[Repo], evg_conf: EvergreenProjectConfig, + install_dir: str) -> None: """ Generate burn in tests tasks for a given shrub config and group of build variants. @@ -147,7 +148,7 @@ def _generate_evg_tasks(evergreen_api: EvergreenApi, shrub_project: ShrubProject task_id = task_expansions[TASK_ID_EXPANSION] change_detector = EvergreenFileChangeDetector(task_id, evergreen_api, os.environ) changed_tests = change_detector.find_changed_tests(repos) - tests_by_task = create_tests_by_task(build_variant, evg_conf, changed_tests) + tests_by_task = create_tests_by_task(build_variant, evg_conf, changed_tests, install_dir) if tests_by_task: shrub_build_variant = _generate_evg_build_variant( evg_conf.get_variant(build_variant), run_build_variant, @@ -165,7 +166,7 @@ def _generate_evg_tasks(evergreen_api: EvergreenApi, shrub_project: ShrubProject def burn_in(task_expansions: Dict[str, Any], evg_conf: EvergreenProjectConfig, - evergreen_api: RetryingEvergreenApi, repos: List[Repo]): + evergreen_api: RetryingEvergreenApi, repos: List[Repo], install_dir: str): """ Execute main program. @@ -173,11 +174,12 @@ def burn_in(task_expansions: Dict[str, Any], evg_conf: EvergreenProjectConfig, :param evg_conf: Evergreen configuration. :param evergreen_api: Evergreen.py object. :param repos: Git repositories. + :param install_dir: path to bin directory of a testable installation """ shrub_project = ShrubProject.empty() build_variant_map = _create_evg_build_variant_map(task_expansions) _generate_evg_tasks(evergreen_api, shrub_project, task_expansions, build_variant_map, repos, - evg_conf) + evg_conf, install_dir) if not validate_task_generation_limit(shrub_project): sys.exit(1) @@ -204,7 +206,9 @@ def _configure_logging(verbose: bool): @click.option("--expansion-file", "expansion_file", required=True, help="Location of expansions file generated by evergreen.") @click.option("--verbose", is_flag=True) -def main(expansion_file: str, verbose: bool): +@click.option("--install-dir", "install_dir", required=True, + help="Path to bin directory of a testable installation") +def main(expansion_file: str, verbose: bool, install_dir: str): """ Run new or changed tests in repeated mode to validate their stability. @@ -220,7 +224,7 @@ def main(expansion_file: str, verbose: bool): expansions_file_data = read_config.read_config_file(expansion_file) evg_conf = evergreen.parse_evergreen_file(EVERGREEN_FILE) - burn_in(expansions_file_data, evg_conf, evg_api, repos) + burn_in(expansions_file_data, evg_conf, evg_api, repos, install_dir) if __name__ == "__main__": diff --git a/buildscripts/burn_in_tests.py b/buildscripts/burn_in_tests.py index dbd5f29f92c..27da0996b63 100755 --- a/buildscripts/burn_in_tests.py +++ b/buildscripts/burn_in_tests.py @@ -352,7 +352,7 @@ def create_task_list_for_tests(changed_tests: Set[str], build_variant: str, def create_tests_by_task(build_variant: str, evg_conf: EvergreenProjectConfig, - changed_tests: Set[str]) -> Dict[str, TaskInfo]: + changed_tests: Set[str], install_dir: str) -> Dict[str, TaskInfo]: """ Create a list of tests by task. @@ -367,7 +367,7 @@ def create_tests_by_task(build_variant: str, evg_conf: EvergreenProjectConfig, exclude_tests.append(f"{ENTERPRISE_MODULE_PATH}/**/*") changed_tests = filter_tests(changed_tests, exclude_tests) - buildscripts.resmokelib.parser.set_run_options() + buildscripts.resmokelib.parser.set_run_options(f"--installDir={shlex.quote(install_dir)}") if changed_tests: return create_task_list_for_tests(changed_tests, build_variant, evg_conf, exclude_suites, exclude_tasks) @@ -553,7 +553,7 @@ class BurnInOrchestrator: self.burn_in_executor = burn_in_executor self.evg_conf = evg_conf - def burn_in(self, repos: List[Repo], build_variant: str) -> None: + def burn_in(self, repos: List[Repo], build_variant: str, install_dir: str) -> None: """ Execute burn in tests for the given git repositories. @@ -563,12 +563,14 @@ class BurnInOrchestrator: changed_tests = self.change_detector.find_changed_tests(repos) LOGGER.info("Found changed tests", files=changed_tests) - tests_by_task = create_tests_by_task(build_variant, self.evg_conf, changed_tests) + tests_by_task = create_tests_by_task(build_variant, self.evg_conf, changed_tests, + install_dir) LOGGER.debug("tests and tasks found", tests_by_task=tests_by_task) self.burn_in_executor.execute(tests_by_task) +# pylint: disable=too-many-function-args @click.command(context_settings=dict(ignore_unknown_options=True)) @click.option("--no-exec", "no_exec", default=False, is_flag=True, help="Do not execute the found tests.") @@ -586,12 +588,14 @@ class BurnInOrchestrator: @click.option( "--origin-rev", "origin_rev", default=None, help="The revision in the mongo repo that changes will be compared against if specified.") +@click.option("--install-dir", "install_dir", required=True, type=str, + help="Path to bin directory of a testable installation") @click.argument("resmoke_args", nargs=-1, type=click.UNPROCESSED) # pylint: disable=too-many-arguments,too-many-locals def main(build_variant: str, no_exec: bool, repeat_tests_num: Optional[int], repeat_tests_min: Optional[int], repeat_tests_max: Optional[int], repeat_tests_secs: Optional[int], resmoke_args: str, verbose: bool, - origin_rev: Optional[str]) -> None: + origin_rev: Optional[str], install_dir: str) -> None: """ Run new or changed tests in repeated mode to validate their stability. @@ -639,7 +643,7 @@ def main(build_variant: str, no_exec: bool, repeat_tests_num: Optional[int], executor = NopBurnInExecutor() burn_in_orchestrator = BurnInOrchestrator(change_detector, executor, evg_conf) - burn_in_orchestrator.burn_in(repos, build_variant) + burn_in_orchestrator.burn_in(repos, build_variant, install_dir) if __name__ == "__main__": diff --git a/buildscripts/evergreen_burn_in_tests.py b/buildscripts/evergreen_burn_in_tests.py index 7ac28e4063c..37a0d2874ff 100644 --- a/buildscripts/evergreen_burn_in_tests.py +++ b/buildscripts/evergreen_burn_in_tests.py @@ -419,7 +419,7 @@ class GenerateBurnInExecutor(BurnInExecutor): # pylint: disable=too-many-arguments def burn_in(task_id: str, build_variant: str, generate_config: GenerateConfig, repeat_config: RepeatConfig, evg_api: EvergreenApi, evg_conf: EvergreenProjectConfig, - repos: List[Repo], generate_tasks_file: str) -> None: + repos: List[Repo], generate_tasks_file: str, install_dir: str) -> None: """ Run burn_in_tests. @@ -431,12 +431,13 @@ def burn_in(task_id: str, build_variant: str, generate_config: GenerateConfig, :param evg_conf: Evergreen project configuration. :param repos: Git repos containing changes. :param generate_tasks_file: File to write generate tasks configuration to. + :param install_dir: Path to bin directory of a testable installation """ change_detector = EvergreenFileChangeDetector(task_id, evg_api, os.environ) executor = GenerateBurnInExecutor(generate_config, repeat_config, evg_api, generate_tasks_file) burn_in_orchestrator = BurnInOrchestrator(change_detector, executor, evg_conf) - burn_in_orchestrator.burn_in(repos, build_variant) + burn_in_orchestrator.burn_in(repos, build_variant, install_dir) @click.command() @@ -463,11 +464,13 @@ def burn_in(task_id: str, build_variant: str, generate_config: GenerateConfig, @click.option("--verbose", "verbose", default=False, is_flag=True, help="Enable extra logging.") @click.option("--task_id", "task_id", required=True, metavar='TASK_ID', help="The evergreen task id.") +@click.option("--install-dir", "install_dir", required=True, + help="Path to bin directory of a testable installation.") # pylint: disable=too-many-arguments,too-many-locals def main(build_variant: str, run_build_variant: str, distro: str, project: str, generate_tasks_file: str, repeat_tests_num: Optional[int], repeat_tests_min: Optional[int], repeat_tests_max: Optional[int], repeat_tests_secs: Optional[int], evg_api_config: str, - verbose: bool, task_id: str): + verbose: bool, task_id: str, install_dir: str): """ Run new or changed tests in repeated mode to validate their stability. @@ -500,6 +503,7 @@ def main(build_variant: str, run_build_variant: str, distro: str, project: str, :param evg_api_config: Location of configuration file to connect to evergreen. :param verbose: Log extra debug information. :param task_id: Id of evergreen task being run in. + :param install_dir: path to bin directory of a testable installation """ _configure_logging(verbose) @@ -520,7 +524,7 @@ def main(build_variant: str, run_build_variant: str, distro: str, project: str, generate_config.validate(evg_conf) burn_in(task_id, build_variant, generate_config, repeat_config, evg_api, evg_conf, repos, - generate_tasks_file) + generate_tasks_file, install_dir) if __name__ == "__main__": diff --git a/buildscripts/evergreen_task_timeout.py b/buildscripts/evergreen_task_timeout.py index f2177b35a16..bd5d48275d9 100755 --- a/buildscripts/evergreen_task_timeout.py +++ b/buildscripts/evergreen_task_timeout.py @@ -8,6 +8,7 @@ import sys from datetime import datetime, timedelta from pathlib import Path from typing import Dict, List, Optional +import shlex import inject import structlog @@ -15,6 +16,7 @@ import yaml from pydantic import BaseModel from evergreen import EvergreenApi, RetryingEvergreenApi +from buildscripts.task_generation.resmoke_proxy import ResmokeProxyService from buildscripts.ciconfig.evergreen import (EvergreenProjectConfig, parse_evergreen_file) from buildscripts.timeouts.timeout_service import (TimeoutParams, TimeoutService, TimeoutSettings) from buildscripts.util.cmdutils import enable_logging @@ -319,6 +321,8 @@ def main(): """Determine the timeout value a task should use in evergreen.""" parser = argparse.ArgumentParser(description=main.__doc__) + parser.add_argument("--install-dir", dest="install_dir", required=True, + help="Path to bin directory of testable installation") parser.add_argument("--task-name", dest="task", required=True, help="Task being executed.") parser.add_argument("--suite-name", dest="suite_name", required=True, help="Resmoke suite being run against.") @@ -363,6 +367,9 @@ def main(): binder.bind(TimeoutOverrides, timeout_overrides) binder.bind(EvergreenProjectConfig, parse_evergreen_file(os.path.expanduser(options.evg_project_config))) + binder.bind( + ResmokeProxyService, + ResmokeProxyService(run_options=f"--installDir={shlex.quote(options.install_dir)}")) inject.configure(dependencies) diff --git a/buildscripts/idl/check_stable_api_commands_have_idl_definitions.py b/buildscripts/idl/check_stable_api_commands_have_idl_definitions.py index b8b0c0ccae7..af3fe729ab7 100644 --- a/buildscripts/idl/check_stable_api_commands_have_idl_definitions.py +++ b/buildscripts/idl/check_stable_api_commands_have_idl_definitions.py @@ -167,14 +167,20 @@ def main(): arg_parser = argparse.ArgumentParser(description=__doc__) arg_parser.add_argument("--include", type=str, action="append", help="Directory to search for IDL import files") - arg_parser.add_argument("--installDir", dest="install_dir", metavar="INSTALL_DIR", + arg_parser.add_argument("--install-dir", dest="install_dir", required=True, help="Directory to search for MongoDB binaries") arg_parser.add_argument("-v", "--verbose", action="count", help="Enable verbose logging") arg_parser.add_argument("api_version", metavar="API_VERSION", help="API Version to check") args = arg_parser.parse_args() + class FakeArgs: + """Fake argparse.Namespace-like class to pass arguments to _update_config_vars.""" + + def __init__(self): + self.INSTALL_DIR = args.install_dir # pylint: disable=invalid-name + # pylint: disable=protected-access - configure_resmoke._update_config_vars(object) + configure_resmoke._update_config_vars(FakeArgs()) configure_resmoke._set_logging_config() # Configure Fixture logging. diff --git a/buildscripts/resmoke.ini.in b/buildscripts/resmoke.ini.in deleted file mode 100644 index 5ee714b6dd2..00000000000 --- a/buildscripts/resmoke.ini.in +++ /dev/null @@ -1,2 +0,0 @@ -[resmoke] -install_dir = @install_dir@
\ No newline at end of file diff --git a/buildscripts/resmokelib/configure_resmoke.py b/buildscripts/resmokelib/configure_resmoke.py index cc474d4dc59..ae3e05df25d 100644 --- a/buildscripts/resmokelib/configure_resmoke.py +++ b/buildscripts/resmokelib/configure_resmoke.py @@ -9,6 +9,9 @@ import distutils.spawn import sys import platform import random +import glob +import textwrap +import shlex import pymongo.uri_parser @@ -133,6 +136,19 @@ def _validate_config(parser): # pylint: disable=too-many-branches parser.error(f"Found '{resolved_path}', but it is not an executable file") +def _find_resmoke_wrappers(): + # This is technically incorrect. PREFIX_BINDIR defaults to $PREFIX/bin, so + # if the user changes it to any some other value, this glob will fail to + # detect the resmoke wrapper. + # Additionally, the resmoke wrapper will not be found if a user performs + # their builds outside of the git repository root, (ex checkout at + # /data/mongo, build-dir at /data/build) + # We assume that users who fall under either case will explicitly pass the + # --installDir argument. + candidate_installs = glob.glob("**/bin/resmoke.py", recursive=True) + return list(map(os.path.dirname, candidate_installs)) + + def _update_config_vars(values): # pylint: disable=too-many-statements,too-many-locals,too-many-branches """Update the variables of the config module.""" @@ -148,11 +164,25 @@ def _update_config_vars(values): # pylint: disable=too-many-statements,too-many config[cmdline_key] = cmdline_vars[cmdline_key] if os.path.isfile("resmoke.ini"): + err = textwrap.dedent("""\ +Support for resmoke.ini has been removed. You must delete +resmoke.ini and rerun your build to run resmoke. If only one testable +installation is present, resmoke will automatically locate that installation. +If you have multiple installations, you must either pass an explicit +--installDir argument to the run subcommand to identify the installation you +would like to test, or invoke the customized resmoke.py wrapper script staged +into the bin directory of each installation.""") config_parser = configparser.ConfigParser() config_parser.read("resmoke.ini") if "resmoke" in config_parser.sections(): user_config = dict(config_parser["resmoke"]) - config.update(user_config) + err += textwrap.dedent(f""" + +Based on the current value of resmoke.ini, after rebuilding, resmoke.py should +be invoked as either: +- {shlex.quote(f"{user_config['install_dir']}/resmoke.py")} +- buildscripts/resmoke.py --installDir {shlex.quote(user_config['install_dir'])}""") + raise RuntimeError(err) def setup_feature_flags(): _config.RUN_ALL_FEATURE_FLAG_TESTS = config.pop("run_all_feature_flag_tests") @@ -224,6 +254,18 @@ def _update_config_vars(values): # pylint: disable=too-many-statements,too-many _config.MULTIVERSION_BIN_VERSION = config.pop("old_bin_version") _config.INSTALL_DIR = config.pop("install_dir") + if _config.INSTALL_DIR is None: + resmoke_wrappers = _find_resmoke_wrappers() + if len(resmoke_wrappers) == 1: + _config.INSTALL_DIR = resmoke_wrappers[0] + elif len(resmoke_wrappers) > 1: + err = textwrap.dedent(f"""\ +Multiple testable installations were found, but installDir was not specified. +You must either call resmoke via one of the following scripts: +{os.linesep.join(shlex.quote(resmoke_wrappers))} + +or explicitly pass --installDir to the run subcommand of buildscripts/resmoke.py.""") + raise RuntimeError(err) if _config.INSTALL_DIR is not None: # Normalize the path so that on Windows dist-test/bin # translates to .\dist-test\bin then absolutify it since the diff --git a/buildscripts/resmokelib/testing/testcases/sdam_json_test.py b/buildscripts/resmokelib/testing/testcases/sdam_json_test.py index 3318e8c6ccc..0c952a84490 100644 --- a/buildscripts/resmokelib/testing/testcases/sdam_json_test.py +++ b/buildscripts/resmokelib/testing/testcases/sdam_json_test.py @@ -14,7 +14,6 @@ class SDAMJsonTestCase(interface.ProcessTestCase): """Server Discovery and Monitoring JSON test case.""" REGISTERED_NAME = "sdam_json_test" - EXECUTABLE_BUILD_PATH = "build/**/mongo/client/sdam/sdam_json_test" TEST_DIR = os.path.normpath("src/mongo/client/sdam/json_tests/sdam_tests") def __init__(self, logger, json_test_file, program_options=None): @@ -25,22 +24,14 @@ class SDAMJsonTestCase(interface.ProcessTestCase): self.json_test_file = os.path.normpath(json_test_file) self.program_options = utils.default_if_none(program_options, {}).copy() - def _find_executable(self): - if config.INSTALL_DIR is not None: - binary = os.path.join(config.INSTALL_DIR, "sdam_json_test") - if os.name == "nt": - binary += ".exe" - - if os.path.isfile(binary): - return binary - - execs = globstar.glob(self.EXECUTABLE_BUILD_PATH + '.exe') - if not execs: - execs = globstar.glob(self.EXECUTABLE_BUILD_PATH) - if len(execs) != 1: - raise errors.StopExecution( - "There must be a single sdam_json_test binary in {}".format(execs)) - return execs[0] + def _find_executable(self): # pylint: disable=no-self-use + binary = os.path.join(config.INSTALL_DIR, "sdam_json_test") + if os.name == "nt": + binary += ".exe" + + if not os.path.isfile(binary): + raise errors.StopExecution(f"Failed to locate sdam_json_test binary at {binary}") + return binary def _make_process(self): command_line = [self.program_executable] diff --git a/buildscripts/resmokelib/testing/testcases/server_selection_json_test.py b/buildscripts/resmokelib/testing/testcases/server_selection_json_test.py index b32c9dede9f..65dc5a49e39 100644 --- a/buildscripts/resmokelib/testing/testcases/server_selection_json_test.py +++ b/buildscripts/resmokelib/testing/testcases/server_selection_json_test.py @@ -14,7 +14,6 @@ class ServerSelectionJsonTestCase(interface.ProcessTestCase): """Server Selection JSON test case.""" REGISTERED_NAME = "server_selection_json_test" - EXECUTABLE_BUILD_PATH = "build/**/mongo/client/sdam/server_selection_json_test" TEST_DIR = os.path.normpath("src/mongo/client/sdam/json_tests/server_selection_tests") def __init__(self, logger, json_test_file, program_options=None): @@ -26,22 +25,15 @@ class ServerSelectionJsonTestCase(interface.ProcessTestCase): self.json_test_file = os.path.normpath(json_test_file) self.program_options = utils.default_if_none(program_options, {}).copy() - def _find_executable(self): - if config.INSTALL_DIR is not None: - binary = os.path.join(config.INSTALL_DIR, "server_selection_json_test") - if os.name == "nt": - binary += ".exe" + def _find_executable(self): # pylint: disable=no-self-use + binary = os.path.join(config.INSTALL_DIR, "server_selection_json_test") + if os.name == "nt": + binary += ".exe" - if os.path.isfile(binary): - return binary - - execs = globstar.glob(self.EXECUTABLE_BUILD_PATH + '.exe') - if not execs: - execs = globstar.glob(self.EXECUTABLE_BUILD_PATH) - if len(execs) != 1: + if not os.path.isfile(binary): raise errors.StopExecution( - "There must be a single server_selection_json_test binary in {}".format(execs)) - return execs[0] + f"Failed to locate server_selection_json_test binary at {binary}") + return binary def _make_process(self): command_line = [self.program_executable] diff --git a/buildscripts/tests/test_burn_in_tags.py b/buildscripts/tests/test_burn_in_tags.py index 023a47c3eb1..ec53a02d161 100644 --- a/buildscripts/tests/test_burn_in_tags.py +++ b/buildscripts/tests/test_burn_in_tags.py @@ -100,7 +100,7 @@ class TestGenerateEvgTasks(unittest.TestCase): evergreen_api = MagicMock() repo = MagicMock(working_dir=os.getcwd()) under_test._generate_evg_tasks(evergreen_api, shrub_config, expansions_file_data, - buildvariant_map, [repo], evg_conf_mock) + buildvariant_map, [repo], evg_conf_mock, 'install-dir/bin') self.assertEqual(shrub_config.as_dict(), EMPTY_PROJECT) @@ -131,7 +131,7 @@ class TestGenerateEvgTasks(unittest.TestCase): MagicMock(test_file="dir/test2.js", avg_duration_pass=10) ] under_test._generate_evg_tasks(evergreen_api, shrub_config, expansions_file_data, - buildvariant_map, [repo], evg_conf_mock) + buildvariant_map, [repo], evg_conf_mock, 'install-dir/bin') generated_config = shrub_config.as_dict() self.assertEqual(len(generated_config["buildvariants"]), 2) @@ -207,7 +207,8 @@ class TestAcceptance(unittest.TestCase): create_evg_build_variant_map_mock.return_value = CREATE_EVG_BUILD_VARIANT_MAP - under_test.burn_in(EXPANSIONS_FILE_DATA, evg_conf_mock, MagicMock(), repos) + under_test.burn_in(EXPANSIONS_FILE_DATA, evg_conf_mock, MagicMock(), repos, + 'install_dir/bin') write_to_file_mock.assert_called_once() shrub_config = write_to_file_mock.call_args[0][2] @@ -236,7 +237,7 @@ class TestAcceptance(unittest.TestCase): 'jstests/aggregation/accumulators/accumulator_js.js' } - under_test.burn_in(EXPANSIONS_FILE_DATA, evg_conf, MagicMock(), repos) + under_test.burn_in(EXPANSIONS_FILE_DATA, evg_conf, MagicMock(), repos, 'install_dir/bin') write_to_file_mock.assert_called_once() written_config = write_to_file_mock.call_args[0][2] diff --git a/buildscripts/tests/test_burn_in_tests.py b/buildscripts/tests/test_burn_in_tests.py index 4610332dfe6..c51f8c60ecf 100644 --- a/buildscripts/tests/test_burn_in_tests.py +++ b/buildscripts/tests/test_burn_in_tests.py @@ -497,7 +497,7 @@ class TestCreateTestsByTask(unittest.TestCase): evg_conf_mock.get_variant.return_value = None with self.assertRaises(ValueError): - under_test.create_tests_by_task(variant, evg_conf_mock, set()) + under_test.create_tests_by_task(variant, evg_conf_mock, set(), "install-dir/bin") class TestLocalFileChangeDetector(unittest.TestCase): diff --git a/buildscripts/tests/test_evergreen_burn_in_tests.py b/buildscripts/tests/test_evergreen_burn_in_tests.py index 8b13189fe81..ee77ced2579 100644 --- a/buildscripts/tests/test_evergreen_burn_in_tests.py +++ b/buildscripts/tests/test_evergreen_burn_in_tests.py @@ -79,7 +79,7 @@ class TestAcceptance(unittest.TestCase): mock_evg_api = MagicMock() under_test.burn_in("task_id", variant, gen_config, repeat_config, mock_evg_api, - mock_evg_conf, repos, "testfile.json") + mock_evg_conf, repos, "testfile.json", "install-dir/bin") write_json_mock.assert_called_once() written_config = json.loads(write_json_mock.call_args[0][1]) @@ -110,7 +110,7 @@ class TestAcceptance(unittest.TestCase): mock_evg_api = MagicMock() under_test.burn_in("task_id", variant, gen_config, repeat_config, mock_evg_api, - mock_evg_conf, repos, "testfile.json") + mock_evg_conf, repos, "testfile.json", 'install-dir/bin') write_json_mock.assert_called_once() written_config = json.loads(write_json_mock.call_args[0][1]) diff --git a/docs/testing/README.md b/docs/testing/README.md new file mode 100644 index 00000000000..8c76472c334 --- /dev/null +++ b/docs/testing/README.md @@ -0,0 +1,36 @@ +# Testing + +Most tests for MongoDB are run through resmoke, our test runner and orchestration tool. +The entry point for resmoke can be found at `buildscripts/resmoke.py` + +## run + +The run subcommand can run suites (list of tests and the MongoDB topology and +configuration to run them against), and explicitly named test files. + +A single suite can be specified using the `--suite` flag, and multiple suites +can be specified by providing a comma separated list to the `--suites` flag. + +Additional parameters for the run subcommand can be found on the help page, +accessible by running `buildscripts/resmoke.py run --help` + +Additional documentation on our suite configuration can be found on the +[Suites configuration file page](../suites.md) + +### Testable Installations (`--installDir`) + +resmoke can run tests against any testable installation of MongoDB (such +as ASAN, Debug, Release). When possible, resmoke will automatically locate and +run with a locally built copy of MongoDB Server, so long as that build was +installed to a subdirectory of the root of the git repository, and there is +exactly one build. In other situations, the `--installDir` flag, passed to run +subcommand, can be used to indicate the location of the mongod/mongos binaries. + +As an alternative, you may instead prefer to use the resmoke.py wrapper script +located in the same directory as the mongod binary, which will automatically +set `installDir` for you. + +Note that this wrapper is unavailable in packaged installations of MongoDB +Server, such as those provided by Homebrew, and other package managers. If you +would like to run tests against a packaged installation, you must explicitly +pass `--installDir` to resmoke.py diff --git a/etc/evergreen_yml_components/definitions.yml b/etc/evergreen_yml_components/definitions.yml index a0ab51f47ea..79927a7c975 100644 --- a/etc/evergreen_yml_components/definitions.yml +++ b/etc/evergreen_yml_components/definitions.yml @@ -2425,6 +2425,7 @@ tasks: - func: "run tests" vars: suite: unittests + install_dir: build/install/bin ## run_unittests with UndoDB live-record ## #- name: run_unittests_with_recording @@ -2452,6 +2453,7 @@ tasks: # # Start fewer jobs since there's a constant amount of overhead of starting # # live-record for each job. # resmoke_jobs_factor: 0.3 +# install_dir: build/install/bin ##compile_and_archive_libfuzzertests - build libfuzzertests ## @@ -2492,6 +2494,7 @@ tasks: vars: targets: install-sdam-json-test compiling_for_test: true + install_dir: build/install/bin - func: "run tests" vars: suite: sdam_json_test @@ -2503,6 +2506,7 @@ tasks: vars: targets: install-server-selection-json-test compiling_for_test: true + install_dir: build/install/bin - func: "run tests" ## compile_dbtest ## diff --git a/evergreen/burn_in_tests.sh b/evergreen/burn_in_tests.sh index 23ac58851d4..5f6124a5f47 100755 --- a/evergreen/burn_in_tests.sh +++ b/evergreen/burn_in_tests.sh @@ -17,4 +17,4 @@ if [ -n "${burn_in_tests_build_variant}" ]; then fi burn_in_args="$burn_in_args --repeat-tests-min=2 --repeat-tests-max=1000 --repeat-tests-secs=600" # Evergreen executable is in $HOME. -PATH="$PATH:$HOME" eval $python buildscripts/evergreen_burn_in_tests.py --project=${project} $build_variant_opts --distro=${distro_id} --generate-tasks-file=burn_in_tests_gen.json --task_id ${task_id} $burn_in_args --verbose +PATH="$PATH:$HOME" eval $python buildscripts/evergreen_burn_in_tests.py --project=${project} $build_variant_opts --distro=${distro_id} --generate-tasks-file=burn_in_tests_gen.json --task_id ${task_id} $burn_in_args --verbose --install-dir "${install_dir}" diff --git a/evergreen/burn_in_tests_generate.sh b/evergreen/burn_in_tests_generate.sh index 639a18e0045..6e2499d5e39 100644 --- a/evergreen/burn_in_tests_generate.sh +++ b/evergreen/burn_in_tests_generate.sh @@ -9,4 +9,4 @@ set -o verbose activate_venv # Evergreen executable is in $HOME. -PATH=$PATH:$HOME $python buildscripts/burn_in_tags.py --expansion-file ../expansions.yml +PATH=$PATH:$HOME $python buildscripts/burn_in_tags.py --expansion-file ../expansions.yml --install-dir "${install_dir}" diff --git a/evergreen/check_idl_compat.sh b/evergreen/check_idl_compat.sh index 0fb71452e9c..afa9c3f3239 100755 --- a/evergreen/check_idl_compat.sh +++ b/evergreen/check_idl_compat.sh @@ -7,7 +7,7 @@ set -o errexit set -o verbose activate_venv -$python buildscripts/idl/check_stable_api_commands_have_idl_definitions.py -v --include src --include src/mongo/db/modules/enterprise/src --installDir dist-test/bin 1 +$python buildscripts/idl/check_stable_api_commands_have_idl_definitions.py -v --install-dir dist-test/bin --include src --include src/mongo/db/modules/enterprise/src 1 $python buildscripts/idl/checkout_idl_files_from_past_releases.py -v idls find idls -maxdepth 1 -mindepth 1 -type d | while read dir; do echo "Performing idl check compatibility with release: $dir:" diff --git a/evergreen/functions/task_timeout_determine.sh b/evergreen/functions/task_timeout_determine.sh index 93c7d2baa36..79d393bb964 100644 --- a/evergreen/functions/task_timeout_determine.sh +++ b/evergreen/functions/task_timeout_determine.sh @@ -27,6 +27,7 @@ fi activate_venv PATH=$PATH:$HOME:/ $python buildscripts/evergreen_task_timeout.py $timeout_factor \ + --install-dir "${install_dir}" \ --task-name ${task_name} \ --suite-name ${suite_name} \ --build-variant $build_variant_for_timeout \ diff --git a/site_scons/site_tools/auto_archive.py b/site_scons/site_tools/auto_archive.py index f4c7201b33e..b3c9ddd99a4 100644 --- a/site_scons/site_tools/auto_archive.py +++ b/site_scons/site_tools/auto_archive.py @@ -133,7 +133,14 @@ def collect_transitive_files(env, entry): # anyway. stack.extend(env.GetTransitivelyInstalledFiles(s)) - return sorted(files) + # Setting the AIB_NO_ARCHIVE attribute to True prevents outputs from an + # AutoInstall builder from being included into archives produced by this + # tool + # Usage: + # node = env.AutoInstall(...) + # setattr(node[0].attributes, 'AIB_NO_ARCHIVE', True) + # TODO SERVER-61013 Update documentation once AutoInstall is a real builder + return sorted(f for f in files if not getattr(f.attributes, 'AIB_NO_ARCHIVE', False)) def auto_archive_gen(first_env, make_archive_script, pkg_fmt): diff --git a/site_scons/site_tools/ninja.py b/site_scons/site_tools/ninja.py index 5826be827f4..c755e12b469 100644 --- a/site_scons/site_tools/ninja.py +++ b/site_scons/site_tools/ninja.py @@ -660,6 +660,7 @@ class NinjaState: kwargs['pool'] = 'local_pool' ninja.rule(rule, **kwargs) + # TODO SERVER-64664 generated_source_files = sorted({ output # First find builds which have header files in their outputs. diff --git a/src/mongo/SConscript b/src/mongo/SConscript index 82e6e06b67c..8696da79339 100644 --- a/src/mongo/SConscript +++ b/src/mongo/SConscript @@ -35,6 +35,7 @@ env.SConscript( 'installer', 'logv2', 'platform', + 'resmoke', 'rpc', 's', 'scripting', diff --git a/src/mongo/resmoke/SConscript b/src/mongo/resmoke/SConscript new file mode 100644 index 00000000000..fb80ab1a713 --- /dev/null +++ b/src/mongo/resmoke/SConscript @@ -0,0 +1,28 @@ +# -*- mode: python -*- +import os +import SCons +Import('env') + +env = env.Clone() + +install_dir = env.Dir('$DESTDIR/$PREFIX_BINDIR').path.replace("\\", r"\\") +resmoke_py = env.Substfile( + target="resmoke.py", + source='resmoke.py.in', + SUBST_DICT = { + '@install_dir@': install_dir, + } +) +resmoke_py_install = env.AutoInstall( + '$PREFIX_BINDIR', + source=resmoke_py, + AIB_COMPONENT='common', + AIB_ROLE='runtime', +) +# TODO SERVER-61013: We shouldn't have to setattr this once AutoInstall is a +# real builder +setattr(resmoke_py_install[0].attributes, 'AIB_NO_ARCHIVE', True) +env.AddPostAction( + resmoke_py_install, + action=SCons.Defaults.Chmod('$TARGET', "u+x"), +) diff --git a/src/mongo/resmoke/resmoke.py.in b/src/mongo/resmoke/resmoke.py.in new file mode 100644 index 00000000000..8b5d4106480 --- /dev/null +++ b/src/mongo/resmoke/resmoke.py.in @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 +import importlib.util +import os +import os.path +import argparse +import sys +import shlex +import textwrap + +BUILD_BIN_DIR=r"@install_dir@" + +class _SilentArgumentParser(argparse.ArgumentParser): + """ArgumentParser variant that silently swallows errors.""" + def error(self, message): + pass + +# We do not want people to use this wrapper and provide --installDir, +# especially if the user tries to supply the wrong installDir. Prevent that +# by parsing the command line flags and make sure that --installDir is not +# passed in. (only the run command supports this flag) +try: + parser = _SilentArgumentParser(add_help=False) + + subparsers = parser.add_subparsers(dest="cmd") + run = subparsers.add_parser("run") + run.add_argument("--installDir") + args, _ = parser.parse_known_args() + if "installDir" in args and args.installDir is not None: + err = textwrap.dedent(f"""\ +Argument '--installDir' passed to resmoke wrapper script, but this action can +have unforeseen consequences. Either remove --installDir, or call resmoke as +`buildscripts/resmoke.py run --installDir={shlex.quote(args.installDir)}`""") + raise RuntimeError(err) + + run_cmd_called = args.cmd == "run" +except RuntimeError: + raise +except: + run_cmd_called = False + +# If the run subcommand is being used, inject installDir +if run_cmd_called: + i = sys.argv.index("run") + sys.argv.insert(i+1, "--installDir") + sys.argv.insert(i+2, BUILD_BIN_DIR) + +path_to_resmoke_py = os.path.join("buildscripts", "resmoke.py") + +# import and run the cli +spec = importlib.util.spec_from_file_location("__main__", path_to_resmoke_py) +resmoke = importlib.util.module_from_spec(spec) +resmoke.__package__ = None +spec.loader.exec_module(resmoke) + +# -*- mode: python -*- |