Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: scripts/master/chromium_step.py

Issue 1949753003: Fail build if triggering fails (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: addressed comments Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | scripts/slave/recipe_modules/trigger/api.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Subclasses of various slave command classes.""" 5 """Subclasses of various slave command classes."""
6 6
7 from datetime import datetime 7 from datetime import datetime
8 import copy 8 import copy
9 import errno 9 import errno
10 import json 10 import json
(...skipping 641 matching lines...) Expand 10 before | Expand all | Expand 10 after
652 def __init__(self, command=None, show_perf=False, perf_id=None, 652 def __init__(self, command=None, show_perf=False, perf_id=None,
653 perf_report_url_suffix=None, target=None, active_master=None, 653 perf_report_url_suffix=None, target=None, active_master=None,
654 *args, **kwargs): 654 *args, **kwargs):
655 buildstep.LogLineObserver.__init__(self, *args, **kwargs) 655 buildstep.LogLineObserver.__init__(self, *args, **kwargs)
656 self.command = command 656 self.command = command
657 self.sections = [] 657 self.sections = []
658 self.annotate_status = builder.SUCCESS 658 self.annotate_status = builder.SUCCESS
659 self.halt_on_failure = False 659 self.halt_on_failure = False
660 self.honor_zero_return_code = False 660 self.honor_zero_return_code = False
661 self.cursor = None 661 self.cursor = None
662 self.fatal_errors = [] # a list of fatal error descriptions
662 663
663 self.show_perf = show_perf 664 self.show_perf = show_perf
664 self.perf_id = perf_id 665 self.perf_id = perf_id
665 self.perf_report_url_suffix = perf_report_url_suffix 666 self.perf_report_url_suffix = perf_report_url_suffix
666 self.target = target 667 self.target = target
667 self.active_master = active_master 668 self.active_master = active_master
668 self.bb_triggering_service = None 669 self.bb_triggering_service = None
669 670
671 def record_fatal_error(self, description):
672 self.fatal_errors.append(description)
673
670 def initialSection(self): 674 def initialSection(self):
671 """Initializes the annotator's sections. 675 """Initializes the annotator's sections.
672 676
673 Annotator uses a list of dictionaries which hold information stuch as status 677 Annotator uses a list of dictionaries which hold information stuch as status
674 and logs for each added step. This method populates the section list with an 678 and logs for each added step. This method populates the section list with an
675 entry referencing the original buildbot step.""" 679 entry referencing the original buildbot step."""
676 if self.sections: 680 if self.sections:
677 return 681 return
678 # Add a log section for output before the first section heading. 682 # Add a log section for output before the first section heading.
679 preamble = self.command.addLog('preamble') 683 preamble = self.command.addLog('preamble')
(...skipping 188 matching lines...) Expand 10 before | Expand all | Expand 10 after
868 self.outReceived(line) 872 self.outReceived(line)
869 elif channel == interfaces.LOG_CHANNEL_STDERR: 873 elif channel == interfaces.LOG_CHANNEL_STDERR:
870 self.errReceived(line) 874 self.errReceived(line)
871 elif channel == interfaces.LOG_CHANNEL_HEADER: 875 elif channel == interfaces.LOG_CHANNEL_HEADER:
872 self.headerReceived(line) 876 self.headerReceived(line)
873 877
874 def outReceived(self, data): 878 def outReceived(self, data):
875 buildstep.LogLineObserver.outReceived(self, data) 879 buildstep.LogLineObserver.outReceived(self, data)
876 if self.sections: 880 if self.sections:
877 self.ensureStepIsStarted(self.cursor) 881 self.ensureStepIsStarted(self.cursor)
878 self.cursor['log'].addStdout(data) 882 if self.cursor['log'].finished:
883 # This might happen if the section was finished with a premature error
884 # asynchronously.
885 pass
886 else:
887 self.cursor['log'].addStdout(data)
879 888
880 def errReceived(self, data): 889 def errReceived(self, data):
881 buildstep.LogLineObserver.errReceived(self, data) 890 buildstep.LogLineObserver.errReceived(self, data)
882 if self.sections: 891 if self.sections:
883 self.ensureStepIsStarted(self.cursor) 892 self.ensureStepIsStarted(self.cursor)
884 self.cursor['log'].addStderr(data) 893 self.cursor['log'].addStderr(data)
885 894
886 def headerReceived(self, data): 895 def headerReceived(self, data):
887 if self.sections: 896 if self.sections:
888 preamble = self.sections[0] 897 preamble = self.sections[0]
(...skipping 318 matching lines...) Expand 10 before | Expand all | Expand 10 after
1207 1216
1208 # Sort files. 1217 # Sort files.
1209 files = change.get('files') 1218 files = change.get('files')
1210 if files: 1219 if files:
1211 change['files'] = sorted(files) 1220 change['files'] = sorted(files)
1212 1221
1213 return change 1222 return change
1214 1223
1215 def STEP_TRIGGER(self, spec): 1224 def STEP_TRIGGER(self, spec):
1216 # Support: @@@STEP_TRIGGER <json spec>@@@ (trigger build(s)). 1225 # Support: @@@STEP_TRIGGER <json spec>@@@ (trigger build(s)).
1226 builder_names = ['<unknown>']
1227
1228 section = self.cursor
1229 critical = True
1230
1231 def handle_exception(ex):
1232 if not section['step'].isFinished():
1233 self.finishStep(section, builder.EXCEPTION, ex)
1234 if critical:
1235 self.record_fatal_error(
1236 'step %s: could not trigger builds %r: %s' %
1237 (section['name'], builder_names, ex))
1238
1217 try: 1239 try:
1218 spec = json.loads(spec) 1240 spec = json.loads(spec)
1241 critical = spec.get('critical', True)
1219 bucket = spec.get('bucket') 1242 bucket = spec.get('bucket')
1220 builder_names = spec.get('builderNames') 1243 builder_names = spec.get('builderNames')
1221 properties = spec.get('properties') or {} 1244 properties = spec.get('properties') or {}
1222 changes = spec.get('changes') 1245 changes = spec.get('changes')
1223 tags = spec.get('tags') or [] 1246 tags = spec.get('tags') or []
1224 if changes: 1247 if changes:
1225 assert isinstance(changes, list) 1248 assert isinstance(changes, list)
1226 changes = map(self.normalizeChangeSpec, changes) 1249 changes = map(self.normalizeChangeSpec, changes)
1227 1250
1228 if not builder_names: 1251 if not builder_names:
1229 raise ValueError('builderNames is not specified: %r' % (spec,)) 1252 raise ValueError('builderNames is not specified: %r' % (spec,))
1230 1253
1231 build = self.command.build 1254 build = self.command.build
1232 build_is_from_buildbucket = bool( 1255 build_is_from_buildbucket = bool(
1233 build.getProperties().getProperty(buildbucket.common.INFO_PROPERTY)) 1256 build.getProperties().getProperty(buildbucket.common.INFO_PROPERTY))
1234 trigger_via_buildbucket = bucket or build_is_from_buildbucket 1257 trigger_via_buildbucket = bucket or build_is_from_buildbucket
1235 1258
1236 properties = self.getPropertiesForTriggeredBuild(build.getProperties(), 1259 properties = self.getPropertiesForTriggeredBuild(build.getProperties(),
1237 properties) 1260 properties)
1238 1261
1239 if trigger_via_buildbucket: 1262 if trigger_via_buildbucket:
1240 d = self.triggerBuildsViaBuildBucket( 1263 d = self.triggerBuildsViaBuildBucket(
1241 bucket, builder_names, properties, tags, changes) 1264 bucket, builder_names, properties, tags, changes)
1242 else: 1265 else:
1243 d = self.triggerBuildsLocally(builder_names, properties, changes) 1266 d = self.triggerBuildsLocally(builder_names, properties, changes)
1244 # addAsyncOpToCursor expects a deferred to return a build result. If a 1267 # addAsyncOpToCursor expects a deferred to return a build result. If a
1245 # buildset is added, then it is a success. This lambda function returns a 1268 # buildset is added, then it is a success. This lambda function returns a
1246 # tuple, which is received by addAsyncOpToCursor. 1269 # tuple, which is received by addAsyncOpToCursor.
1247 d.addCallback(lambda _: (builder.SUCCESS, None)) 1270 d.addCallback(lambda _: (builder.SUCCESS, None))
1248 description = 'Triggering build(s) on %s' % (', '.join(builder_names),) 1271 self.addAsyncOpToCursor(
1249 self.addAsyncOpToCursor(d, description) 1272 d,
1273 'Triggering build(s) on %s' % (', '.join(builder_names),))
1274 d.addErrback(handle_exception)
1250 except Exception as ex: 1275 except Exception as ex:
1251 self.finishStep(self.cursor, builder.FAILURE, ex) 1276 handle_exception(ex)
Dirk Pranke 2016/05/05 02:16:07 I'm confused ... why isn't it sufficient to just s
nodir 2016/05/05 16:45:32 because triggering is an async operation, in parti
Dirk Pranke 2016/05/05 16:56:10 Ah, okay.
nodir 2016/05/05 16:58:20 iannucci verified that exceptions are used in reci
1252 1277
1253 @staticmethod 1278 @staticmethod
1254 def getPropertiesForTriggeredBuild(current_properties, new_properties): 1279 def getPropertiesForTriggeredBuild(current_properties, new_properties):
1255 props = { 1280 props = {
1256 'parent_buildername': current_properties.getProperty('buildername'), 1281 'parent_buildername': current_properties.getProperty('buildername'),
1257 'parent_buildnumber': current_properties.getProperty('buildnumber'), 1282 'parent_buildnumber': current_properties.getProperty('buildnumber'),
1258 } 1283 }
1259 props.update(new_properties) 1284 props.update(new_properties)
1260 return props 1285 return props
1261 1286
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after
1399 bsid, brids = yield master.addBuildset( 1424 bsid, brids = yield master.addBuildset(
1400 ssid=ssid, 1425 ssid=ssid,
1401 reason='Triggered by %s' % build.builder.name, 1426 reason='Triggered by %s' % build.builder.name,
1402 # Specify property source. 1427 # Specify property source.
1403 properties={k: (v, 'ParentBuild') for k, v in properties.iteritems()}, 1428 properties={k: (v, 'ParentBuild') for k, v in properties.iteritems()},
1404 builderNames=builder_names) 1429 builderNames=builder_names)
1405 log.msg('Triggered a buildset %s with builders %s' % (bsid, builder_names)) 1430 log.msg('Triggered a buildset %s with builders %s' % (bsid, builder_names))
1406 defer.returnValue((bsid, brids)) 1431 defer.returnValue((bsid, brids))
1407 1432
1408 def handleReturnCode(self, return_code): 1433 def handleReturnCode(self, return_code):
1409 # Treat all non-zero return codes as failure. 1434 succeeded = False
1410 # We could have a special return code for warnings/exceptions, however, 1435 if self.fatal_errors:
1411 # this might conflict with some existing use of a return code. 1436 # Even if return_code==0 and self.honor_zero_return_code is True
1412 # Besides, applications can always intercept return codes and emit 1437 # we can't do much when there are fatal errors
1413 # STEP_* tags. 1438 preamble = self.sections[0]
1414 succeeded = return_code == 0 1439 fatal_errors_str = '\n'.join(self.fatal_errors)
1415 if succeeded: 1440 fatal_error_log = preamble['step'].addLog('fatal_errors')
1441 fatal_error_log.addStdout(fatal_errors_str)
1442 fatal_error_log.finish()
1443 self.annotate_status = builder.EXCEPTION
1444 if not self.cursorIsPreamble():
1445 self.finishCursor(self.annotate_status,
1446 reason='Fatal errors: %s' % fatal_errors_str)
1447 elif return_code == 0:
1448 succeeded = True
1416 # Do not close the initial section because it will be closed by 1449 # Do not close the initial section because it will be closed by
1417 # self.finished when runCommand completes. 1450 # self.finished when runCommand completes.
1418 if not self.cursorIsPreamble(): 1451 if not self.cursorIsPreamble():
1419 self.closeCursor() 1452 self.closeCursor()
1420 if self.honor_zero_return_code: 1453 if self.honor_zero_return_code:
1421 self.annotate_status = builder.SUCCESS 1454 self.annotate_status = builder.SUCCESS
1422 else: 1455 else:
1456 # Treat all non-zero return codes as failure.
1457 # We could have a special return code for warnings/exceptions, however,
1458 # this might conflict with some existing use of a return code.
1459 # Besides, applications can always intercept return codes and emit
1460 # STEP_* tags.
1461
1423 # Only change annotate status if it'd otherwise indicate success. 1462 # Only change annotate status if it'd otherwise indicate success.
1424 # This helps propagate purple (exception) step status to to-level 1463 # This helps propagate purple (exception) step status to to-level
1425 # purple build status as opposed to red. 1464 # purple build status as opposed to red.
1426 if self.annotate_status in (builder.SUCCESS, builder.WARNINGS): 1465 if self.annotate_status in (builder.SUCCESS, builder.WARNINGS):
1427 self.annotate_status = builder.FAILURE 1466 self.annotate_status = builder.FAILURE
1428 if not self.cursorIsPreamble(): 1467 if not self.cursorIsPreamble():
1429 self.finishCursor(self.annotate_status, 1468 self.finishCursor(self.annotate_status,
1430 reason='return code was %d.' % return_code) 1469 reason='return code was %d.' % return_code)
1431 self.cleanupSteps(exclude_async_pending=succeeded) 1470 self.cleanupSteps(exclude_async_pending=succeeded)
1432 1471
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
1543 and starts to wait for remaining steps to finish. Returns command_result 1582 and starts to wait for remaining steps to finish. Returns command_result
1544 as Deferred.""" 1583 as Deferred."""
1545 self.scriptComplete(command_result) 1584 self.scriptComplete(command_result)
1546 steps_d = self.script_observer.waitForSteps() 1585 steps_d = self.script_observer.waitForSteps()
1547 # Ignore the waitForSteps' result and return the original result, 1586 # Ignore the waitForSteps' result and return the original result,
1548 # so the caller of runCommand receives command_result. 1587 # so the caller of runCommand receives command_result.
1549 steps_d.addCallback(lambda *_: command_result) 1588 steps_d.addCallback(lambda *_: command_result)
1550 return steps_d 1589 return steps_d
1551 d.addCallback(onCommandFinished) 1590 d.addCallback(onCommandFinished)
1552 return d 1591 return d
OLDNEW
« no previous file with comments | « no previous file | scripts/slave/recipe_modules/trigger/api.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698