Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 Loading... | |
| 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 Loading... | |
| 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 an premature error | |
|
Vadim Sh.
2016/05/04 19:24:44
nit: "a premature error"
nodir
2016/05/05 00:03:12
Done.
| |
| 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 Loading... | |
| 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 self.finishStep(section, builder.FAILURE, ex) | |
|
Vadim Sh.
2016/05/04 19:24:44
what if 'section' is already finished?
nodir
2016/05/05 00:03:12
Done
| |
| 1233 if critical: | |
| 1234 self.record_fatal_error( | |
| 1235 'step %s: could not trigger builds %r: %s' % | |
| 1236 (section['name'], builder_names, ex)) | |
| 1237 | |
| 1217 try: | 1238 try: |
| 1218 spec = json.loads(spec) | 1239 spec = json.loads(spec) |
| 1240 critical = spec.get('critical', True) | |
| 1219 bucket = spec.get('bucket') | 1241 bucket = spec.get('bucket') |
| 1220 builder_names = spec.get('builderNames') | 1242 builder_names = spec.get('builderNames') |
| 1221 properties = spec.get('properties') or {} | 1243 properties = spec.get('properties') or {} |
| 1222 changes = spec.get('changes') | 1244 changes = spec.get('changes') |
| 1223 tags = spec.get('tags') or [] | 1245 tags = spec.get('tags') or [] |
| 1224 if changes: | 1246 if changes: |
| 1225 assert isinstance(changes, list) | 1247 assert isinstance(changes, list) |
| 1226 changes = map(self.normalizeChangeSpec, changes) | 1248 changes = map(self.normalizeChangeSpec, changes) |
| 1227 | 1249 |
| 1228 if not builder_names: | 1250 if not builder_names: |
| 1229 raise ValueError('builderNames is not specified: %r' % (spec,)) | 1251 raise ValueError('builderNames is not specified: %r' % (spec,)) |
| 1230 | 1252 |
| 1231 build = self.command.build | 1253 build = self.command.build |
| 1232 build_is_from_buildbucket = bool( | 1254 build_is_from_buildbucket = bool( |
| 1233 build.getProperties().getProperty(buildbucket.common.INFO_PROPERTY)) | 1255 build.getProperties().getProperty(buildbucket.common.INFO_PROPERTY)) |
| 1234 trigger_via_buildbucket = bucket or build_is_from_buildbucket | 1256 trigger_via_buildbucket = bucket or build_is_from_buildbucket |
| 1235 | 1257 |
| 1236 properties = self.getPropertiesForTriggeredBuild(build.getProperties(), | 1258 properties = self.getPropertiesForTriggeredBuild(build.getProperties(), |
| 1237 properties) | 1259 properties) |
| 1238 | 1260 |
| 1239 if trigger_via_buildbucket: | 1261 if trigger_via_buildbucket: |
| 1240 d = self.triggerBuildsViaBuildBucket( | 1262 d = self.triggerBuildsViaBuildBucket( |
| 1241 bucket, builder_names, properties, tags, changes) | 1263 bucket, builder_names, properties, tags, changes) |
| 1242 else: | 1264 else: |
| 1243 d = self.triggerBuildsLocally(builder_names, properties, changes) | 1265 d = self.triggerBuildsLocally(builder_names, properties, changes) |
| 1244 # addAsyncOpToCursor expects a deferred to return a build result. If a | 1266 # 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 | 1267 # buildset is added, then it is a success. This lambda function returns a |
| 1246 # tuple, which is received by addAsyncOpToCursor. | 1268 # tuple, which is received by addAsyncOpToCursor. |
| 1247 d.addCallback(lambda _: (builder.SUCCESS, None)) | 1269 d.addCallback(lambda _: (builder.SUCCESS, None)) |
| 1248 description = 'Triggering build(s) on %s' % (', '.join(builder_names),) | 1270 self.addAsyncOpToCursor( |
| 1249 self.addAsyncOpToCursor(d, description) | 1271 d, |
| 1272 'Triggering build(s) on %s' % (', '.join(builder_names),)) | |
| 1273 d.addErrback(handle_exception) | |
| 1250 except Exception as ex: | 1274 except Exception as ex: |
| 1251 self.finishStep(self.cursor, builder.FAILURE, ex) | 1275 handle_exception(ex) |
| 1252 | 1276 |
| 1253 @staticmethod | 1277 @staticmethod |
| 1254 def getPropertiesForTriggeredBuild(current_properties, new_properties): | 1278 def getPropertiesForTriggeredBuild(current_properties, new_properties): |
| 1255 props = { | 1279 props = { |
| 1256 'parent_buildername': current_properties.getProperty('buildername'), | 1280 'parent_buildername': current_properties.getProperty('buildername'), |
| 1257 'parent_buildnumber': current_properties.getProperty('buildnumber'), | 1281 'parent_buildnumber': current_properties.getProperty('buildnumber'), |
| 1258 } | 1282 } |
| 1259 props.update(new_properties) | 1283 props.update(new_properties) |
| 1260 return props | 1284 return props |
| 1261 | 1285 |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1399 bsid, brids = yield master.addBuildset( | 1423 bsid, brids = yield master.addBuildset( |
| 1400 ssid=ssid, | 1424 ssid=ssid, |
| 1401 reason='Triggered by %s' % build.builder.name, | 1425 reason='Triggered by %s' % build.builder.name, |
| 1402 # Specify property source. | 1426 # Specify property source. |
| 1403 properties={k: (v, 'ParentBuild') for k, v in properties.iteritems()}, | 1427 properties={k: (v, 'ParentBuild') for k, v in properties.iteritems()}, |
| 1404 builderNames=builder_names) | 1428 builderNames=builder_names) |
| 1405 log.msg('Triggered a buildset %s with builders %s' % (bsid, builder_names)) | 1429 log.msg('Triggered a buildset %s with builders %s' % (bsid, builder_names)) |
| 1406 defer.returnValue((bsid, brids)) | 1430 defer.returnValue((bsid, brids)) |
| 1407 | 1431 |
| 1408 def handleReturnCode(self, return_code): | 1432 def handleReturnCode(self, return_code): |
| 1409 # Treat all non-zero return codes as failure. | 1433 succeeded = False |
| 1410 # We could have a special return code for warnings/exceptions, however, | 1434 if self.fatal_errors: |
| 1411 # this might conflict with some existing use of a return code. | 1435 # Even if return_code==0 and self.honor_zero_return_code is True |
| 1412 # Besides, applications can always intercept return codes and emit | 1436 # we can't do much when there are fatal errors |
| 1413 # STEP_* tags. | 1437 preamble = self.sections[0] |
| 1414 succeeded = return_code == 0 | 1438 fatal_errors_str = '\n'.join(self.fatal_errors) |
| 1415 if succeeded: | 1439 fatal_error_log = preamble['step'].addLog('fatal_errors') |
| 1440 fatal_error_log.addStdout(fatal_errors_str) | |
| 1441 fatal_error_log.finish() | |
| 1442 self.annotate_status = builder.EXCEPTION | |
| 1443 if not self.cursorIsPreamble(): | |
| 1444 self.finishCursor(self.annotate_status, | |
| 1445 reason='Fatal errors: %s' % fatal_errors_str) | |
| 1446 elif return_code == 0: | |
| 1447 succeeded = True | |
| 1416 # Do not close the initial section because it will be closed by | 1448 # Do not close the initial section because it will be closed by |
| 1417 # self.finished when runCommand completes. | 1449 # self.finished when runCommand completes. |
| 1418 if not self.cursorIsPreamble(): | 1450 if not self.cursorIsPreamble(): |
| 1419 self.closeCursor() | 1451 self.closeCursor() |
| 1420 if self.honor_zero_return_code: | 1452 if self.honor_zero_return_code: |
| 1421 self.annotate_status = builder.SUCCESS | 1453 self.annotate_status = builder.SUCCESS |
| 1422 else: | 1454 else: |
| 1455 # Treat all non-zero return codes as failure. | |
| 1456 # We could have a special return code for warnings/exceptions, however, | |
| 1457 # this might conflict with some existing use of a return code. | |
| 1458 # Besides, applications can always intercept return codes and emit | |
| 1459 # STEP_* tags. | |
| 1460 | |
| 1423 # Only change annotate status if it'd otherwise indicate success. | 1461 # Only change annotate status if it'd otherwise indicate success. |
| 1424 # This helps propagate purple (exception) step status to to-level | 1462 # This helps propagate purple (exception) step status to to-level |
| 1425 # purple build status as opposed to red. | 1463 # purple build status as opposed to red. |
| 1426 if self.annotate_status in (builder.SUCCESS, builder.WARNINGS): | 1464 if self.annotate_status in (builder.SUCCESS, builder.WARNINGS): |
| 1427 self.annotate_status = builder.FAILURE | 1465 self.annotate_status = builder.FAILURE |
| 1428 if not self.cursorIsPreamble(): | 1466 if not self.cursorIsPreamble(): |
| 1429 self.finishCursor(self.annotate_status, | 1467 self.finishCursor(self.annotate_status, |
| 1430 reason='return code was %d.' % return_code) | 1468 reason='return code was %d.' % return_code) |
| 1431 self.cleanupSteps(exclude_async_pending=succeeded) | 1469 self.cleanupSteps(exclude_async_pending=succeeded) |
| 1432 | 1470 |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1543 and starts to wait for remaining steps to finish. Returns command_result | 1581 and starts to wait for remaining steps to finish. Returns command_result |
| 1544 as Deferred.""" | 1582 as Deferred.""" |
| 1545 self.scriptComplete(command_result) | 1583 self.scriptComplete(command_result) |
| 1546 steps_d = self.script_observer.waitForSteps() | 1584 steps_d = self.script_observer.waitForSteps() |
| 1547 # Ignore the waitForSteps' result and return the original result, | 1585 # Ignore the waitForSteps' result and return the original result, |
| 1548 # so the caller of runCommand receives command_result. | 1586 # so the caller of runCommand receives command_result. |
| 1549 steps_d.addCallback(lambda *_: command_result) | 1587 steps_d.addCallback(lambda *_: command_result) |
| 1550 return steps_d | 1588 return steps_d |
| 1551 d.addCallback(onCommandFinished) | 1589 d.addCallback(onCommandFinished) |
| 1552 return d | 1590 return d |
| OLD | NEW |