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 """Entry point for fully-annotated builds. | 5 """Entry point for fully-annotated builds. |
6 | 6 |
7 This script is part of the effort to move all builds to annotator-based | 7 This script is part of the effort to move all builds to annotator-based |
8 systems. Any builder configured to use the AnnotatorFactory.BaseFactory() | 8 systems. Any builder configured to use the AnnotatorFactory.BaseFactory() |
9 found in scripts/master/factory/annotator_factory.py executes a single | 9 found in scripts/master/factory/annotator_factory.py executes a single |
10 AddAnnotatedScript step. That step (found in annotator_commands.py) calls | 10 AddAnnotatedScript step. That step (found in annotator_commands.py) calls |
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
204 properties.pop('root', None) | 204 properties.pop('root', None) |
205 | 205 |
206 # TODO(iannucci): A much better way to do this would be to dynamically | 206 # TODO(iannucci): A much better way to do this would be to dynamically |
207 # detect if the mirrors are actually available during the execution of the | 207 # detect if the mirrors are actually available during the execution of the |
208 # recipe. | 208 # recipe. |
209 if ('use_mirror' not in properties and ( | 209 if ('use_mirror' not in properties and ( |
210 'TESTING_MASTERNAME' in os.environ or | 210 'TESTING_MASTERNAME' in os.environ or |
211 'TESTING_SLAVENAME' in os.environ)): | 211 'TESTING_SLAVENAME' in os.environ)): |
212 properties['use_mirror'] = False | 212 properties['use_mirror'] = False |
213 | 213 |
214 with stream_engine.new_step_stream('setup_build') as s: | 214 with stream_engine.make_step_stream('setup_build') as s: |
215 engine = RecipeEngine(step_runner, properties, universe_view) | 215 engine = RecipeEngine(step_runner, properties, universe_view) |
216 | 216 |
217 # Create all API modules and top level RunSteps function. It doesn't launch | 217 # Create all API modules and top level RunSteps function. It doesn't launch |
218 # any recipe code yet; RunSteps needs to be called. | 218 # any recipe code yet; RunSteps needs to be called. |
219 api = None | 219 api = None |
220 | 220 |
221 assert 'recipe' in properties | 221 assert 'recipe' in properties |
222 recipe = properties['recipe'] | 222 recipe = properties['recipe'] |
223 | 223 |
224 properties_to_print = properties.copy() | 224 properties_to_print = properties.copy() |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
291 Knows how to execute steps emitted by a recipe, holds global state such as | 291 Knows how to execute steps emitted by a recipe, holds global state such as |
292 step history and build properties. Each recipe module API has a reference to | 292 step history and build properties. Each recipe module API has a reference to |
293 this object. | 293 this object. |
294 | 294 |
295 Recipe modules that are aware of the engine: | 295 Recipe modules that are aware of the engine: |
296 * properties - uses engine.properties. | 296 * properties - uses engine.properties. |
297 * step - uses engine.create_step(...), and previous_step_result. | 297 * step - uses engine.create_step(...), and previous_step_result. |
298 """ | 298 """ |
299 | 299 |
300 ActiveStep = collections.namedtuple('ActiveStep', ( | 300 ActiveStep = collections.namedtuple('ActiveStep', ( |
301 'step', 'step_result', 'open_step', 'nest_level')) | 301 'config', 'step_result', 'open_step')) |
302 | 302 |
303 def __init__(self, step_runner, properties, universe_view): | 303 def __init__(self, step_runner, properties, universe_view): |
304 """See run_steps() for parameter meanings.""" | 304 """See run_steps() for parameter meanings.""" |
305 self._step_runner = step_runner | 305 self._step_runner = step_runner |
306 self._properties = properties | 306 self._properties = properties |
307 self._universe_view = universe_view | 307 self._universe_view = universe_view |
308 | 308 |
309 # A stack of ActiveStep objects, holding the most recently executed step at | 309 # A stack of ActiveStep objects, holding the most recently executed step at |
310 # each nest level (objects deeper in the stack have lower nest levels). | 310 # each nest level (objects deeper in the stack have lower nest levels). |
311 # When we pop from this stack, we close the corresponding step stream. | 311 # When we pop from this stack, we close the corresponding step stream. |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
343 " result. Check to make sure your code isn't doing incorrect logic" | 343 " result. Check to make sure your code isn't doing incorrect logic" |
344 " with try-finally blocks.") | 344 " with try-finally blocks.") |
345 return self._step_stack[-1].step_result | 345 return self._step_stack[-1].step_result |
346 | 346 |
347 def _close_through_level(self, level): | 347 def _close_through_level(self, level): |
348 """Close all open steps whose nest level is >= the supplied level. | 348 """Close all open steps whose nest level is >= the supplied level. |
349 | 349 |
350 Args: | 350 Args: |
351 level (int): the nest level to close through. | 351 level (int): the nest level to close through. |
352 """ | 352 """ |
353 while self._step_stack and self._step_stack[-1].nest_level >= level: | 353 while self._step_stack and self._step_stack[-1].config.nest_level >= level: |
354 cur = self._step_stack.pop() | 354 cur = self._step_stack.pop() |
355 if cur.step_result: | 355 if cur.step_result: |
356 cur.step_result.presentation.finalize(cur.open_step.stream) | 356 cur.step_result.presentation.finalize(cur.open_step.stream) |
357 cur.open_step.finalize() | 357 cur.open_step.finalize() |
358 | 358 |
359 def run_step(self, step): | 359 def run_step(self, step_dict): |
360 """ | 360 """ |
361 Runs a step. | 361 Runs a step. |
362 | 362 |
363 Args: | 363 Args: |
364 step: The step to run. | 364 step_dict (dict): A step dictionary to run. |
365 | 365 |
366 Returns: | 366 Returns: |
367 A StepData object containing the result of running the step. | 367 A StepData object containing the result of running the step. |
368 """ | 368 """ |
| 369 step_config = recipe_api._make_step_config(**step_dict) |
369 with util.raises((recipe_api.StepFailure, OSError), | 370 with util.raises((recipe_api.StepFailure, OSError), |
370 self._step_runner.stream_engine): | 371 self._step_runner.stream_engine): |
371 ok_ret = step.pop('ok_ret') | |
372 infra_step = step.pop('infra_step') | |
373 | |
374 step_result = None | 372 step_result = None |
375 | 373 |
376 nest_level = step.get('step_nest_level', 0) | 374 self._close_through_level(step_config.nest_level) |
377 self._close_through_level(nest_level) | |
378 | 375 |
379 open_step = self._step_runner.open_step(step) | 376 open_step = self._step_runner.open_step(step_config) |
380 self._step_stack.append(self.ActiveStep( | 377 self._step_stack.append(self.ActiveStep( |
381 step=step, | 378 config=step_config, |
382 step_result=None, | 379 step_result=None, |
383 open_step=open_step, | 380 open_step=open_step)) |
384 nest_level=nest_level)) | |
385 | 381 |
386 step_result = open_step.run() | 382 step_result = open_step.run() |
387 self._step_stack[-1] = ( | 383 self._step_stack[-1] = ( |
388 self._step_stack[-1]._replace(step_result=step_result)) | 384 self._step_stack[-1]._replace(step_result=step_result)) |
389 | 385 |
390 if step_result.retcode in ok_ret: | 386 if step_result.retcode in step_config.ok_ret: |
391 step_result.presentation.status = 'SUCCESS' | 387 step_result.presentation.status = 'SUCCESS' |
392 return step_result | 388 return step_result |
393 else: | 389 else: |
394 if not infra_step: | 390 if not step_config.infra_step: |
395 state = 'FAILURE' | 391 state = 'FAILURE' |
396 exc = recipe_api.StepFailure | 392 exc = recipe_api.StepFailure |
397 else: | 393 else: |
398 state = 'EXCEPTION' | 394 state = 'EXCEPTION' |
399 exc = recipe_api.InfraFailure | 395 exc = recipe_api.InfraFailure |
400 | 396 |
401 step_result.presentation.status = state | 397 step_result.presentation.status = state |
402 | 398 |
403 self._step_stack[-1].open_step.stream.write_line( | 399 self._step_stack[-1].open_step.stream.write_line( |
404 'step returned non-zero exit code: %d' % step_result.retcode) | 400 'step returned non-zero exit code: %d' % step_result.retcode) |
405 | 401 |
406 raise exc(step['name'], step_result) | 402 raise exc(step_config.name, step_result) |
407 | 403 |
408 def run(self, recipe_script, api): | 404 def run(self, recipe_script, api): |
409 """Run a recipe represented by a recipe_script object. | 405 """Run a recipe represented by a recipe_script object. |
410 | 406 |
411 This function blocks until recipe finishes. | 407 This function blocks until recipe finishes. |
412 It mainly executes the recipe, and has some exception handling logic, and | 408 It mainly executes the recipe, and has some exception handling logic, and |
413 adds the step history to the result. | 409 adds the step history to the result. |
414 | 410 |
415 Args: | 411 Args: |
416 recipe_script: The recipe to run, as represented by a RecipeScript object. | 412 recipe_script: The recipe to run, as represented by a RecipeScript object. |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
458 "reason": "Uncaught Exception: %r" % ex, | 454 "reason": "Uncaught Exception: %r" % ex, |
459 "traceback": traceback.format_exc().splitlines(), | 455 "traceback": traceback.format_exc().splitlines(), |
460 "status_code": -1 | 456 "status_code": -1 |
461 } | 457 } |
462 | 458 |
463 raise | 459 raise |
464 | 460 |
465 result['name'] = '$result' | 461 result['name'] = '$result' |
466 return RecipeResult(result) | 462 return RecipeResult(result) |
467 | 463 |
468 def create_step(self, step): # pylint: disable=R0201 | |
469 """Called by step module to instantiate a new step. | |
470 | |
471 Args: | |
472 step: ConfigGroup object with information about the step, see | |
473 recipe_modules/step/config.py. | |
474 | |
475 Returns: | |
476 Opaque engine specific object that is understood by 'run_steps' method. | |
477 """ | |
478 return step.as_jsonish() | |
479 | |
480 def depend_on(self, recipe, properties, distributor=None): | 464 def depend_on(self, recipe, properties, distributor=None): |
481 return self.depend_on_multi( | 465 return self.depend_on_multi( |
482 ((recipe, properties),), distributor=distributor)[0] | 466 ((recipe, properties),), distributor=distributor)[0] |
483 | 467 |
484 def depend_on_multi(self, dependencies, distributor=None): | 468 def depend_on_multi(self, dependencies, distributor=None): |
485 results = [] | 469 results = [] |
486 for recipe, properties in dependencies: | 470 for recipe, properties in dependencies: |
487 recipe_script = self._universe_view.load_recipe(recipe) | 471 recipe_script = self._universe_view.load_recipe(recipe) |
488 | 472 |
489 return_schema = getattr(recipe_script, 'RETURN_SCHEMA', None) | 473 return_schema = getattr(recipe_script, 'RETURN_SCHEMA', None) |
(...skipping 14 matching lines...) Expand all Loading... |
504 results.append( | 488 results.append( |
505 loader._invoke_with_properties( | 489 loader._invoke_with_properties( |
506 run_recipe, properties, recipe_script.PROPERTIES, | 490 run_recipe, properties, recipe_script.PROPERTIES, |
507 properties.keys())) | 491 properties.keys())) |
508 except TypeError as e: | 492 except TypeError as e: |
509 raise TypeError( | 493 raise TypeError( |
510 "Got %r while trying to call recipe %s with properties %r" % ( | 494 "Got %r while trying to call recipe %s with properties %r" % ( |
511 e, recipe, properties)) | 495 e, recipe, properties)) |
512 | 496 |
513 return results | 497 return results |
OLD | NEW |