Skip to content

Commit

Permalink
Merge remote-tracking branch 'buildbot/master' into reporter
Browse files Browse the repository at this point in the history
Conflicts:
	master/docs/manual/cfg-statustargets.rst
  • Loading branch information
Pierre Tardy committed May 16, 2015
2 parents 815990c + 32f66f7 commit 924c44f
Show file tree
Hide file tree
Showing 95 changed files with 7,955 additions and 858 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pep8:
frontend:
$(PIP) install -e pkg
$(PIP) install mock
for i in www/*/; do $(PIP) install -e $$i ; done
for i in base codeparameter console_view waterfall_view; do $(PIP) install -e www/$$i ; done

# do installation tests. Test front-end can build and install for all install methods
frontend_install_tests:
Expand Down
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Related repositories:
* https://github.com/buildbot/buildbot-media - Buildbot-related media
* https://github.com/buildbot/buildbot-website - Source for http://buildbot.net

.. |travis-badge| image:: https://travis-ci.org/buildbot/buildbot.png?branch=master
.. |travis-badge| image:: https://travis-ci.org/buildbot/buildbot.svg?branch=master
.. _travis-badge: https://travis-ci.org/buildbot/buildbot
.. |coveralls-badge| image:: https://img.shields.io/coveralls/buildbot/buildbot.svg
.. _coveralls-badge: https://coveralls.io/r/buildbot/buildbot?branch=master
3 changes: 2 additions & 1 deletion master/buildbot/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,8 @@ def load_www(self, filename, config_dict):
allowed = set(['port', 'debug', 'json_cache_seconds',
'rest_minimum_version', 'allowed_origins', 'jsonp',
'plugins', 'auth', 'avatar_methods', 'logfileName',
'logRotateLength', 'maxRotatedFiles', 'versions'])
'logRotateLength', 'maxRotatedFiles', 'versions',
'change_hook_dialects', 'change_hook_auth'])
unknown = set(www_cfg.iterkeys()) - allowed
if unknown:
error("unknown www configuration parameter(s) %s" %
Expand Down
36 changes: 36 additions & 0 deletions master/buildbot/data/buildrequests.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from buildbot.db.buildrequests import AlreadyClaimedError

from buildbot.db.buildrequests import NotClaimedError
from buildbot.status import results

from twisted.internet import defer
from twisted.internet import reactor

Expand Down Expand Up @@ -62,6 +64,40 @@ def startConsuming(self, callback, options, kwargs):
return self.master.mq.startConsuming(callback,
('buildrequests', buildrequestid, None))

@defer.inlineCallbacks
def control(self, action, args, kwargs):
if action != "cancel":
raise ValueError("action: {} is not supported".format(action))
brid = kwargs['buildrequestid']
# first, try to claim the request; if this fails, then it's too late to
# cancel the build anyway
try:
b = yield self.master.db.buildrequests.claimBuildRequests(brids=[brid])
except AlreadyClaimedError:
# XXX race condition
# - After a buildrequest was claimed, and
# - Before creating a build,
# the claiming master still
# needs to do some processing, (send a message to the message queue,
# call maybeStartBuild on the related builder).
# In that case we won't have the related builds here. We don't have
# an alternative to letting them run without stopping them for now.
builds = yield self.master.data.get(("buildrequests", brid, "builds"))

# Don't call the data API here, as the buildrequests might have been
# taken by another master. We just send the stop message and forget about those.
mqKwargs = {'reason': kwargs.get('reason', 'no reason')}
for b in builds:
self.master.mq.produce(("control", "builds", str(b['buildid']), "stop"),
mqKwargs)
defer.returnValue(None)

# then complete it with 'CANCELLED'; this is the closest we can get to
# cancelling a request without running into trouble with dangling
# references.
yield self.master.data.updates.completeBuildRequests([brid],
results.CANCELLED)


class BuildRequestsEndpoint(Db2DataMixin, base.Endpoint):

Expand Down
15 changes: 12 additions & 3 deletions master/buildbot/data/builds.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ def startConsuming(self, callback, options, kwargs):
return self.master.mq.startConsuming(callback,
('builds', str(buildid), None))

def control(self, action, args, kwargs):
if action != "stop":
raise ValueError("action: {} is not supported".format(action))
self.master.mq.produce(("control", "builds",
str(kwargs['buildid']), 'stop'),
dict(reason=kwargs.get('reason', 'no reason')))
return defer.succeed(None)


class BuildsEndpoint(Db2DataMixin, base.Endpoint):

Expand Down Expand Up @@ -146,11 +154,12 @@ def addBuild(self, builderid, buildrequestid, buildslaveid):
buildslaveid=buildslaveid,
masterid=self.master.masterid,
state_string=u'created')
if res is not None:
_id, number = res
yield self.generateEvent(_id, "new")
defer.returnValue(res)

@base.updateMethod
def generateNewBuildEvent(self, buildid):
return self.generateEvent(buildid, "new")

@base.updateMethod
@defer.inlineCallbacks
def setBuildStateString(self, buildid, state_string):
Expand Down
58 changes: 31 additions & 27 deletions master/buildbot/process/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from buildbot.status.results import SUCCESS
from buildbot.status.results import WARNINGS
from buildbot.status.results import computeResultAndTermination
from buildbot.status.results import worst_status
from buildbot.util.eventual import eventually


Expand Down Expand Up @@ -89,6 +90,7 @@ def __init__(self, requests):
self.terminate = False

self._acquiringLock = None
self.results = SUCCESS # overall results, may downgrade after each step

def setBuilder(self, builder):
"""
Expand Down Expand Up @@ -141,7 +143,7 @@ def blamelist(self):
if c.who not in blamelist:
blamelist.append(c.who)
for source in self.sources:
if source.patch_info: # Add patch author to blamelist
if source.patch: # Add patch author to blamelist
blamelist.append(source.patch_info[0])
blamelist.sort()
return blamelist
Expand Down Expand Up @@ -236,6 +238,7 @@ def startBuild(self, build_status, expectations, slavebuilder):
# the Deferred returned by this method.

log.msg("%s.startBuild" % self)

self.build_status = build_status
# TODO: this will go away when build collapsing is implemented; until
# then we just assign the bulid to the first buildrequest
Expand All @@ -246,6 +249,12 @@ def startBuild(self, build_status, expectations, slavebuilder):
buildrequestid=brid,
buildslaveid=slave.buildslaveid)

self.stopBuildConsumer = yield self.master.mq.startConsuming(self.stopBuild,
("control", "builds",
str(self.buildid),
"stop"))
yield self.master.data.updates.generateNewBuildEvent(self.buildid)

# now that we have a build_status, we can set properties
self.setupProperties()
self.setupSlaveBuilder(slavebuilder)
Expand Down Expand Up @@ -368,9 +377,6 @@ def setupBuild(self, expectations):
if "owner" in r.properties]
if owners:
self.setProperty('owners', owners, self.reason)

self.results = [] # list of FAILURE, SUCCESS, WARNINGS, SKIPPED
self.result = SUCCESS # overall result, may downgrade after each step
self.text = [] # list of text string lists (text2)

def _addBuildSteps(self, step_factories):
Expand Down Expand Up @@ -440,22 +446,21 @@ def _stepDone(self, results, step):
self.terminate = True
return self.startNextStep()

def stepDone(self, result, step):
def stepDone(self, results, step):
"""This method is called when the BuildStep completes. It is passed a
status object from the BuildStep and is responsible for merging the
Step's results into those of the overall Build."""

terminate = False
text = None
if isinstance(result, types.TupleType):
result, text = result
assert isinstance(result, type(SUCCESS)), "got %r" % (result,)
log.msg(" step '%s' complete: %s" % (step.name, Results[result]))
self.results.append(result)
if isinstance(results, types.TupleType):
results, text = results
assert isinstance(results, type(SUCCESS)), "got %r" % (results,)
log.msg(" step '%s' complete: %s" % (step.name, Results[results]))
if text:
self.text.extend(text)
self.result, terminate = computeResultAndTermination(step, result,
self.result)
self.results, terminate = computeResultAndTermination(step, results,
self.results)
if not self.conn:
terminate = True
return terminate
Expand All @@ -471,52 +476,52 @@ def lostRemote(self, conn=None):
log.msg(" stopping currentStep", self.currentStep)
self.currentStep.interrupt(Failure(error.ConnectionLost()))
else:
self.result = RETRY
self.results = RETRY
self.text = ["lost", "connection"]
self.stopped = True
if self._acquiringLock:
lock, access, d = self._acquiringLock
lock.stopWaitingUntilAvailable(self, access, d)
d.callback(None)

def stopBuild(self, reason="<no reason given>"):
def stopBuild(self, reason="<no reason given>", cbParams=None):
# the idea here is to let the user cancel a build because, e.g.,
# they realized they committed a bug and they don't want to waste
# the time building something that they know will fail. Another
# reason might be to abandon a stuck build. We want to mark the
# build as failed quickly rather than waiting for the slave's
# timeout to kill it on its own.

log.msg(" %s: stopping build: %s" % (self, reason))
log.msg(" %s: stopping build: %s %s" % (self, reason, cbParams))
if self.finished:
return
# TODO: include 'reason' in this point event
self.stopped = True
if self.currentStep:
self.currentStep.interrupt(reason)

self.result = CANCELLED
self.results = CANCELLED

if self._acquiringLock:
lock, access, d = self._acquiringLock
lock.stopWaitingUntilAvailable(self, access, d)
d.callback(None)

def allStepsDone(self):
if self.result == FAILURE:
if self.results == FAILURE:
text = ["failed"]
elif self.result == WARNINGS:
elif self.results == WARNINGS:
text = ["warnings"]
elif self.result == EXCEPTION:
elif self.results == EXCEPTION:
text = ["exception"]
elif self.result == RETRY:
elif self.results == RETRY:
text = ["retry"]
elif self.result == CANCELLED:
elif self.results == CANCELLED:
text = ["cancelled"]
else:
text = ["build", "successful"]
text.extend(self.text)
return self.buildFinished(text, self.result)
return self.buildFinished(text, self.results)

def buildException(self, why):
log.msg("%s.buildException" % self)
Expand All @@ -534,22 +539,21 @@ def buildFinished(self, text, results):
state.
It takes two arguments which describe the overall build status:
text, results. 'results' is one of SUCCESS, WARNINGS, or FAILURE.
text, results. 'results' is one of the possible results (see buildbot.status.results).
If 'results' is SUCCESS or WARNINGS, we will permit any dependant
builds to start. If it is 'FAILURE', those builds will be
abandoned."""

self.stopBuildConsumer.stopConsuming()
self.finished = True
if self.conn:
self.subs.unsubscribe()
self.subs = None
self.conn = None
self.results = results

log.msg(" %s: build finished" % self)
self.results = worst_status(self.results, results)
self.build_status.setText(text)
self.build_status.setResults(results)
self.build_status.setResults(self.results)
self.build_status.buildFinished()
eventually(self.releaseLocks)
self.deferred.callback(self)
Expand Down
50 changes: 0 additions & 50 deletions master/buildbot/process/buildrequest.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,10 @@

import calendar

from buildbot import interfaces
from buildbot.data import resultspec
from buildbot.db import buildrequests
from buildbot.process import properties
from buildbot.status.results import FAILURE
from buildbot.status.results import SKIPPED
from twisted.internet import defer
from twisted.python import log
from zope.interface import implements


class BuildRequestCollapser(object):
Expand Down Expand Up @@ -320,48 +315,3 @@ def mergeReasons(self, others):

def getSubmitTime(self):
return self.submittedAt

@defer.inlineCallbacks
def cancelBuildRequest(self):
# first, try to claim the request; if this fails, then it's too late to
# cancel the build anyway
try:
yield self.master.data.updates.claimBuildRequests([self.id])
except buildrequests.AlreadyClaimedError:
log.msg("build request already claimed; cannot cancel")
return

# send a cancellation message
builderid = -1 # TODO
key = ('buildrequests', self.bsid, builderid, self.id, 'cancelled')
msg = dict(
brid=self.id,
bsid=self.bsid,
buildername=self.buildername,
builderid=builderid)
self.master.mq.produce(key, msg)

# then complete it with 'FAILURE'; this is the closest we can get to
# cancelling a request without running into trouble with dangling
# references.
yield self.master.data.updates.completeBuildRequests([self.id],
FAILURE)


class BuildRequestControl:
implements(interfaces.IBuildRequestControl)

def __init__(self, builder, request):
self.original_builder = builder
self.original_request = request
self.brid = request.id

def subscribe(self, observer):
raise NotImplementedError

def unsubscribe(self, observer):
raise NotImplementedError

def cancel(self):
d = self.original_request.cancelBuildRequest()
d.addErrback(log.err, 'while cancelling build request')
7 changes: 4 additions & 3 deletions master/buildbot/process/buildstep.py
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ def __init__(self, **kwargs):
self.statistics = {}
self.logs = {}
self._running = False

self.stepid = None
self._start_unhandled_deferreds = None

def __new__(klass, *args, **kwargs):
Expand Down Expand Up @@ -415,8 +415,9 @@ def methodInfo(m):
if not isinstance(stepResult, unicode):
raise TypeError("step result string must be unicode (got %r)"
% (stepResult,))
yield self.master.data.updates.setStepStateString(self.stepid,
stepResult)
if self.stepid is not None:
yield self.master.data.updates.setStepStateString(self.stepid,
stepResult)

if not self._running:
buildResult = summary.get('build', None)
Expand Down

0 comments on commit 924c44f

Please sign in to comment.