diff options
author | David Bradford <david.bradford@mongodb.com> | 2019-07-08 18:12:47 -0400 |
---|---|---|
committer | David Bradford <david.bradford@mongodb.com> | 2019-07-08 18:12:47 -0400 |
commit | e5d7c73e7eb9f3bfe70925dd3154ca348b1ee285 (patch) | |
tree | 2ac6b928e36ec6958d9c5ffa7c44dd9b27af20d1 | |
parent | c6468f7d3ac783b4bcdeb6744fc976c8ecee2969 (diff) | |
download | mongo-e5d7c73e7eb9f3bfe70925dd3154ca348b1ee285.tar.gz |
SERVER-41940: Use evergreen.py in burn_in_tests
-rw-r--r-- | buildscripts/burn_in_tags.py | 2 | ||||
-rw-r--r-- | buildscripts/burn_in_tests.py | 102 | ||||
-rw-r--r-- | buildscripts/tests/test_burn_in_tests.py | 143 | ||||
-rw-r--r-- | buildscripts/util/read_config.py | 2 |
4 files changed, 88 insertions, 161 deletions
diff --git a/buildscripts/burn_in_tags.py b/buildscripts/burn_in_tags.py index 15b00e5c1c6..9b595ecd624 100644 --- a/buildscripts/burn_in_tags.py +++ b/buildscripts/burn_in_tags.py @@ -125,7 +125,7 @@ def _generate_evg_tasks(evergreen_api, shrub_config, expansions_file_data, build """ for buildvariant, run_buildvariant in buildvariant_map.items(): config_options = _get_config_options(expansions_file_data, buildvariant, run_buildvariant) - tests_by_task = create_tests_by_task(config_options) + tests_by_task = create_tests_by_task(config_options, evergreen_api) if tests_by_task: _generate_evg_buildvariant(shrub_config, buildvariant, run_buildvariant) create_generate_tasks_config(evergreen_api, shrub_config, config_options, tests_by_task, diff --git a/buildscripts/burn_in_tests.py b/buildscripts/burn_in_tests.py index 9f098cf0ff2..c3dd28f1a95 100644 --- a/buildscripts/burn_in_tests.py +++ b/buildscripts/burn_in_tests.py @@ -7,10 +7,8 @@ import json import optparse import os.path import subprocess -import re import shlex import sys -import urllib.parse import datetime import logging @@ -36,14 +34,11 @@ if __name__ == "__main__" and __package__ is None: from buildscripts import git from buildscripts import resmokelib from buildscripts.ciconfig import evergreen -from buildscripts.client import evergreen as evergreen_client from buildscripts.util import teststats # pylint: enable=wrong-import-position LOGGER = logging.getLogger(__name__) -API_REST_PREFIX = "/rest/v1/" -API_SERVER_DEFAULT = "https://evergreen.mongodb.com" AVG_TEST_RUNTIME_ANALYSIS_DAYS = 14 AVG_TEST_TIME_MULTIPLIER = 3 CONFIG_FILE = "../src/.evergreen.yml" @@ -185,48 +180,46 @@ def validate_options(parser, options): check_variant(options.run_buildvariant, parser) -def find_last_activated_task(revisions, variant, branch_name): - """Get the git hash of the most recently activated build before this one.""" - - project = "mongodb-mongo-" + branch_name - build_prefix = "mongodb_mongo_" + branch_name + "_" + variant.replace("-", "_") - - evg_cfg = evergreen_client.read_evg_config() - if evg_cfg is not None and "api_server_host" in evg_cfg: - api_server = "{url.scheme}://{url.netloc}".format( - url=urllib.parse.urlparse(evg_cfg["api_server_host"])) - else: - api_server = API_SERVER_DEFAULT +def find_last_activated_task(revisions, variant, project, evg_api): + """ + Search the given list of revisions for the first build that was activated in evergreen. - api_prefix = api_server + API_REST_PREFIX + :param revisions: List of revisions to search. + :param variant: Build variant to query for. + :param project: Project being run against. + :param evg_api: Evergreen api. + :return: First revision from list that has been activated. + """ + prefix = project.replace("-", "_") for githash in revisions: - url = "{}projects/{}/revisions/{}".format(api_prefix, project, githash) - response = requests.get(url) - revision_data = response.json() + version_id = f"{prefix}_{githash}" + version = evg_api.version_by_id(version_id) - try: - for build in revision_data["builds"]: - if build.startswith(build_prefix): - url = "{}builds/{}".format(api_prefix, build) - build_resp = requests.get(url) - build_data = build_resp.json() - if build_data["activated"]: - return build_data["revision"] - except: # pylint: disable=bare-except - # Sometimes build data is incomplete, as was the related build. - pass + build = version.build_by_variant(variant) + if build.activated: + return githash return None -def find_changed_tests( # pylint: disable=too-many-locals - branch_name, base_commit, max_revisions, buildvariant, check_evergreen): - """Find the changed tests. +def find_changed_tests( # pylint: disable=too-many-locals,too-many-arguments + branch_name, base_commit, max_revisions, buildvariant, project, check_evergreen, evg_api): + """ + Find the changed tests. Use git to find which files have changed in this patch. TODO: This should be expanded to search for enterprise modules. The returned file paths are in normalized form (see os.path.normpath(path)). + + :param branch_name: Branch being run against. + :param base_commit: Commit changes are made on top of. + :param max_revisions: Max number of revisions to search through. + :param buildvariant: Build variant burn is being run on. + :param project: Project that is being run on. + :param check_evergreen: Should evergreen be checked for an activated build. + :param evg_api: Evergreen api. + :returns: List of changed tests. """ changed_tests = [] @@ -242,7 +235,7 @@ def find_changed_tests( # pylint: disable=too-many-locals # previous commit when trying to find the most recent preceding commit that has been # activated. revs_to_check = repo.git_rev_list([base_commit, "--max-count=200", "--skip=1"]).splitlines() - last_activated = find_last_activated_task(revs_to_check, buildvariant, branch_name) + last_activated = find_last_activated_task(revs_to_check, buildvariant, project, evg_api) if last_activated is None: # When the current commit is the first time 'buildvariant' has run, there won't be a # commit among 'revs_to_check' that's been activated in Evergreen. We handle this by @@ -289,7 +282,7 @@ def find_excludes(selector_file): return ([], [], []) with open(selector_file, "r") as fstream: - yml = yaml.load(fstream) + yml = yaml.safe_load(fstream) try: js_test = yml["selector"]["js_test"] @@ -543,11 +536,11 @@ def _generate_timeouts(options, commands, test, task_avg_test_runtime_stats): commands.append(cmd_timeout.validate().resolve()) -def _get_task_runtime_history(evergreen_api, project, task, variant): +def _get_task_runtime_history(evg_api, project, task, variant): """ Fetch historical average runtime for all tests in a task from Evergreen API. - :param evergreen_api: Evergreen API. + :param evg_api: Evergreen API. :param project: Project name. :param task: Task name. :param variant: Variant name. @@ -556,10 +549,10 @@ def _get_task_runtime_history(evergreen_api, project, task, variant): try: end_date = datetime.datetime.utcnow().replace(microsecond=0) start_date = end_date - datetime.timedelta(days=AVG_TEST_RUNTIME_ANALYSIS_DAYS) - data = evergreen_api.test_stats_by_project( - project, after_date=start_date.strftime("%Y-%m-%d"), - before_date=end_date.strftime("%Y-%m-%d"), tasks=[task], variants=[variant], - group_by="test", group_num_days=AVG_TEST_RUNTIME_ANALYSIS_DAYS) + data = evg_api.test_stats_by_project(project, after_date=start_date.strftime("%Y-%m-%d"), + before_date=end_date.strftime("%Y-%m-%d"), + tasks=[task], variants=[variant], group_by="test", + group_num_days=AVG_TEST_RUNTIME_ANALYSIS_DAYS) test_runtimes = teststats.TestStats(data).get_tests_runtimes() LOGGER.debug("Test_runtime data parsed from Evergreen history: %s", test_runtimes) return test_runtimes @@ -572,8 +565,7 @@ def _get_task_runtime_history(evergreen_api, project, task, variant): raise -def create_generate_tasks_config(evergreen_api, evg_config, options, tests_by_task, - include_gen_task): +def create_generate_tasks_config(evg_api, evg_config, options, tests_by_task, include_gen_task): """Create the config for the Evergreen generate.tasks file.""" # pylint: disable=too-many-locals task_specs = [] @@ -582,8 +574,8 @@ def create_generate_tasks_config(evergreen_api, evg_config, options, tests_by_ta task_names.append(BURN_IN_TESTS_GEN_TASK) for task in sorted(tests_by_task): multiversion_path = tests_by_task[task].get("use_multiversion") - task_avg_test_runtime_stats = _get_task_runtime_history(evergreen_api, options.project, - task, options.buildvariant) + task_avg_test_runtime_stats = _get_task_runtime_history(evg_api, options.project, task, + options.buildvariant) for test_num, test in enumerate(tests_by_task[task]["tests"]): sub_task_name = _sub_task_name(options, task, test_num) task_names.append(sub_task_name) @@ -612,18 +604,20 @@ def create_generate_tasks_config(evergreen_api, evg_config, options, tests_by_ta return evg_config -def create_tests_by_task(options): +def create_tests_by_task(options, evg_api): """ Create a list of tests by task. :param options: Options. + :param evg_api: Evergreen api. :return: Tests by task """ # Parse the Evergreen project configuration file. evergreen_conf = evergreen.parse_evergreen_file(EVERGREEN_FILE) changed_tests = find_changed_tests(options.branch, options.base_commit, options.max_revisions, - options.buildvariant, options.check_evergreen) + options.buildvariant, options.project, + options.check_evergreen, evg_api) exclude_suites, exclude_tasks, exclude_tests = find_excludes(SELECTOR_FILE) changed_tests = filter_tests(changed_tests, exclude_tests) @@ -640,11 +634,11 @@ def create_tests_by_task(options): return tests_by_task -def create_generate_tasks_file(evergreen_api, options, tests_by_task): +def create_generate_tasks_file(evg_api, options, tests_by_task): """Create the Evergreen generate.tasks file.""" evg_config = Configuration() - evg_config = create_generate_tasks_config(evergreen_api, evg_config, options, tests_by_task, + evg_config = create_generate_tasks_config(evg_api, evg_config, options, tests_by_task, include_gen_task=True) _write_json_file(evg_config.to_map(), options.generate_tasks_file) @@ -676,7 +670,7 @@ def run_tests(no_exec, tests_by_task, resmoke_cmd, report_file): _write_json_file(test_results, report_file) -def main(evergreen_api): +def main(evg_api): """Execute Main program.""" logging.basicConfig( @@ -700,13 +694,13 @@ def main(evergreen_api): # Run the executor finder. else: - tests_by_task = create_tests_by_task(options) + tests_by_task = create_tests_by_task(options, evg_api) if options.test_list_outfile: _write_json_file(tests_by_task, options.test_list_outfile) if options.generate_tasks_file: - create_generate_tasks_file(evergreen_api, options, tests_by_task) + create_generate_tasks_file(evg_api, options, tests_by_task) else: run_tests(options.no_exec, tests_by_task, resmoke_cmd, options.report_file) diff --git a/buildscripts/tests/test_burn_in_tests.py b/buildscripts/tests/test_burn_in_tests.py index ac0f8fe44da..90b83758675 100644 --- a/buildscripts/tests/test_burn_in_tests.py +++ b/buildscripts/tests/test_burn_in_tests.py @@ -870,94 +870,47 @@ class RunTests(unittest.TestCase): class FindLastActivated(unittest.TestCase): - - REVISION_BUILDS = { - "rev1": { - "not_mongodb_mongo_master_variant1_build1": {"activated": False}, # force line break - "mongodb_mongo_unmaster_variant_build1": {"activated": True}, - "mongodb_mongo_master_variant1_build1": {"activated": True}, - "mongodb_mongo_master_variant2_build1": {"activated": False}, - "mongodb_mongo_master_variant3_build1": {"activated": False} - }, - "rev2": { - "not_mongodb_mongo_master_variant1_build1": {"activated": True}, - "mongodb_mongo_unmaster_variant_build1": {"activated": True}, - "mongodb_mongo_master_variant1_build1": {"activated": True}, - "mongodb_mongo_master_variant2_build1": {"activated": False} - }, - "rev3": { - "not_mongodb_mongo_master_variant1_build1": {"activated": True}, - "mongodb_mongo_unmaster_variant_build1": {"activated": True}, - "mongodb_mongo_master_variant1_build1": {"activated": True}, - "mongodb_mongo_master_variant2_build1": {"activated": False}, - }, - "rev4": { - "not_mongodb_mongo_master_variant1_build1": {"activated": True}, - "mongodb_mongo_unmaster_variant_build1": {"activated": True}, - "mongodb_mongo_master_variant1_build1": {"activated": True}, # force line break - "mongodb_mongo_master_variant2_build1": {"activated": False}, - "mongodb_mongo_master_variant3_build1": {"activated": True} - }, - } - - @staticmethod - def builds_url(build): - """Return build URL.""" - return "{}{}builds/{}".format(burn_in.API_SERVER_DEFAULT, burn_in.API_REST_PREFIX, build) - - @staticmethod - def revisions_url(project, revision): - """Return revisions URL.""" - return "{}{}projects/{}/revisions/{}".format(burn_in.API_SERVER_DEFAULT, - burn_in.API_REST_PREFIX, project, revision) - - @staticmethod - def load_urls(request, project, revision_builds): - """Store request in URLs to support REST APIs.""" - - for revision in revision_builds: - builds = revision_builds[revision] - # The 'revisions' endpoint contains the list of builds. - url = FindLastActivated.revisions_url(project, revision) - build_list = [] - for build in builds: - build_list.append("{}_{}".format(build, revision)) - build_data = {"builds": build_list} - request.put(url, None, build_data) - - for build in builds: - # The 'builds' endpoint contains the activated & revision field. - url = FindLastActivated.builds_url("{}_{}".format(build, revision)) - build_data = builds[build] - build_data["revision"] = revision - request.put(url, None, build_data) - - def _test_find_last_activated_task(self, branch, variant, revision, - revisions=REVISION_BUILDS.keys()): - with patch(BURN_IN + ".requests", MockRequests()),\ - patch(EVG_CLIENT + ".read_evg_config", return_value=None): - self.load_urls(burn_in.requests, "mongodb-mongo-master", self.REVISION_BUILDS) - last_revision = burn_in.find_last_activated_task(revisions, variant, branch) - self.assertEqual(last_revision, revision) - def test_find_last_activated_task_first_rev(self): - self._test_find_last_activated_task("master", "variant1", "rev1") + rev_list = ["rev1", "rev2", "rev3"] + variant = "build_variant_0" + branch = "master" + evg_api = MagicMock() + + revision = burn_in.find_last_activated_task(rev_list, variant, branch, evg_api) + self.assertEqual(revision, rev_list[0]) def test_find_last_activated_task_last_rev(self): - self._test_find_last_activated_task("master", "variant3", "rev4") + rev_list = ["rev1", "rev2", "rev3"] + variant = "build_variant_0" + branch = "master" + evg_api = MagicMock() + evg_api.version_by_id.return_value.build_by_variant.side_effect = [ + MagicMock(activated=False), + MagicMock(activated=False), + MagicMock(activated=True), + ] - def test_find_last_activated_task_no_rev(self): - self._test_find_last_activated_task("master", "variant2", None) + revision = burn_in.find_last_activated_task(rev_list, variant, branch, evg_api) + self.assertEqual(revision, rev_list[2]) - def test_find_last_activated_task_no_variant(self): - self._test_find_last_activated_task("master", "novariant", None) + def test_find_last_activated_task_no_rev(self): + rev_list = ["rev1", "rev2", "rev3"] + variant = "build_variant_0" + branch = "master" + evg_api = MagicMock() + evg_api.version_by_id.return_value.build_by_variant.return_value.activated = False - def test_find_last_activated_task_no_branch(self): - with self.assertRaises(AttributeError): - self._test_find_last_activated_task("nobranch", "variant2", None) + revision = burn_in.find_last_activated_task(rev_list, variant, branch, evg_api) + self.assertIsNone(revision) def test_find_last_activated_norevisions(self): - self._test_find_last_activated_task("master", "novariant", None, []) + rev_list = [] + variant = "build_variant_0" + branch = "master" + evg_api = MagicMock() + + revision = burn_in.find_last_activated_task(rev_list, variant, branch, evg_api) + self.assertIsNone(revision) MEMBERS_MAP = { @@ -1175,6 +1128,7 @@ class FindChangedTests(unittest.TestCase): self, commit, max_revisions, variant, check_evg, rev1, rev2, rev_diff, untracked_files, last_activated_task=None): branch = "master" + project = "project" # pylint: disable=attribute-defined-outside-init self.rev1 = rev1 self.rev2 = rev2 @@ -1187,11 +1141,12 @@ class FindChangedTests(unittest.TestCase): self.expected_changed_tests += rev_diff.get(commit, []) self.expected_changed_tests += untracked_files # pylint: enable=attribute-defined-outside-init - with patch(EVG_CLIENT + ".read_evg_config", return_value=None),\ - patch(GIT + ".Repository", self._mock_git_repository),\ + evg_api = MagicMock() + with patch(GIT + ".Repository", self._mock_git_repository),\ patch("os.path.isfile", return_value=True),\ patch(BURN_IN + ".find_last_activated_task", return_value=last_activated_task): - return burn_in.find_changed_tests(branch, commit, max_revisions, variant, check_evg) + return burn_in.find_changed_tests(branch, commit, max_revisions, variant, project, + check_evg, evg_api) def test_find_changed_tests(self): commit = "3" @@ -1350,25 +1305,3 @@ class MockGitRepository(object): modified_files = [" M {}".format(untracked) for untracked in diff_list] untracked_files = ["?? {}".format(untracked) for untracked in self.untracked_files] return "\n".join(modified_files + untracked_files) - - -class MockResponse(object): - def __init__(self, response, json_data): - self.response = response - self.json_data = json_data - - def json(self): - return self.json_data - - -class MockRequests(object): - def __init__(self): - self.responses = {} - - def put(self, url, response, json_data): - self.responses[url] = MockResponse(response, json_data) - - def get(self, url): - if url in self.responses: - return self.responses[url] - return None diff --git a/buildscripts/util/read_config.py b/buildscripts/util/read_config.py index aad2b9a150c..a937908670a 100644 --- a/buildscripts/util/read_config.py +++ b/buildscripts/util/read_config.py @@ -39,6 +39,6 @@ def read_config_file(config_file): config_file_data = {} if config_file: with open(config_file) as file_handle: - config_file_data = yaml.load(file_handle) + config_file_data = yaml.safe_load(file_handle) return config_file_data |