| OLD | NEW |
| 1 # Copyright 2016 The LUCI Authors. All rights reserved. | 1 # Copyright 2016 The LUCI Authors. All rights reserved. |
| 2 # Use of this source code is governed under the Apache License, Version 2.0 | 2 # Use of this source code is governed under the Apache License, Version 2.0 |
| 3 # that can be found in the LICENSE file. | 3 # that can be found in the LICENSE file. |
| 4 | 4 |
| 5 import StringIO | 5 import StringIO |
| 6 import collections | 6 import collections |
| 7 import contextlib | 7 import contextlib |
| 8 import datetime | 8 import datetime |
| 9 import json | 9 import json |
| 10 import os | 10 import os |
| (...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 90 """Constructs an OpenStep object which can be used to actually run a step. | 90 """Constructs an OpenStep object which can be used to actually run a step. |
| 91 | 91 |
| 92 step_dict parameters: | 92 step_dict parameters: |
| 93 name: name of the step, will appear in buildbots waterfall | 93 name: name of the step, will appear in buildbots waterfall |
| 94 cmd: command to run, list of one or more strings | 94 cmd: command to run, list of one or more strings |
| 95 cwd: absolute path to working directory for the command | 95 cwd: absolute path to working directory for the command |
| 96 env: dict with overrides for environment variables | 96 env: dict with overrides for environment variables |
| 97 allow_subannotations: if True, lets the step emit its own annotations | 97 allow_subannotations: if True, lets the step emit its own annotations |
| 98 trigger_specs: a list of trigger specifications, see also _trigger_builds. | 98 trigger_specs: a list of trigger specifications, see also _trigger_builds. |
| 99 stdout: Path to a file to put step stdout into. If used, stdout won't | 99 stdout: Path to a file to put step stdout into. If used, stdout won't |
| 100 appear in annotator's stdout (and |allow_subannotations| is | 100 appear in annotator's stdout (and |allow_subannotations| is |
| 101 ignored). | 101 ignored). |
| 102 stderr: Path to a file to put step stderr into. If used, stderr won't | 102 stderr: Path to a file to put step stderr into. If used, stderr won't |
| 103 appear in annotator's stderr. | 103 appear in annotator's stderr. |
| 104 stdin: Path to a file to read step stdin from. | 104 stdin: Path to a file to read step stdin from. |
| 105 step_nest_level: the step's nesting level. | |
| 106 | 105 |
| 107 Returns an OpenStep object. | 106 Returns an OpenStep object. |
| 108 """ | 107 """ |
| 109 raise NotImplementedError() | 108 raise NotImplementedError() |
| 110 | 109 |
| 111 def run_recipe(self, universe, recipe, properties): | 110 def run_recipe(self, universe, recipe, properties): |
| 112 """Run the recipe named |recipe|. | 111 """Run the recipe named |recipe|. |
| 113 | 112 |
| 114 Args: | 113 Args: |
| 115 universe: The RecipeUniverse where the recipe lives. | 114 universe: The RecipeUniverse where the recipe lives. |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 158 | 157 |
| 159 def __init__(self, stream_engine): | 158 def __init__(self, stream_engine): |
| 160 self._stream_engine = stream_engine | 159 self._stream_engine = stream_engine |
| 161 | 160 |
| 162 @property | 161 @property |
| 163 def stream_engine(self): | 162 def stream_engine(self): |
| 164 return self._stream_engine | 163 return self._stream_engine |
| 165 | 164 |
| 166 def open_step(self, step_dict): | 165 def open_step(self, step_dict): |
| 167 allow_subannotations = step_dict.get('allow_subannotations', False) | 166 allow_subannotations = step_dict.get('allow_subannotations', False) |
| 168 nest_level = step_dict.pop('step_nest_level', 0) | |
| 169 | |
| 170 step_stream = self._stream_engine.new_step_stream( | 167 step_stream = self._stream_engine.new_step_stream( |
| 171 step_dict['name'], | 168 step_dict['name'], |
| 172 allow_subannotations=allow_subannotations, | 169 allow_subannotations=allow_subannotations) |
| 173 nest_level=nest_level) | |
| 174 if not step_dict.get('cmd'): | 170 if not step_dict.get('cmd'): |
| 175 class EmptyOpenStep(OpenStep): | 171 class EmptyOpenStep(OpenStep): |
| 176 def run(inner): | 172 def run(inner): |
| 177 if 'trigger_specs' in step_dict: | 173 if 'trigger_specs' in step_dict: |
| 178 self._trigger_builds(step_stream, step_dict['trigger_specs']) | 174 self._trigger_builds(step_stream, step_dict['trigger_specs']) |
| 179 return types.StepData(step_dict, 0) | 175 return types.StepData(step_dict, 0) |
| 180 | 176 |
| 181 def finalize(inner): | 177 def finalize(inner): |
| 182 step_stream.close() | 178 step_stream.close() |
| 183 | 179 |
| 184 @property | 180 @property |
| 185 def stream(inner): | 181 def stream(inner): |
| 186 return step_stream | 182 return step_stream |
| 187 | 183 |
| 188 return EmptyOpenStep() | 184 return EmptyOpenStep() |
| 189 | 185 |
| 190 step_dict, placeholders = render_step( | 186 step_dict, placeholders = render_step( |
| 191 step_dict, recipe_test_api.DisabledTestData()) | 187 step_dict, recipe_test_api.DisabledTestData()) |
| 192 cmd = map(str, step_dict['cmd']) | 188 cmd = map(str, step_dict['cmd']) |
| 193 step_env = _merge_envs(os.environ, step_dict.get('env', {})) | 189 step_env = _merge_envs(os.environ, step_dict.get('env', {})) |
| 190 if 'nest_level' in step_dict: |
| 191 step_stream.step_nest_level(step_dict['nest_level']) |
| 194 self._print_step(step_stream, step_dict, step_env) | 192 self._print_step(step_stream, step_dict, step_env) |
| 195 | 193 |
| 196 class ReturnOpenStep(OpenStep): | 194 class ReturnOpenStep(OpenStep): |
| 197 def run(inner): | 195 def run(inner): |
| 198 try: | 196 try: |
| 199 # Open file handles for IO redirection based on file names in | 197 # Open file handles for IO redirection based on file names in |
| 200 # step_dict. | 198 # step_dict. |
| 201 handles = { | 199 handles = { |
| 202 'stdout': step_stream, | 200 'stdout': step_stream, |
| 203 'stderr': step_stream, | 201 'stderr': step_stream, |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 394 return change | 392 return change |
| 395 | 393 |
| 396 | 394 |
| 397 class SimulationStepRunner(StepRunner): | 395 class SimulationStepRunner(StepRunner): |
| 398 """Pretends to run steps, instead recording what would have been run. | 396 """Pretends to run steps, instead recording what would have been run. |
| 399 | 397 |
| 400 This is the main workhorse of recipes.py simulation_test. Returns the log of | 398 This is the main workhorse of recipes.py simulation_test. Returns the log of |
| 401 steps that would have been run in steps_ran. Uses test_data to mock return | 399 steps that would have been run in steps_ran. Uses test_data to mock return |
| 402 values. | 400 values. |
| 403 """ | 401 """ |
| 404 def __init__(self, stream_engine, test_data, annotator): | 402 def __init__(self, stream_engine, test_data): |
| 405 self._test_data = test_data | 403 self._test_data = test_data |
| 406 self._stream_engine = stream_engine | 404 self._stream_engine = stream_engine |
| 407 self._annotator = annotator | |
| 408 self._step_history = collections.OrderedDict() | 405 self._step_history = collections.OrderedDict() |
| 409 | 406 |
| 410 @property | 407 @property |
| 411 def stream_engine(self): | 408 def stream_engine(self): |
| 412 return self._stream_engine | 409 return self._stream_engine |
| 413 | 410 |
| 414 def open_step(self, step_dict): | 411 def open_step(self, step_dict): |
| 415 # We modify step_dict. In particular, we add ~followup_annotations during | 412 # We modify step_dict. In particular, we add ~followup_annotations during |
| 416 # finalize, and depend on that side-effect being carried into what we | 413 # finalize, and depend on that side-effect being carried into what we |
| 417 # added to self._step_history, earlier. So copy it here so at least we | 414 # added to self._step_history, earlier. So copy it here so at least we |
| 418 # keep the modifications local. | 415 # keep the modifications local. |
| 419 step_dict = dict(step_dict) | 416 step_dict = dict(step_dict) |
| 420 nest_level = step_dict.pop('step_nest_level', 0) | |
| 421 | 417 |
| 422 test_data_fn = step_dict.pop('step_test_data', recipe_test_api.StepTestData) | 418 test_data_fn = step_dict.pop('step_test_data', recipe_test_api.StepTestData) |
| 423 step_test = self._test_data.pop_step_test_data( | 419 step_test = self._test_data.pop_step_test_data( |
| 424 step_dict['name'], test_data_fn) | 420 step_dict['name'], test_data_fn) |
| 425 step_dict, placeholders = render_step(step_dict, step_test) | 421 step_dict, placeholders = render_step(step_dict, step_test) |
| 422 outstream = StringIO.StringIO() |
| 426 | 423 |
| 427 # Layer the simulation step on top of the given stream engine. | 424 # Layer the simulation step on top of the given stream engine. |
| 428 step_stream = self._stream_engine.new_step_stream( | 425 step_stream = stream.ProductStreamEngine.StepStream( |
| 429 step_dict['name'], | 426 self._stream_engine.new_step_stream(step_dict['name']), |
| 430 nest_level=nest_level) | 427 stream.BareAnnotationStepStream(outstream)) |
| 431 | 428 |
| 432 class ReturnOpenStep(OpenStep): | 429 class ReturnOpenStep(OpenStep): |
| 433 def run(inner): | 430 def run(inner): |
| 434 timeout = step_dict.get('timeout') | 431 timeout = step_dict.get('timeout') |
| 435 if (timeout and step_test.times_out_after and | 432 if (timeout and step_test.times_out_after and |
| 436 step_test.times_out_after > timeout): | 433 step_test.times_out_after > timeout): |
| 437 raise recipe_api.StepTimeout(step_dict['name'], timeout) | 434 raise recipe_api.StepTimeout(step_dict['name'], timeout) |
| 438 | 435 |
| 439 self._step_history[step_dict['name']] = step_dict | 436 self._step_history[step_dict['name']] = step_dict |
| 440 return construct_step_result(step_dict, step_test.retcode, placeholders) | 437 return construct_step_result(step_dict, step_test.retcode, placeholders) |
| 441 | 438 |
| 442 def finalize(inner): | 439 def finalize(inner): |
| 443 # note that '~' sorts after 'z' so that this will be last on each | 440 # note that '~' sorts after 'z' so that this will be last on each |
| 444 # step. also use _step to get access to the mutable step | 441 # step. also use _step to get access to the mutable step |
| 445 # dictionary. | 442 # dictionary. |
| 446 buf = self._annotator.step_buffer(step_dict['name']) | 443 lines = filter(None, outstream.getvalue()).splitlines() |
| 447 lines = filter(None, buf.getvalue()).splitlines() | |
| 448 lines = [stream.encode_str(x) for x in lines] | 444 lines = [stream.encode_str(x) for x in lines] |
| 449 if lines: | 445 if lines: |
| 450 # This magically floats into step_history, which we have already | 446 # This magically floats into step_history, which we have already |
| 451 # added step_dict to. | 447 # added step_dict to. |
| 452 step_dict['~followup_annotations'] = lines | 448 step_dict['~followup_annotations'] = lines |
| 453 step_stream.close() | |
| 454 | 449 |
| 455 @property | 450 @property |
| 456 def stream(inner): | 451 def stream(inner): |
| 457 return step_stream | 452 return step_stream |
| 458 | 453 |
| 459 return ReturnOpenStep() | 454 return ReturnOpenStep() |
| 460 | 455 |
| 461 def run_recipe(self, universe, recipe, properties): | 456 def run_recipe(self, universe, recipe, properties): |
| 462 return self._test_data.depend_on_data.pop(types.freeze((recipe, properties),
)) | 457 return self._test_data.depend_on_data.pop(types.freeze((recipe, properties),
)) |
| 463 | 458 |
| (...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 661 supplied command, and only uses the |env| kwarg for modifying the environment | 656 supplied command, and only uses the |env| kwarg for modifying the environment |
| 662 of the child process. | 657 of the child process. |
| 663 """ | 658 """ |
| 664 saved_path = os.environ['PATH'] | 659 saved_path = os.environ['PATH'] |
| 665 try: | 660 try: |
| 666 if path is not None: | 661 if path is not None: |
| 667 os.environ['PATH'] = path | 662 os.environ['PATH'] = path |
| 668 yield | 663 yield |
| 669 finally: | 664 finally: |
| 670 os.environ['PATH'] = saved_path | 665 os.environ['PATH'] = saved_path |
| OLD | NEW |