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