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

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

Issue 968053003: BuildBucket-based build triggering (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: added build_url Created 5 years, 9 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
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
11 import logging 11 import logging
12 import os 12 import os
13 import time 13 import time
14 14
15 from twisted.internet import defer 15 from twisted.internet import defer
16 from twisted.python import log 16 from twisted.python import log
17 17
18 from buildbot import interfaces, util 18 from buildbot import interfaces, util
19 from buildbot.changes.changes import Change 19 from buildbot.changes.changes import Change
20 from buildbot.process import buildstep 20 from buildbot.process import buildstep
21 from buildbot.process.properties import WithProperties 21 from buildbot.process.properties import WithProperties
22 from buildbot.status import builder 22 from buildbot.status import builder
23 from buildbot.steps import shell 23 from buildbot.steps import shell
24 from buildbot.steps import source 24 from buildbot.steps import source
25 import sqlalchemy as sa 25 import sqlalchemy as sa
26 26
27 from common import annotator 27 from common import annotator
28 from master import buildbucket
28 from common import chromium_utils 29 from common import chromium_utils
29 import config 30 import config
30 31
31 32
32 def change_to_revision(c): 33 def change_to_revision(c):
33 """Handle revision == None or any invalid value.""" 34 """Handle revision == None or any invalid value."""
34 try: 35 try:
35 return int(str(c.revision).split('@')[-1]) 36 return int(str(c.revision).split('@')[-1])
36 except (ValueError, TypeError): 37 except (ValueError, TypeError):
37 return 0 38 return 0
(...skipping 595 matching lines...) Expand 10 before | Expand all | Expand 10 after
633 self.annotate_status = builder.SUCCESS 634 self.annotate_status = builder.SUCCESS
634 self.halt_on_failure = False 635 self.halt_on_failure = False
635 self.honor_zero_return_code = False 636 self.honor_zero_return_code = False
636 self.cursor = None 637 self.cursor = None
637 638
638 self.show_perf = show_perf 639 self.show_perf = show_perf
639 self.perf_id = perf_id 640 self.perf_id = perf_id
640 self.perf_report_url_suffix = perf_report_url_suffix 641 self.perf_report_url_suffix = perf_report_url_suffix
641 self.target = target 642 self.target = target
642 self.active_master = active_master 643 self.active_master = active_master
644 self.bb_triggering_service = None
643 645
644 def initialSection(self): 646 def initialSection(self):
645 """Initializes the annotator's sections. 647 """Initializes the annotator's sections.
646 648
647 Annotator uses a list of dictionaries which hold information stuch as status 649 Annotator uses a list of dictionaries which hold information stuch as status
648 and logs for each added step. This method populates the section list with an 650 and logs for each added step. This method populates the section list with an
649 entry referencing the original buildbot step.""" 651 entry referencing the original buildbot step."""
650 if self.sections: 652 if self.sections:
651 return 653 return
652 # Add a log section for output before the first section heading. 654 # Add a log section for output before the first section heading.
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after
781 section['closed'] = True 783 section['closed'] = True
782 if section['step'].isFinished(): 784 if section['step'].isFinished():
783 return 785 return
784 786
785 # Everything was fine on the slave side, 787 # Everything was fine on the slave side,
786 # so the final result depends on async operations. 788 # so the final result depends on async operations.
787 async_ops = section['async_ops'] 789 async_ops = section['async_ops']
788 if async_ops: 790 if async_ops:
789 op_list = '\n'.join('* %s' % o['description'] 791 op_list = '\n'.join('* %s' % o['description']
790 for o in async_ops) 792 for o in async_ops)
791 msg = 'Will wait till async operations complete:\n%s' % (op_list,) 793 msg = 'Will wait till async operations complete:\n%s\n\n' % (op_list,)
792 section['log'].addStdout(msg) 794 section['log'].addStdout(msg)
793 795
794 d = defer.DeferredList([o['deferred'] for o in async_ops]) 796 d = defer.DeferredList([o['deferred'] for o in async_ops])
795 def finish(results): 797 def finish(results):
796 try: 798 try:
797 reasons = [] 799 reasons = []
798 status = section['status'] 800 status = section['status']
799 for succeeded, defer_result in results: 801 for succeeded, defer_result in results:
800 if succeeded: 802 if succeeded:
801 # callback was called. 803 # callback was called.
(...skipping 369 matching lines...) Expand 10 before | Expand all | Expand 10 after
1171 files = change.get('files') 1173 files = change.get('files')
1172 if files: 1174 if files:
1173 change['files'] = sorted(files) 1175 change['files'] = sorted(files)
1174 1176
1175 return change 1177 return change
1176 1178
1177 def STEP_TRIGGER(self, spec): 1179 def STEP_TRIGGER(self, spec):
1178 # Support: @@@STEP_TRIGGER <json spec>@@@ (trigger build(s)). 1180 # Support: @@@STEP_TRIGGER <json spec>@@@ (trigger build(s)).
1179 try: 1181 try:
1180 spec = json.loads(spec) 1182 spec = json.loads(spec)
1183 bucket = spec.get('bucket')
1181 builder_names = spec.get('builderNames') 1184 builder_names = spec.get('builderNames')
1182 1185 properties = spec.get('properties') or {}
1183 changes = spec.get('changes') 1186 changes = spec.get('changes')
1184 if changes: 1187 if changes:
1185 assert isinstance(changes, list) 1188 assert isinstance(changes, list)
1186 changes = map(self.normalizeChangeSpec, changes) 1189 changes = map(self.normalizeChangeSpec, changes)
1187 1190
1188 if not builder_names: 1191 if not builder_names:
1189 raise ValueError('builderNames is not specified: %r' % (spec,)) 1192 raise ValueError('builderNames is not specified: %r' % (spec,))
1190 1193
1191 # Start builds. 1194 build = self.command.build
1192 d = self.triggerBuilds(builder_names, spec.get('properties') or {}, 1195 build_is_from_buildbucket = bool(
1193 changes) 1196 build.getProperties().getProperty(buildbucket.common.INFO_PROPERTY))
1197 trigger_via_buildbucket = bucket or build_is_from_buildbucket
1198
1199 if trigger_via_buildbucket:
1200 d = self.triggerBuildsViaBuildBucket(
1201 bucket, builder_names, properties, changes)
1202 else:
1203 d = self.triggerBuildsAsBuildsets(builder_names, properties, changes)
ghost stip (do not use) 2015/03/04 23:07:31 can you rename this to triggerBuildsLocally() inst
nodir 2015/03/04 23:52:10 Done
1194 # addAsyncOpToCursor expects a deferred to return a build result. If a 1204 # addAsyncOpToCursor expects a deferred to return a build result. If a
1195 # buildset is added, then it is a success. This lambda function returns a 1205 # buildset is added, then it is a success. This lambda function returns a
1196 # tuple, which is received by addAsyncOpToCursor. 1206 # tuple, which is received by addAsyncOpToCursor.
1197 d.addCallback(lambda _: (builder.SUCCESS, None)) 1207 d.addCallback(lambda _: (builder.SUCCESS, None))
1198 description = 'Triggering build(s) on %s' % (', '.join(builder_names),) 1208 description = 'Triggering build(s) on %s' % (', '.join(builder_names),)
1199 self.addAsyncOpToCursor(d, description) 1209 self.addAsyncOpToCursor(d, description)
1200 except Exception as ex: 1210 except Exception as ex:
1201 self.finishStep(self.cursor, builder.FAILURE, ex) 1211 self.finishStep(self.cursor, builder.FAILURE, ex)
1202 1212
1203 @staticmethod 1213 @staticmethod
(...skipping 12 matching lines...) Expand all
1216 """Inserts a new SourceStamp. 1226 """Inserts a new SourceStamp.
1217 1227
1218 For each change in changes_spec, finds an existing or creates a new Change 1228 For each change in changes_spec, finds an existing or creates a new Change
1219 object. Then creates a SourceStamp with these changes. 1229 object. Then creates a SourceStamp with these changes.
1220 1230
1221 Args: 1231 Args:
1222 master: an instance of buildbot.master.BuildMaster. 1232 master: an instance of buildbot.master.BuildMaster.
1223 changes_spec (list of dict): a list of change dicts, where each contains 1233 changes_spec (list of dict): a list of change dicts, where each contains
1224 keyword arguments for 1234 keyword arguments for
1225 buildbot.db.changes.ChangesConnectorComponent.addChange() function, 1235 buildbot.db.changes.ChangesConnectorComponent.addChange() function,
1226 except when_timestamp is int instead of datetime. The first change 1236 except when_timestamp is int (seconds since Unix Epoch) instead of
1227 is used to populate source stamp properties. 1237 datetime. The first change is used to populate source stamp properties.
1228 """ 1238 """
1229 def find_changes_by_revision(revision): 1239 def find_changes_by_revision(revision):
1230 """Searches for Changes in db by |revision| and returns change ids.""" 1240 """Searches for Changes in db by |revision| and returns change ids."""
1231 def find(conn): 1241 def find(conn):
1232 table = master.db.model.changes 1242 table = master.db.model.changes
1233 q = sa.select([table.c.changeid]).where(table.c.revision == revision) 1243 q = sa.select([table.c.changeid]).where(table.c.revision == revision)
1234 return [c.changeid for c in conn.execute(q)] 1244 return [c.changeid for c in conn.execute(q)]
1235 return master.db.pool.do(find) 1245 return master.db.pool.do(find)
1236 1246
1237 @defer.inlineCallbacks 1247 @defer.inlineCallbacks
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
1288 ssid = yield master.db.sourcestamps.addSourceStamp( 1298 ssid = yield master.db.sourcestamps.addSourceStamp(
1289 branch=main_change.branch, 1299 branch=main_change.branch,
1290 revision=main_change.revision, 1300 revision=main_change.revision,
1291 repository=main_change.repository, 1301 repository=main_change.repository,
1292 project=main_change.project, 1302 project=main_change.project,
1293 changeids=[c.number for c in changes], 1303 changeids=[c.number for c in changes],
1294 ) 1304 )
1295 defer.returnValue(ssid) 1305 defer.returnValue(ssid)
1296 1306
1297 @defer.inlineCallbacks 1307 @defer.inlineCallbacks
1298 def triggerBuilds(self, builder_names, properties, changes=None): 1308 def triggerBuildsViaBuildBucket(
1309 self, bucket_name, builder_names, properties, changes_spec=None):
1310 """Schedules builds on buildbucket."""
1311 if self.active_master is None:
1312 raise buildbucket.Error(
1313 'In order to trigger builds through buildbucket, '
1314 'ActiveMaster must be passed to AnnotatorFactory')
1315 build = self.command.build
1316 section = self.cursor
1317 if not self.bb_triggering_service:
ghost stip (do not use) 2015/03/04 23:07:30 ok, so the service is only instantiated when somet
nodir 2015/03/04 23:52:10 Yes, when something is actually triggered through
1318 self.bb_triggering_service = yield (
1319 buildbucket.trigger.get_triggering_service(self.active_master))
1320 changes = map(
1321 buildbucket.trigger.change_from_change_spec, changes_spec or [])
1322 for builder_name in builder_names:
1323 result = yield self.bb_triggering_service.trigger(
1324 build, bucket_name, builder_name, properties, changes)
1325 response = result['response']
1326 section['log'].addStdout(
1327 'BuildBucket.put API response: %s\n' % json.dumps(response, indent=4))
1328 build_url = result.get('build_url')
1329 if build_url:
1330 section['log'].addStdout('See %s\n\n' % build_url)
1331
1332
1333 @defer.inlineCallbacks
1334 def triggerBuildsAsBuildsets(
ghost stip (do not use) 2015/03/04 23:07:30 triggerBuildsLocally
nodir 2015/03/04 23:52:10 Done
1335 self, builder_names, properties, changes_spec=None):
1299 """Creates a new buildset.""" 1336 """Creates a new buildset."""
1300 build = self.command.build 1337 build = self.command.build
1301 master = build.builder.botmaster.parent 1338 master = build.builder.botmaster.parent
1302 current_properties = build.getProperties() 1339 current_properties = build.getProperties()
1303 1340
1304 if changes: 1341 if changes_spec:
1305 # Changes have been specified explicitly. 1342 # Changes have been specified explicitly.
1306 ssid = yield self.insertSourceStamp(master, changes) 1343 ssid = yield self.insertSourceStamp(master, changes_spec)
1307 else: 1344 else:
1308 # Use the same source stamp. 1345 # Use the same source stamp.
1309 source_stamp = build.getSourceStamp() 1346 source_stamp = build.getSourceStamp()
1310 if source_stamp.revision is None: 1347 if source_stamp.revision is None:
1311 revision = current_properties.getProperty('got_revision') 1348 revision = current_properties.getProperty('got_revision')
1312 if revision: 1349 if revision:
1313 # The source stamp is relative, but we have the "got_revision", so 1350 # The source stamp is relative, but we have the "got_revision", so
1314 # make it absolute. 1351 # make it absolute.
1315 source_stamp = source_stamp.getAbsoluteSourceStamp(revision) 1352 source_stamp = source_stamp.getAbsoluteSourceStamp(revision)
1316 else: 1353 else:
(...skipping 151 matching lines...) Expand 10 before | Expand all | Expand 10 after
1468 and starts to wait for remaining steps to finish. Returns command_result 1505 and starts to wait for remaining steps to finish. Returns command_result
1469 as Deferred.""" 1506 as Deferred."""
1470 self.scriptComplete(command_result) 1507 self.scriptComplete(command_result)
1471 steps_d = self.script_observer.waitForSteps() 1508 steps_d = self.script_observer.waitForSteps()
1472 # Ignore the waitForSteps' result and return the original result, 1509 # Ignore the waitForSteps' result and return the original result,
1473 # so the caller of runCommand receives command_result. 1510 # so the caller of runCommand receives command_result.
1474 steps_d.addCallback(lambda *_: command_result) 1511 steps_d.addCallback(lambda *_: command_result)
1475 return steps_d 1512 return steps_d
1476 d.addCallback(onCommandFinished) 1513 d.addCallback(onCommandFinished)
1477 return d 1514 return d
OLDNEW
« scripts/master/buildbucket/trigger.py ('K') | « scripts/master/buildbucket/trigger.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698