| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 | |
| 6 """ Monkeypatches to override upstream code. """ | |
| 7 | |
| 8 | |
| 9 from buildbot.changes.gitpoller import GitPoller | |
| 10 from buildbot.process.properties import Properties | |
| 11 from buildbot.schedulers.trysched import BadJobfile | |
| 12 from buildbot.status.builder import EXCEPTION, FAILURE | |
| 13 from buildbot.status.web import base as webstatus_base | |
| 14 from buildbot.status.web.status_json import BuilderJsonResource | |
| 15 from buildbot.status.web.status_json import BuildersJsonResource | |
| 16 from buildbot.status.web.status_json import ChangeSourcesJsonResource | |
| 17 from buildbot.status.web.status_json import JsonResource | |
| 18 from buildbot.status.web.status_json import JsonStatusResource | |
| 19 from buildbot.status.web.status_json import MetricsJsonResource | |
| 20 from buildbot.status.web.status_json import ProjectJsonResource | |
| 21 from buildbot.status.web.status_json import SlavesJsonResource | |
| 22 from master import build_utils | |
| 23 from master import chromium_notifier | |
| 24 from master import gatekeeper | |
| 25 from master import try_job_base | |
| 26 from master import try_job_rietveld | |
| 27 from master import try_job_svn | |
| 28 from master.try_job_base import text_to_dict | |
| 29 from twisted.internet import defer | |
| 30 from twisted.python import log | |
| 31 from twisted.web import server | |
| 32 from webstatus import builder_statuses | |
| 33 | |
| 34 import builder_name_schema | |
| 35 import config_private | |
| 36 import json | |
| 37 import master_revision | |
| 38 import re | |
| 39 import slave_hosts_cfg | |
| 40 import slaves_cfg | |
| 41 import skia_vars | |
| 42 import utils | |
| 43 | |
| 44 | |
| 45 # The following users are allowed to run trybots even though they do not have | |
| 46 # accounts in google.com or chromium.org | |
| 47 TRYBOTS_REQUESTER_WHITELIST = [ | |
| 48 'henrik.smiding@intel.com', | |
| 49 'kkinnunen@nvidia.com', | |
| 50 'cdalton@nvidia.com', | |
| 51 'ravimist@gmail.com' | |
| 52 ] | |
| 53 | |
| 54 | |
| 55 ################################################################################ | |
| 56 ############################# Trybot Monkeypatches ############################# | |
| 57 ################################################################################ | |
| 58 | |
| 59 | |
| 60 @defer.deferredGenerator | |
| 61 def SubmitTryJobChanges(self, changes): | |
| 62 """ Override of SVNPoller.submit_changes: | |
| 63 http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/master/try_job
_svn.py?view=markup | |
| 64 | |
| 65 We modify it so that the patch file url is added to the build properties. | |
| 66 This allows the slave to download the patch directly rather than receiving | |
| 67 it from the master. | |
| 68 """ | |
| 69 for chdict in changes: | |
| 70 # pylint: disable=E1101 | |
| 71 parsed = self.parent.parse_options(text_to_dict(chdict['comments'])) | |
| 72 | |
| 73 # 'fix' revision. | |
| 74 # LKGR must be known before creating the change object. | |
| 75 wfd = defer.waitForDeferred(self.parent.get_lkgr(parsed)) | |
| 76 yield wfd | |
| 77 wfd.getResult() | |
| 78 | |
| 79 wfd = defer.waitForDeferred(self.master.addChange( | |
| 80 author=','.join(parsed['email']), | |
| 81 revision=parsed['revision'], | |
| 82 comments='', | |
| 83 properties={'patch_file_url': chdict['repository'] + '/' + \ | |
| 84 chdict['files'][0]})) | |
| 85 yield wfd | |
| 86 change = wfd.getResult() | |
| 87 | |
| 88 self.parent.addChangeInner(chdict['files'], parsed, change.number) | |
| 89 | |
| 90 try_job_svn.SVNPoller.submit_changes = SubmitTryJobChanges | |
| 91 | |
| 92 | |
| 93 def TryJobCreateBuildset(self, ssid, parsed_job): | |
| 94 """ Override of TryJobBase.create_buildset: | |
| 95 http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/master/try_job
_base.py?view=markup | |
| 96 | |
| 97 We modify it to verify that the requested builders are in the builder pool for | |
| 98 this try scheduler. This prevents try requests from running on builders which | |
| 99 are not registered as trybots. This apparently isn't a problem for Chromium | |
| 100 since they use a separate try master. | |
| 101 """ | |
| 102 log.msg('Creating try job(s) %s' % ssid) | |
| 103 result = None | |
| 104 for builder in parsed_job['bot']: | |
| 105 if builder in self.pools[self.name]: | |
| 106 result = self.addBuildsetForSourceStamp(ssid=ssid, | |
| 107 reason=parsed_job['name'], | |
| 108 external_idstring=parsed_job['name'], | |
| 109 builderNames=[builder], | |
| 110 properties=self.get_props(builder, parsed_job)) | |
| 111 else: | |
| 112 log.msg('Scheduler: %s rejecting try job for builder: %s not in %s' % ( | |
| 113 self.name, | |
| 114 builder, | |
| 115 self.pools[self.name])) | |
| 116 log.msg('Returning buildset: %s' % result) | |
| 117 return result | |
| 118 | |
| 119 try_job_base.TryJobBase.create_buildset = TryJobCreateBuildset | |
| 120 | |
| 121 | |
| 122 def HtmlResourceRender(self, request): | |
| 123 """ Override of buildbot.status.web.base.HtmlResource.render: | |
| 124 http://src.chromium.org/viewvc/chrome/trunk/tools/build/third_party/buildbot_8
_4p1/buildbot/status/web/base.py?view=markup | |
| 125 | |
| 126 We modify it to pass additional variables on to the web status pages, and | |
| 127 remove the "if False" section. | |
| 128 """ | |
| 129 # tell the WebStatus about the HTTPChannel that got opened, so they | |
| 130 # can close it if we get reconfigured and the WebStatus goes away. | |
| 131 # They keep a weakref to this, since chances are good that it will be | |
| 132 # closed by the browser or by us before we get reconfigured. See | |
| 133 # ticket #102 for details. | |
| 134 if hasattr(request, "channel"): | |
| 135 # web.distrib.Request has no .channel | |
| 136 request.site.buildbot_service.registerChannel(request.channel) | |
| 137 | |
| 138 ctx = self.getContext(request) | |
| 139 | |
| 140 ############################## Added by borenet ############################## | |
| 141 status = self.getStatus(request) | |
| 142 all_builders = status.getBuilderNames() | |
| 143 all_full_category_names = set() | |
| 144 all_categories = set() | |
| 145 all_subcategories = set() | |
| 146 subcategories_by_category = {} | |
| 147 for builder_name in all_builders: | |
| 148 category_full = status.getBuilder(builder_name).category or 'default' | |
| 149 all_full_category_names.add(category_full) | |
| 150 category_split = category_full.split('|') | |
| 151 category = category_split[0] | |
| 152 subcategory = category_split[1] if len(category_split) > 1 else 'default' | |
| 153 all_categories.add(category) | |
| 154 all_subcategories.add(subcategory) | |
| 155 if not subcategories_by_category.get(category): | |
| 156 subcategories_by_category[category] = [] | |
| 157 if not subcategory in subcategories_by_category[category]: | |
| 158 subcategories_by_category[category].append(subcategory) | |
| 159 | |
| 160 ctx['tree_status_baseurl'] = \ | |
| 161 skia_vars.GetGlobalVariable('tree_status_baseurl') | |
| 162 | |
| 163 ctx['all_full_category_names'] = sorted(list(all_full_category_names)) | |
| 164 ctx['all_categories'] = sorted(list(all_categories)) | |
| 165 ctx['all_subcategories'] = sorted(list(all_subcategories)) | |
| 166 ctx['subcategories_by_category'] = subcategories_by_category | |
| 167 ctx['default_refresh'] = \ | |
| 168 skia_vars.GetGlobalVariable('default_webstatus_refresh') | |
| 169 ctx['skia_repo'] = config_private.SKIA_GIT_URL | |
| 170 | |
| 171 active_master = config_private.Master.get_active_master() | |
| 172 ctx['internal_port'] = active_master.master_port | |
| 173 ctx['external_port'] = active_master.master_port_alt | |
| 174 ctx['title_url'] = config_private.Master.Skia.project_url | |
| 175 ctx['slave_hosts_cfg'] = slave_hosts_cfg.SLAVE_HOSTS | |
| 176 ctx['slaves_cfg'] = slaves_cfg.SLAVES | |
| 177 | |
| 178 ctx['active_master_name'] = active_master.project_name | |
| 179 ctx['master_revision'] = utils.get_current_revision() | |
| 180 ctx['master_running_revision'] = active_master.running_revision | |
| 181 ctx['master_launch_datetime'] = active_master.launch_datetime | |
| 182 ctx['is_internal_view'] = request.host.port == ctx['internal_port'] | |
| 183 ctx['masters'] = [] | |
| 184 for master in config_private.Master.valid_masters: | |
| 185 ctx['masters'].append({ | |
| 186 'name': master.project_name, | |
| 187 'host': master.master_host, | |
| 188 'internal_port': master.master_port, | |
| 189 'external_port': master.master_port_alt, | |
| 190 }) | |
| 191 ############################################################################## | |
| 192 | |
| 193 d = defer.maybeDeferred(lambda : self.content(request, ctx)) | |
| 194 def handle(data): | |
| 195 if isinstance(data, unicode): | |
| 196 data = data.encode("utf-8") | |
| 197 request.setHeader("content-type", self.contentType) | |
| 198 if request.method == "HEAD": | |
| 199 request.setHeader("content-length", len(data)) | |
| 200 return '' | |
| 201 return data | |
| 202 d.addCallback(handle) | |
| 203 def ok(data): | |
| 204 request.write(data) | |
| 205 request.finish() | |
| 206 def fail(f): | |
| 207 request.processingFailed(f) | |
| 208 return None # processingFailed will log this for us | |
| 209 d.addCallbacks(ok, fail) | |
| 210 return server.NOT_DONE_YET | |
| 211 | |
| 212 webstatus_base.HtmlResource.render = HtmlResourceRender | |
| 213 | |
| 214 | |
| 215 class TryBuildersJsonResource(JsonResource): | |
| 216 """ Clone of buildbot.status.web.status_json.BuildersJsonResource: | |
| 217 http://src.chromium.org/viewvc/chrome/trunk/tools/build/third_party/buildbot_8
_4p1/buildbot/status/web/status_json.py?view=markup | |
| 218 | |
| 219 We add filtering to display only the try builders. | |
| 220 """ | |
| 221 help = """List of all the try builders defined on a master.""" | |
| 222 pageTitle = 'Builders' | |
| 223 | |
| 224 def __init__(self, status, include_only_cq_trybots=False): | |
| 225 JsonResource.__init__(self, status) | |
| 226 for builder_name in self.status.getBuilderNames(): | |
| 227 if builder_name_schema.IsTrybot(builder_name) and ( | |
| 228 not include_only_cq_trybots or builder_name in slaves_cfg.CQ_TRYBOTS): | |
| 229 self.putChild(builder_name, | |
| 230 BuilderJsonResource(status, | |
| 231 status.getBuilder(builder_name))) | |
| 232 | |
| 233 | |
| 234 class CQRequiredStepsJsonResource(JsonResource): | |
| 235 help = 'List the steps which cannot fail on the commit queue.' | |
| 236 pageTitle = 'CQ Required Steps' | |
| 237 | |
| 238 def asDict(self, request): | |
| 239 return {'cq_required_steps': | |
| 240 skia_vars.GetGlobalVariable('cq_required_steps')} | |
| 241 | |
| 242 | |
| 243 def JsonStatusResourceInit(self, status): | |
| 244 """ Override of buildbot.status.web.status_json.JsonStatusResource.__init__: | |
| 245 http://src.chromium.org/viewvc/chrome/trunk/tools/build/third_party/buildbot_8
_4p1/buildbot/status/web/status_json.py?view=markup | |
| 246 | |
| 247 We add trybots, cqtrybots, cq_required_steps (details below). | |
| 248 """ | |
| 249 JsonResource.__init__(self, status) | |
| 250 self.level = 1 | |
| 251 self.putChild('builders', BuildersJsonResource(status)) | |
| 252 self.putChild('change_sources', ChangeSourcesJsonResource(status)) | |
| 253 self.putChild('project', ProjectJsonResource(status)) | |
| 254 self.putChild('slaves', SlavesJsonResource(status)) | |
| 255 self.putChild('metrics', MetricsJsonResource(status)) | |
| 256 | |
| 257 ############################## Added by borenet ############################## | |
| 258 # Added to address: https://code.google.com/p/skia/issues/detail?id=1134 | |
| 259 self.putChild('trybots', TryBuildersJsonResource(status)) | |
| 260 ############################################################################## | |
| 261 ############################## Added by rmistry ############################## | |
| 262 # Added to have a place to get the list of trybots run by the CQ. | |
| 263 self.putChild('cqtrybots', | |
| 264 TryBuildersJsonResource(status, include_only_cq_trybots=True)) | |
| 265 ############################################################################## | |
| 266 ############################## Added by borenet ############################## | |
| 267 # Added to have a place to get the list of steps which cannot fail on the CQ. | |
| 268 self.putChild('cq_required_steps', CQRequiredStepsJsonResource(status)) | |
| 269 ############################################################################## | |
| 270 | |
| 271 ############################## Added by borenet ############################## | |
| 272 # Added to have a way to determine which code revision the master is running. | |
| 273 self.putChild('master_revision', | |
| 274 master_revision.MasterCheckedOutRevisionJsonResource(status)) | |
| 275 running_rev = config_private.Master.get_active_master().running_revision | |
| 276 self.putChild('master_running_revision', | |
| 277 master_revision.MasterRunningRevisionJsonResource( | |
| 278 status=status, running_revision=running_rev)) | |
| 279 | |
| 280 # This page gives the result of the most recent build for each builder. | |
| 281 self.putChild('builder_statuses', | |
| 282 builder_statuses.BuilderStatusesJsonResource(status)) | |
| 283 ############################################################################## | |
| 284 | |
| 285 # This needs to be called before the first HelpResource().body call. | |
| 286 self.hackExamples() | |
| 287 | |
| 288 JsonStatusResource.__init__ = JsonStatusResourceInit | |
| 289 | |
| 290 | |
| 291 @defer.deferredGenerator | |
| 292 def TryJobRietveldSubmitJobs(self, jobs): | |
| 293 """ Override of master.try_job_rietveld.TryJobRietveld.SubmitJobs: | |
| 294 http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/master/try_job
_rietveld.py?view=markup | |
| 295 | |
| 296 We modify it to include "baseurl" as a build property. | |
| 297 """ | |
| 298 log.msg('TryJobRietveld.SubmitJobs: %s' % json.dumps(jobs, indent=2)) | |
| 299 for job in jobs: | |
| 300 try: | |
| 301 # Gate the try job on the user that requested the job, not the one that | |
| 302 # authored the CL. | |
| 303 # pylint: disable=W0212 | |
| 304 ########################## Added by rmistry ########################## | |
| 305 if (job.get('requester') and not job['requester'].endswith('@google.com') | |
| 306 and not job['requester'].endswith('@chromium.org') | |
| 307 and not job['requester'] in TRYBOTS_REQUESTER_WHITELIST): | |
| 308 # Reject the job only if the requester has an email not ending in | |
| 309 # google.com or chromium.org | |
| 310 raise BadJobfile( | |
| 311 'TryJobRietveld rejecting job from %s' % job['requester']) | |
| 312 ###################################################################### | |
| 313 ########################## Added by borenet ########################## | |
| 314 if not (job.get('baseurl') and | |
| 315 config_private.Master.Skia.project_name.lower() in | |
| 316 job['baseurl']): | |
| 317 raise BadJobfile('TryJobRietveld rejecting job with unknown baseurl: %s' | |
| 318 % job.get('baseurl')) | |
| 319 ###################################################################### | |
| 320 if job['email'] != job['requester']: | |
| 321 # Note the fact the try job was requested by someone else in the | |
| 322 # 'reason'. | |
| 323 job['reason'] = job.get('reason') or '' | |
| 324 if job['reason']: | |
| 325 job['reason'] += '; ' | |
| 326 job['reason'] += "This CL was triggered by %s" % job['requester'] | |
| 327 | |
| 328 options = { | |
| 329 'bot': {job['builder']: job['tests']}, | |
| 330 'email': [job['email']], | |
| 331 'project': [self._project], | |
| 332 'try_job_key': job['key'], | |
| 333 } | |
| 334 # Transform some properties as is expected by parse_options(). | |
| 335 for key in ( | |
| 336 ########################## Added by borenet ########################## | |
| 337 'baseurl', | |
| 338 ###################################################################### | |
| 339 'name', 'user', 'root', 'reason', 'clobber', 'patchset', | |
| 340 'issue', 'requester', 'revision'): | |
| 341 options[key] = [job[key]] | |
| 342 | |
| 343 # Now cleanup the job dictionary and submit it. | |
| 344 cleaned_job = self.parse_options(options) | |
| 345 | |
| 346 wfd = defer.waitForDeferred(self.get_lkgr(cleaned_job)) | |
| 347 yield wfd | |
| 348 wfd.getResult() | |
| 349 | |
| 350 wfd = defer.waitForDeferred(self.master.addChange( | |
| 351 author=','.join(cleaned_job['email']), | |
| 352 # TODO(maruel): Get patchset properties to get the list of files. | |
| 353 # files=[], | |
| 354 revision=cleaned_job['revision'], | |
| 355 comments='')) | |
| 356 yield wfd | |
| 357 changeids = [wfd.getResult().number] | |
| 358 | |
| 359 wfd = defer.waitForDeferred(self.SubmitJob(cleaned_job, changeids)) | |
| 360 yield wfd | |
| 361 wfd.getResult() | |
| 362 except BadJobfile, e: | |
| 363 # We need to mark it as failed otherwise it'll stay in the pending | |
| 364 # state. Simulate a buildFinished event on the build. | |
| 365 if not job.get('key'): | |
| 366 log.err( | |
| 367 'Got %s for issue %s but not key, not updating Rietveld' % | |
| 368 (e, job.get('issue'))) | |
| 369 continue | |
| 370 log.err( | |
| 371 'Got %s for issue %s, updating Rietveld' % (e, job.get('issue'))) | |
| 372 for service in self.master.services: | |
| 373 if service.__class__.__name__ == 'TryServerHttpStatusPush': | |
| 374 # pylint: disable=W0212,W0612 | |
| 375 build = { | |
| 376 'properties': [ | |
| 377 ('buildername', job.get('builder'), None), | |
| 378 ('buildnumber', -1, None), | |
| 379 ('issue', job['issue'], None), | |
| 380 ('patchset', job['patchset'], None), | |
| 381 ('project', self._project, None), | |
| 382 ('revision', '', None), | |
| 383 ('slavename', '', None), | |
| 384 ('try_job_key', job['key'], None), | |
| 385 ], | |
| 386 'reason': job.get('reason', ''), | |
| 387 # Use EXCEPTION until SKIPPED results in a non-green try job | |
| 388 # results on Rietveld. | |
| 389 'results': EXCEPTION, | |
| 390 } | |
| 391 ########################## Added by rmistry ######################### | |
| 392 # Do not update Rietveld to mark the try job request as failed. | |
| 393 # See https://code.google.com/p/chromium/issues/detail?id=224014 for | |
| 394 # more context. | |
| 395 # service.push('buildFinished', build=build) | |
| 396 ##################################################################### | |
| 397 break | |
| 398 | |
| 399 try_job_rietveld.TryJobRietveld.SubmitJobs = TryJobRietveldSubmitJobs | |
| 400 | |
| 401 | |
| 402 def TryJobBaseGetProps(self, builder, options): | |
| 403 """ Override of try_job_base.TryJobBase.get_props: | |
| 404 http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/master/try_job
_base.py?view=markup | |
| 405 | |
| 406 We modify it to add "baseurl". | |
| 407 """ | |
| 408 keys = ( | |
| 409 ############################### Added by borenet ############################### | |
| 410 'baseurl', | |
| 411 ################################################################################ | |
| 412 'clobber', | |
| 413 'issue', | |
| 414 'patchset', | |
| 415 'requester', | |
| 416 'rietveld', | |
| 417 'root', | |
| 418 'try_job_key', | |
| 419 ) | |
| 420 # All these settings have no meaning when False or not set, so don't set | |
| 421 # them in that case. | |
| 422 properties = dict((i, options[i]) for i in keys if options.get(i)) | |
| 423 properties['testfilter'] = options['bot'].get(builder, None) | |
| 424 # pylint: disable=W0212 | |
| 425 props = Properties() | |
| 426 props.updateFromProperties(self.properties) | |
| 427 props.update(properties, self._PROPERTY_SOURCE) | |
| 428 return props | |
| 429 | |
| 430 try_job_base.TryJobBase.get_props = TryJobBaseGetProps | |
| 431 | |
| 432 | |
| 433 def TryJobRietveldConstructor( | |
| 434 self, name, pools, properties=None, last_good_urls=None, | |
| 435 code_review_sites=None, project=None): | |
| 436 try_job_base.TryJobBase.__init__(self, name, pools, properties, | |
| 437 last_good_urls, code_review_sites) | |
| 438 # pylint: disable=W0212 | |
| 439 endpoint = self._GetRietveldEndPointForProject(code_review_sites, project) | |
| 440 ############################### Added by rmistry ############################### | |
| 441 # rmistry: Adding '&master=tryserver.skia' to the endpoint to help filter the | |
| 442 # number of pending try patchsets returned. More details are in | |
| 443 # https://code.google.com/p/skia/issues/detail?id=2659 | |
| 444 endpoint += '&master=tryserver.skia' | |
| 445 # rmistry: Increased the polling time from 10 seconds to 1 min because 10 | |
| 446 # seconds is too short for us. The RietveldPoller stops working if the time is | |
| 447 # too short. | |
| 448 # pylint: disable=W0212 | |
| 449 self._poller = try_job_rietveld._RietveldPoller(endpoint, interval=60) | |
| 450 ################################################################################ | |
| 451 # pylint: disable=W0212 | |
| 452 self._valid_users = try_job_rietveld._ValidUserPoller(interval=12 * 60 * 60) | |
| 453 self._project = project | |
| 454 log.msg('TryJobRietveld created, get_pending_endpoint=%s ' | |
| 455 'project=%s' % (endpoint, project)) | |
| 456 | |
| 457 try_job_rietveld.TryJobRietveld.__init__ = TryJobRietveldConstructor | |
| 458 | |
| 459 | |
| 460 class SkiaGateKeeper(gatekeeper.GateKeeper): | |
| 461 | |
| 462 def isInterestingBuilder(self, builder_status): | |
| 463 """ Override of gatekeeper.GateKeeper.isInterestingBuilder: | |
| 464 http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/master/gatek
eeper.py?view=markup | |
| 465 | |
| 466 We modify it to actually check whether the builder should be considered by | |
| 467 the GateKeeper, as indicated in its category name. | |
| 468 """ | |
| 469 | |
| 470 ret = (not builder_name_schema.IsTrybot(builder_status.getName()) and | |
| 471 chromium_notifier.ChromiumNotifier.isInterestingBuilder(self, | |
| 472 builder_status)) | |
| 473 log.msg('[gatekeeper-debug2] ======================') | |
| 474 log.msg('[gatekeeper-debug2] is not trybot: %s' % ( | |
| 475 not builder_name_schema.IsTrybot(builder_status.getName()))) | |
| 476 log.msg('[gatekeeper-debug2] isInterestingBuilder: %s' % ( | |
| 477 chromium_notifier.ChromiumNotifier.isInterestingBuilder( | |
| 478 self, builder_status))) | |
| 479 log.msg('[gatekeeper-debug2] builder_status.getName(): %s' % ( | |
| 480 builder_status.getName())) | |
| 481 log.msg('[gatekeeper-debug2] ret: %s' % ret) | |
| 482 log.msg('[gatekeeper-debug2] ======================') | |
| 483 return ret | |
| 484 | |
| 485 def isInterestingStep(self, build_status, step_status, results): | |
| 486 """ Override of gatekeeper.GateKeeper.isInterestingStep: | |
| 487 http://src.chromium.org/viewvc/chrome/trunk/tools/build/scripts/master/gatek
eeper.py?view=markup | |
| 488 | |
| 489 We modify it to comment out the SVN revision comparision to determine if the | |
| 490 current build is older because Skia uses commit hashes. | |
| 491 """ | |
| 492 # If we have not failed, or are not interested in this builder, | |
| 493 # then we have nothing to do. | |
| 494 if results[0] != FAILURE: | |
| 495 return False | |
| 496 | |
| 497 # Check if the slave is still alive. We should not close the tree for | |
| 498 # inactive slaves. | |
| 499 slave_name = build_status.getSlavename() | |
| 500 if slave_name in self.master_status.getSlaveNames(): | |
| 501 # @type self.master_status: L{buildbot.status.builder.Status} | |
| 502 # @type self.parent: L{buildbot.master.BuildMaster} | |
| 503 # @rtype getSlave(): L{buildbot.status.builder.SlaveStatus} | |
| 504 slave_status = self.master_status.getSlave(slave_name) | |
| 505 if slave_status and not slave_status.isConnected(): | |
| 506 log.msg('[gatekeeper] Slave %s was disconnected, ' | |
| 507 'not closing the tree' % slave_name) | |
| 508 return False | |
| 509 | |
| 510 # If the previous build step failed with the same result, we don't care | |
| 511 # about this step. | |
| 512 previous_build_status = build_status.getPreviousBuild() | |
| 513 if previous_build_status: | |
| 514 step_name = self.getName(step_status) | |
| 515 step_type = self.getGenericName(step_name) | |
| 516 previous_steps = [step for step in previous_build_status.getSteps() | |
| 517 if self.getGenericName(self.getName(step)) == step_type] | |
| 518 if len(previous_steps) == 1: | |
| 519 if previous_steps[0].getResults()[0] == FAILURE: | |
| 520 log.msg('[gatekeeper] Slave %s failed, but previously failed on ' | |
| 521 'the same step (%s). So not closing tree.' % ( | |
| 522 (step_name, slave_name))) | |
| 523 return False | |
| 524 else: | |
| 525 log.msg('[gatekeeper] len(previous_steps) == %d which is weird' % | |
| 526 len(previous_steps)) | |
| 527 | |
| 528 # If check_revisions=False that means that the tree closure request is | |
| 529 # coming from nightly scheduled bots, that need not necessarily have the | |
| 530 # revision info. | |
| 531 if not self.check_revisions: | |
| 532 return True | |
| 533 | |
| 534 # If we don't have a version stamp nor a blame list, then this is most | |
| 535 # likely a build started manually, and we don't want to close the | |
| 536 # tree. | |
| 537 latest_revision = build_utils.getLatestRevision(build_status) | |
| 538 if not latest_revision or not build_status.getResponsibleUsers(): | |
| 539 log.msg('[gatekeeper] Slave %s failed, but no version stamp, ' | |
| 540 'so skipping.' % slave_name) | |
| 541 return False | |
| 542 | |
| 543 # If the tree is open, we don't want to close it again for the same | |
| 544 # revision, or an earlier one in case the build that just finished is a | |
| 545 # slow one and we already fixed the problem and manually opened the tree. | |
| 546 ############################### Added by rmistry ########################### | |
| 547 # rmistry: Commenting out the below SVN revision comparision because Skia | |
| 548 # uses commit hashes. | |
| 549 # TODO(rmistry): Figure out how to ensure that previous builds do not close | |
| 550 # the tree again. | |
| 551 # | |
| 552 # if latest_revision <= self._last_closure_revision: | |
| 553 # log.msg('[gatekeeper] Slave %s failed, but we already closed it ' | |
| 554 # 'for a previous revision (old=%s, new=%s)' % ( | |
| 555 # slave_name, str(self._last_closure_revision), | |
| 556 # str(latest_revision))) | |
| 557 # return False | |
| 558 ########################################################################### | |
| 559 | |
| 560 log.msg('[gatekeeper] Decided to close tree because of slave %s ' | |
| 561 'on revision %s' % (slave_name, str(latest_revision))) | |
| 562 | |
| 563 # Up to here, in theory we'd check if the tree is closed but this is too | |
| 564 # slow to check here. Instead, take a look only when we want to close the | |
| 565 # tree. | |
| 566 return True | |
| 567 | |
| 568 | |
| 569 # Fix try_job_base.TryJobBase._EMAIL_VALIDATOR to handle *.info. This was fixed | |
| 570 # in https://codereview.chromium.org/216293005 but we need this monkeypatch to | |
| 571 # pick it up without a DEPS roll. | |
| 572 try_job_base.TryJobBase._EMAIL_VALIDATOR = re.compile( | |
| 573 r'[a-zA-Z0-9][a-zA-Z0-9\.\+\-\_]*@[a-zA-Z0-9\.\-]+\.[a-zA-Z]{2,}$') | |
| 574 | |
| 575 | |
| 576 # Add logging to GitPoller._stop_on_failure | |
| 577 def _stop_on_failure(self, f): | |
| 578 "utility method to stop the service when a failure occurs" | |
| 579 log.err('GitPoller stopping due to failure: %s' % str(f)) | |
| 580 if self.running: | |
| 581 d = defer.maybeDeferred(lambda : self.stopService()) | |
| 582 d.addErrback(log.err, 'while stopping broken GitPoller service') | |
| 583 return f | |
| 584 | |
| 585 GitPoller._stop_on_failure = _stop_on_failure | |
| OLD | NEW |