summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--buildscripts/burn_in_tests.py2
-rw-r--r--buildscripts/ciconfig/evergreen.py36
-rw-r--r--buildscripts/tests/ciconfig/test_evergreen.py6
-rwxr-xr-xbuildscripts/update_test_lifecycle.py2
-rw-r--r--buildscripts/util/runcommand.py68
-rw-r--r--etc/evergreen.yml8
6 files changed, 109 insertions, 13 deletions
diff --git a/buildscripts/burn_in_tests.py b/buildscripts/burn_in_tests.py
index a1875c09995..11a51fd5afb 100644
--- a/buildscripts/burn_in_tests.py
+++ b/buildscripts/burn_in_tests.py
@@ -351,7 +351,7 @@ def main():
# Run the executor finder.
else:
# Parse the Evergreen project configuration file.
- evergreen_conf = evergreen.EvergreenProjectConfig(values.evergreen_file)
+ evergreen_conf = evergreen.parse_evergreen_file(values.evergreen_file)
if values.buildvariant is None:
print "Option buildVariant must be specified to find changed tests.\n", \
diff --git a/buildscripts/ciconfig/evergreen.py b/buildscripts/ciconfig/evergreen.py
index 9d6dfa23efa..e7ab5654187 100644
--- a/buildscripts/ciconfig/evergreen.py
+++ b/buildscripts/ciconfig/evergreen.py
@@ -4,21 +4,47 @@ The API also provides methods to access specific fields present in the mongodb/m
configuration file.
"""
+from __future__ import print_function
+
import datetime
+import distutils.spawn # pylint: disable=no-name-in-module
import fnmatch
+import os
import re
import yaml
+import buildscripts.util.runcommand as runcommand
+
+
+def parse_evergreen_file(path, evergreen_binary="evergreen"):
+ """Read an Evergreen file and return EvergreenProjectConfig instance."""
+ if evergreen_binary:
+ if not distutils.spawn.find_executable(evergreen_binary):
+ raise EnvironmentError(
+ "Executable '{}' does not exist or is not in the PATH.".format(evergreen_binary))
+
+ # Call 'evergreen evaluate path' to pre-process the project configuration file.
+ cmd = runcommand.RunCommand(evergreen_binary)
+ cmd.add("evaluate")
+ cmd.add_file(path)
+ error_code, output = cmd.execute()
+ if error_code:
+ raise RuntimeError("Unable to evaluate {}: {}".format(path, output))
+ config = yaml.load(output)
+ else:
+ with open(path, "r") as fstream:
+ config = yaml.load(fstream)
+
+ return EvergreenProjectConfig(config)
+
class EvergreenProjectConfig(object): # pylint: disable=too-many-instance-attributes
"""Represent an Evergreen project configuration file."""
- def __init__(self, path):
- """Initialize the EvergreenProjectConfig from a file path name."""
- with open(path, "r") as fstream:
- self._conf = yaml.load(fstream)
- self.path = path
+ def __init__(self, conf):
+ """Initialize the EvergreenProjectConfig from a YML dictionary."""
+ self._conf = conf
self.tasks = [Task(task_dict) for task_dict in self._conf["tasks"]]
self._tasks_by_name = {task.name: task for task in self.tasks}
self.task_groups = [
diff --git a/buildscripts/tests/ciconfig/test_evergreen.py b/buildscripts/tests/ciconfig/test_evergreen.py
index f1b6740d539..9af34485713 100644
--- a/buildscripts/tests/ciconfig/test_evergreen.py
+++ b/buildscripts/tests/ciconfig/test_evergreen.py
@@ -18,12 +18,12 @@ class TestEvergreenProjectConfig(unittest.TestCase):
@classmethod
def setUpClass(cls):
- cls.conf = _evergreen.EvergreenProjectConfig(TEST_FILE_PATH)
+ cls.conf = _evergreen.parse_evergreen_file(TEST_FILE_PATH, evergreen_binary=None)
def test_invalid_path(self):
invalid_path = "non_existing_file"
with self.assertRaises(IOError):
- _evergreen.EvergreenProjectConfig(invalid_path)
+ _evergreen.parse_evergreen_file(invalid_path, evergreen_binary=None)
def test_list_tasks(self):
self.assertEqual(6, len(self.conf.tasks))
@@ -135,7 +135,7 @@ class TestVariant(unittest.TestCase):
@classmethod
def setUpClass(cls):
- cls.conf = _evergreen.EvergreenProjectConfig(TEST_FILE_PATH)
+ cls.conf = _evergreen.parse_evergreen_file(TEST_FILE_PATH, evergreen_binary=None)
def test_from_dict(self):
task = _evergreen.Task({"name": "compile"})
diff --git a/buildscripts/update_test_lifecycle.py b/buildscripts/update_test_lifecycle.py
index 9e06d42a086..f813e18412e 100755
--- a/buildscripts/update_test_lifecycle.py
+++ b/buildscripts/update_test_lifecycle.py
@@ -1051,7 +1051,7 @@ def main(): # pylint: disable=too-many-branches,too-many-locals,too-many-statem
logging.basicConfig(format="%(asctime)s %(levelname)s %(message)s", level=options.log_level,
filename=options.log_file)
- evg_conf = ci_evergreen.EvergreenProjectConfig(options.evergreen_project_config)
+ evg_conf = ci_evergreen.parse_evergreen_file(options.evergreen_project_config)
use_test_tasks_membership = False
tasks = options.tasks.split(",") if options.tasks else []
diff --git a/buildscripts/util/runcommand.py b/buildscripts/util/runcommand.py
new file mode 100644
index 00000000000..ff9e726cc3e
--- /dev/null
+++ b/buildscripts/util/runcommand.py
@@ -0,0 +1,68 @@
+"""Utility to support running a command in a subprocess."""
+
+from __future__ import print_function
+
+import os
+import pipes
+import shlex
+import sys
+
+# The subprocess32 module is untested on Windows and thus isn't recommended for use, even when it's
+# installed. See https://github.com/google/python-subprocess32/blob/3.2.7/README.md#usage.
+if os.name == "posix" and sys.version_info[0] == 2:
+ try:
+ import subprocess32 as subprocess
+ except ImportError:
+ import warnings
+ warnings.warn(("Falling back to using the subprocess module because subprocess32 isn't"
+ " available. When using the subprocess module, a child process may"
+ " trigger an invalid free(). See SERVER-22219 for more details."),
+ RuntimeWarning)
+ import subprocess # type: ignore
+else:
+ import subprocess
+
+
+def execute_cmd(cmd):
+ """Execute specified 'cmd' and return err_code and output.
+
+ If 'cmd' is specified as a string, convert it to a list of strings.
+ """
+ if isinstance(cmd, str):
+ cmd = shlex.split(cmd)
+ proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+ output, _ = proc.communicate()
+ error_code = proc.returncode
+ return error_code, output
+
+
+class RunCommand(object):
+ """Class to abstract executing a subprocess."""
+
+ def __init__(self, string=None):
+ """Initialize the RunCommand object."""
+ self._command = string
+
+ def add(self, string):
+ """Add a string to the command."""
+ self._command = "{}{}{}".format(self._command, self._space(), string)
+
+ def add_file(self, path):
+ """Add a file path to the command."""
+ # For Windows compatability, use pipes.quote around file paths.
+ self._command = "{}{}{}".format(self._command, self._space(), pipes.quote(path))
+
+ def execute(self):
+ """Execute the command. Return (error_code, output)."""
+ return execute_cmd(self._command)
+
+ @property
+ def command(self):
+ """Get the command."""
+ return self._command
+
+ def _space(self):
+ """Return a space if the command has been started to be built."""
+ if self._command:
+ return " "
+ return ""
diff --git a/etc/evergreen.yml b/etc/evergreen.yml
index 818c5eae640..af4bc8aa182 100644
--- a/etc/evergreen.yml
+++ b/etc/evergreen.yml
@@ -3540,13 +3540,14 @@ tasks:
if [ -n "${burn_in_tests_build_variant|}" ]; then
build_variant=${burn_in_tests_build_variant|}
fi
- $python buildscripts/burn_in_tests.py --branch=${branch_name} --buildVariant=$build_variant --testListOutfile=jstests/new_tests.json --noExec $burn_in_args
+ # Evergreen executable is in $HOME.
+ PATH=$PATH:$HOME $python buildscripts/burn_in_tests.py --branch=${branch_name} --buildVariant=$build_variant --testListOutfile=jstests/new_tests.json --noExec $burn_in_args
# Copy the results to the src dir.
cp jstests/new_tests.json ../src/jstests/new_tests.json
- func: "do multiversion setup"
- func: "run tests"
vars:
- task_path_suffix: /data/multiversion
+ task_path_suffix: /data/multiversion:$HOME
resmoke_wrapper: $python buildscripts/burn_in_tests.py --testListFile=jstests/new_tests.json
resmoke_args: --repeat=2
run_multiple_jobs: true
@@ -6805,7 +6806,8 @@ tasks:
# We use a small batch size to avoid hitting the load balancer timeout if the Evergreen
# API query is not fast enough.
- $python buildscripts/update_test_lifecycle.py \
+ # Evergreen executable is in $HOME.
+ PATH=$PATH:$HOME $python buildscripts/update_test_lifecycle.py \
--project ${project} \
--requestBatchSize 20 \
--commit \