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

Side by Side Diff: master/skia_master_scripts/monkeypatches.py

Issue 648353002: Remove Skia's forked buildbot code (Closed) Base URL: https://skia.googlesource.com/buildbot.git@master
Patch Set: Address comment Created 6 years, 2 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
OLDNEW
(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
OLDNEW
« no previous file with comments | « master/skia_master_scripts/master_revision.py ('k') | master/skia_master_scripts/nacl_factory.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698