diff options
author | David Bradford <david.bradford@mongodb.com> | 2021-12-16 11:22:55 -0500 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-12-16 22:27:28 +0000 |
commit | 00b7518783c6a576c0996fc27f0bb85f13c78fc3 (patch) | |
tree | 782809c063f8b43de15e4c72747c9d0ddbac3eac | |
parent | 7cb100e649c4e429719b07c9419a0b254ece3b79 (diff) | |
download | mongo-00b7518783c6a576c0996fc27f0bb85f13c78fc3.tar.gz |
SERVER-62097: Generated tasks should inherit timeouts specified by build variant
8 files changed, 66 insertions, 7 deletions
diff --git a/buildscripts/evergreen_gen_build_variant.py b/buildscripts/evergreen_gen_build_variant.py index bd6ceffc98d..758e2f2ab0b 100644 --- a/buildscripts/evergreen_gen_build_variant.py +++ b/buildscripts/evergreen_gen_build_variant.py @@ -39,6 +39,7 @@ class EvgExpansions(BaseModel): build_id: Build ID being run under. build_variant: Build variant being generated. + exec_timeout_secs: Seconds to wait before considering a task timed out. gen_task_gran: Granularity of how tasks are being generated. is_patch: Whether generation is part of a patch build. project: Evergreen project being run under. @@ -50,10 +51,12 @@ class EvgExpansions(BaseModel): task_name: Name of task running. target_resmoke_time: Target time of generated sub-suites. task_id: ID of task being run under. + timeout_secs: Seconds to wait with no output before considering a task timed out. """ build_id: str build_variant: str + exec_timeout_secs: Optional[int] = None is_patch: Optional[bool] project: str max_tests_per_suite: Optional[int] = 100 @@ -64,6 +67,7 @@ class EvgExpansions(BaseModel): task_name: str target_resmoke_time: Optional[int] = None task_id: str + timeout_secs: Optional[int] = None @classmethod def from_yaml_file(cls, path: str) -> "EvgExpansions": @@ -110,6 +114,8 @@ class EvgExpansions(BaseModel): is_patch=self.is_patch, generated_config_dir=GENERATED_CONFIG_DIR, use_default_timeouts=False, + timeout_secs=self.timeout_secs, + exec_timeout_secs=self.exec_timeout_secs, ) def config_location(self) -> str: diff --git a/buildscripts/selected_tests.py b/buildscripts/selected_tests.py index e772eef9eed..b1df4686e22 100644 --- a/buildscripts/selected_tests.py +++ b/buildscripts/selected_tests.py @@ -120,7 +120,8 @@ class EvgExpansions(BaseModel): """Build options needed to generate tasks.""" return GenTaskOptions(create_misc_suite=False, generated_config_dir=SELECTED_TESTS_CONFIG_DIR, is_patch=self.is_patch - or False, use_default_timeouts=False) + or False, use_default_timeouts=False, timeout_secs=None, + exec_timeout_secs=None) def build_suite_split_config(self, start_date: datetime, end_date: datetime) -> SuiteSplitConfig: diff --git a/buildscripts/task_generation/task_types/gentask_options.py b/buildscripts/task_generation/task_types/gentask_options.py index b78a7974979..d9616abfe96 100644 --- a/buildscripts/task_generation/task_types/gentask_options.py +++ b/buildscripts/task_generation/task_types/gentask_options.py @@ -2,6 +2,8 @@ import os from typing import NamedTuple, Optional, List +from buildscripts.patch_builds.task_generation import TimeoutInfo + class GenTaskOptions(NamedTuple): """ @@ -18,6 +20,8 @@ class GenTaskOptions(NamedTuple): is_patch: bool generated_config_dir: str use_default_timeouts: bool + timeout_secs: Optional[int] + exec_timeout_secs: Optional[int] def suite_location(self, suite_name: str) -> str: """ @@ -39,3 +43,9 @@ class GenTaskOptions(NamedTuple): # here, just use the forward slash; otherwise the path separator will be treated as # the escape character on Windows. return "/".join([self.generated_config_dir, base_file]) + + def build_defualt_timeout(self) -> TimeoutInfo: + """Generate a timeout command that can be used if historic timing info is missing.""" + if self.timeout_secs is not None or self.exec_timeout_secs is not None: + return TimeoutInfo.overridden(self.exec_timeout_secs, self.timeout_secs) + return TimeoutInfo.default_timeout() diff --git a/buildscripts/task_generation/task_types/resmoke_tasks.py b/buildscripts/task_generation/task_types/resmoke_tasks.py index 513d6da8c13..24710ffd88e 100644 --- a/buildscripts/task_generation/task_types/resmoke_tasks.py +++ b/buildscripts/task_generation/task_types/resmoke_tasks.py @@ -137,9 +137,12 @@ class ResmokeGenTaskService: suite_file=suite.suite_name, task_name=suite.task_name, params=params) - timeout_info = timeout_est.generate_timeout_cmd(self.gen_task_options.is_patch, - params.repeat_suites, - self.gen_task_options.use_default_timeouts) + if timeout_est.is_specified(): + timeout_info = timeout_est.generate_timeout_cmd( + self.gen_task_options.is_patch, params.repeat_suites, + self.gen_task_options.use_default_timeouts) + else: + timeout_info = self.gen_task_options.build_defualt_timeout() commands = [ timeout_info.cmd, diff --git a/buildscripts/task_generation/timeout.py b/buildscripts/task_generation/timeout.py index 67db6568d65..0143cd6e8b1 100644 --- a/buildscripts/task_generation/timeout.py +++ b/buildscripts/task_generation/timeout.py @@ -43,6 +43,10 @@ class TimeoutEstimate(NamedTuple): """Create an instance with no estimation data.""" return cls(max_test_runtime=None, expected_task_runtime=None) + def is_specified(self) -> bool: + """Determine if any specific timeout value has been specified.""" + return self.max_test_runtime is not None or self.expected_task_runtime is not None + def calculate_test_timeout(self, repeat_factor: int) -> Optional[int]: """ Calculate the timeout to use for tests. @@ -84,7 +88,7 @@ class TimeoutEstimate(NamedTuple): :return: Timeout info for the task. """ - if (self.max_test_runtime is None and self.expected_task_runtime is None) or use_default: + if not self.is_specified or use_default: return TimeoutInfo.default_timeout() test_timeout = self.calculate_test_timeout(repeat_factor) diff --git a/buildscripts/tests/task_generation/task_types/test_gentask_options.py b/buildscripts/tests/task_generation/task_types/test_gentask_options.py index 6d811e32577..657cf669457 100644 --- a/buildscripts/tests/task_generation/task_types/test_gentask_options.py +++ b/buildscripts/tests/task_generation/task_types/test_gentask_options.py @@ -13,6 +13,8 @@ def build_mock_gen_task_options(config_dir="."): is_patch=True, generated_config_dir=config_dir, use_default_timeouts=False, + timeout_secs=None, + exec_timeout_secs=None, ) diff --git a/buildscripts/tests/task_generation/task_types/test_resmoke_tasks.py b/buildscripts/tests/task_generation/task_types/test_resmoke_tasks.py index 9ea74bde214..bd62d2bd5c7 100644 --- a/buildscripts/tests/task_generation/task_types/test_resmoke_tasks.py +++ b/buildscripts/tests/task_generation/task_types/test_resmoke_tasks.py @@ -29,9 +29,10 @@ class TestHelperMethods(unittest.TestCase): self.assertEqual(False, under_test.string_contains_any_of_args(string, args)) -def build_mock_gen_options(use_default_timeouts=False): +def build_mock_gen_options(use_default_timeouts=False, timeout_secs=None, exec_timeout_secs=None): return GenTaskOptions(create_misc_suite=True, is_patch=True, generated_config_dir="tmpdir", - use_default_timeouts=use_default_timeouts) + use_default_timeouts=use_default_timeouts, timeout_secs=timeout_secs, + exec_timeout_secs=exec_timeout_secs) def build_mock_gen_params(repeat_suites=1, resmoke_args="resmoke args"): @@ -155,3 +156,20 @@ class TestGenerateTask(unittest.TestCase): for cmd in task.shrub_task.commands: cmd_dict = cmd.as_dict() self.assertNotEqual("timeout.update", cmd_dict.get("command")) + + def test_suites_without_enough_info_should_inherit_bv_timeouts_if_specified(self): + mock_gen_options = build_mock_gen_options(timeout_secs=300, exec_timeout_secs=150) + params = build_mock_gen_params() + suites = build_mock_suite(1, include_runtimes=False) + + resmoke_service = under_test.ResmokeGenTaskService(mock_gen_options) + tasks = resmoke_service.generate_tasks(suites, params) + + self.assertEqual(2, len(tasks)) + for resmoke_task in tasks: + task = resmoke_task.shrub_task + self.assertGreaterEqual(len(task.commands), 1) + timeout_cmd = task.commands[0] + self.assertEqual("timeout.update", timeout_cmd.command) + self.assertEqual(300, timeout_cmd.params["timeout_secs"]) + self.assertEqual(150, timeout_cmd.params["exec_timeout_secs"]) diff --git a/buildscripts/tests/task_generation/test_timeout.py b/buildscripts/tests/task_generation/test_timeout.py index 4e785897706..a7dd76464a8 100644 --- a/buildscripts/tests/task_generation/test_timeout.py +++ b/buildscripts/tests/task_generation/test_timeout.py @@ -32,6 +32,21 @@ class TimeoutEstimateTest(unittest.TestCase): with self.assertRaises(ValueError): timeout_est.generate_timeout_cmd(is_patch=True, repeat_factor=1) + def test_is_specified_should_return_true_when_a_test_runtime_is_specified(self): + timeout_est = under_test.TimeoutEstimate(max_test_runtime=3.14, expected_task_runtime=None) + + self.assertTrue(timeout_est.is_specified()) + + def test_is_specified_should_return_true_when_a_task_runtime_is_specified(self): + timeout_est = under_test.TimeoutEstimate(max_test_runtime=None, expected_task_runtime=3.14) + + self.assertTrue(timeout_est.is_specified()) + + def test_is_specified_should_return_false_when_no_data_is_specified(self): + timeout_est = under_test.TimeoutEstimate(max_test_runtime=None, expected_task_runtime=None) + + self.assertFalse(timeout_est.is_specified()) + class TestGenerateTimeoutCmd(unittest.TestCase): def test_evg_config_does_not_fails_if_test_timeout_too_high_on_mainline(self): |