Index: recipe_engine/run.py |
diff --git a/recipe_engine/run.py b/recipe_engine/run.py |
index 10570656ec7b93b2cc97af9979573ba26ab212c0..6157ea2ff08f665b1fd4fefbb410eb317b97231d 100644 |
--- a/recipe_engine/run.py |
+++ b/recipe_engine/run.py |
@@ -72,6 +72,7 @@ import os |
import re |
import subprocess |
import sys |
+import tempfile |
import threading |
import traceback |
@@ -81,6 +82,7 @@ import cStringIO |
from . import loader |
from . import recipe_api |
from . import recipe_test_api |
+from . import types |
from . import util |
@@ -433,7 +435,7 @@ def run_steps(properties, |
'TESTING_SLAVENAME' in os.environ)): |
properties['use_mirror'] = False |
- engine = RecipeEngine(stream, properties, test_data) |
+ engine = RecipeEngine(stream, properties, test_data, universe) |
# Create all API modules and top level RunSteps function. It doesn't launch |
# any recipe code yet; RunSteps needs to be called. |
@@ -750,11 +752,12 @@ class RecipeEngine(object): |
* step - uses engine.create_step(...). |
""" |
- def __init__(self, stream, properties, test_data): |
+ def __init__(self, stream, properties, test_data, universe): |
self._stream = stream |
self._properties = properties |
self._test_data = test_data |
self._step_history = collections.OrderedDict() |
+ self._universe = universe |
self._previous_step_annotation = None |
self._previous_step_result = None |
@@ -861,11 +864,12 @@ class RecipeEngine(object): |
raise exc(step['name'], step_result) |
- |
def run(self, recipe_script, api): |
"""Run a recipe represented by a recipe_script object. |
This function blocks until recipe finishes. |
+ It mainly executes the recipe, and has some exception handling logic, and |
+ adds the step history to the result. |
Args: |
recipe_script: The recipe to run, as represented by a RecipeScript object. |
@@ -940,4 +944,63 @@ class RecipeEngine(object): |
""" |
return step.as_jsonish() |
+ def depend_on(self, recipe, properties, distributor=None): |
+ return self.depend_on_multi( |
+ ((recipe, properties),), distributor=distributor)[0] |
+ |
+ def depend_on_multi(self, dependencies, distributor=None): |
+ results = [] |
+ for recipe, properties in dependencies: |
+ recipe_script = self._universe.load_recipe(recipe) |
+ |
+ return_schema = getattr(recipe_script, 'RETURN_SCHEMA', None) |
+ if not return_schema: |
+ raise ValueError( |
+ "Invalid recipe %s. Recipe must have a return schema." % recipe) |
+ |
+ # run_recipe is a function which will be called once the properties have |
+ # been validated by the recipe engine. The arguments being passed in are |
+ # simply the values being passed to the recipe, which we already know, so |
+ # we ignore them. We're only using this for its properties validation |
+ # functionality. |
+ run_recipe = lambda *args, **kwargs: None |
+ |
+ if self._test_data.enabled: |
+ run_recipe = lambda *args, **kwargs: ( |
+ self._test_data.depend_on_data[types.freeze((recipe, properties),)]) |
+ else: |
+ # TODO(martiniss) Add DM integration |
+ assert distributor is None, "Only local recipe execution supported." |
+ |
+ def run_recipe(*args, **kwargs): |
+ with tempfile.NamedTemporaryFile() as f: |
+ cmd = [sys.executable, |
+ self._universe.package_deps.engine_recipes_py, |
+ '--package=%s' % self._universe.config_file, 'run', |
+ '--output-result-json=%s' % f.name, recipe] |
+ cmd.extend(['%s=%s' % (k, v) for k, v in properties.iteritems()]) |
+ |
+ retcode = subprocess.call(cmd) |
+ result = json.load(f) |
+ if retcode != 0: |
+ raise recipe_api.StepFailure( |
+ 'depend on %s with properties %r failed with %d.' |
+ 'Recipe result: %r' % ( |
+ recipe, properties, retcode, result)) |
+ return result |
+ |
+ try: |
+ # This does type checking for properties |
+ results.append( |
+ loader._invoke_with_properties( |
+ run_recipe, properties, recipe_script.PROPERTIES, |
+ properties.keys())) |
+ except TypeError as e: |
+ raise TypeError( |
+ "Got %r while trying to call recipe %s with properties %r" % ( |
+ e, recipe, properties)) |
+ |
+ return results |
+ |
+ |