Skip to content

Commit

Permalink
schedulerdb 3-of-5: BuildRequest/BuildSet
Browse files Browse the repository at this point in the history
The canonical storage of a build request is now in the DB, but a separate
BuildRequest class is defined (in buildbot/buildrequest.py, split out from
buildbot/process/base.py) to pass around to code that needs it. Likewise the
old BuildSet class has been removed and its funcionality moved into the
database. The scheduling methods on BuildSet (like waitUntilSuccess and
start) have been subsumed by other changes to the Schedulers and Builders, so
buildbot/buildset.py was removed altogether.
  • Loading branch information
warner committed Feb 15, 2010
1 parent 0e7f18a commit 6f9156d
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 225 deletions.
122 changes: 122 additions & 0 deletions buildbot/buildrequest.py
@@ -0,0 +1,122 @@
# ***** BEGIN LICENSE BLOCK *****
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
#
# The contents of this file are subject to the Mozilla Public License Version
# 1.1 (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
# http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS IS" basis,
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
# for the specific language governing rights and limitations under the
# License.
#
# The Original Code is Mozilla-specific Buildbot steps.
#
# The Initial Developer of the Original Code is
# Mozilla Foundation.
# Portions created by the Initial Developer are Copyright (C) 2009
# the Initial Developer. All Rights Reserved.
#
# Contributor(s):
# Brian Warner <warner@lothar.com>
#
# Alternatively, the contents of this file may be used under the terms of
# either the GNU General Public License Version 2 or later (the "GPL"), or
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
# in which case the provisions of the GPL or the LGPL are applicable instead
# of those above. If you wish to allow use of your version of this file only
# under the terms of either the GPL or the LGPL, and not to allow others to
# use your version of this file under the terms of the MPL, indicate your
# decision by deleting the provisions above and replace them with the notice
# and other provisions required by the GPL or the LGPL. If you do not delete
# the provisions above, a recipient may use your version of this file under
# the terms of any one of the MPL, the GPL or the LGPL.
#
# ***** END LICENSE BLOCK *****

from buildbot import interfaces
from buildbot.process.properties import Properties

class BuildRequest:
"""I represent a request to a specific Builder to run a single build.
I am generated by db.getBuildRequestWithNumber, and am used to tell the
Build about what it ought to be building. I am also used by the Builder
to let hook functions decide which requests should be handled first.
I have a SourceStamp which specifies what sources I will build. This may
specify a specific revision of the source tree (so source.branch,
source.revision, and source.patch are used). The .patch attribute is
either None or a tuple of (patchlevel, diff), consisting of a number to
use in 'patch -pN', and a unified-format context diff.
Alternatively, the SourceStamp may specify a set of Changes to be built,
contained in source.changes. In this case, I may be mergeable with other
BuildRequests on the same branch.
@type source: a L{buildbot.sourcestamp.SourceStamp} instance.
@ivar source: the source code that this BuildRequest use
@type reason: string
@ivar reason: the reason this Build is being requested. Schedulers
provide this, but for forced builds the user requesting the
build will provide a string.
@type properties: Properties object
@ivar properties: properties that should be applied to this build
'owner' property is used by Build objects to collect
the list returned by getInterestedUsers
@ivar status: the IBuildStatus object which tracks our status
@ivar submittedAt: a timestamp (seconds since epoch) when this request
was submitted to the Builder. This is used by the CVS
step to compute a checkout timestamp, as well as the
master to prioritize build requests from oldest to
newest.
"""

source = None
builder = None # XXXREMOVE
startCount = 0 # how many times we have tried to start this build # XXXREMOVE
submittedAt = None

def __init__(self, reason, source, builderName, properties=None):
assert interfaces.ISourceStamp(source, None)
self.reason = reason
self.source = source
self.builderName = builderName

self.properties = Properties()
if properties:
self.properties.updateFromProperties(properties)

def canBeMergedWith(self, other):
return self.source.canBeMergedWith(other.source)

def mergeWith(self, others):
return self.source.mergeWith([o.source for o in others])

def mergeReasons(self, others):
"""Return a reason for the merged build request."""
reasons = []
for req in [self] + others:
if req.reason and req.reason not in reasons:
reasons.append(req.reason)
return ", ".join(reasons)

# IBuildRequestControl

def cancel(self): # XXXREMOVE
"""Cancel this request. This can only be successful if the Build has
not yet been started.
@return: a boolean indicating if the cancel was successful."""
if self.builder:
return self.builder.cancelBuildRequest(self)
return False

def getSubmitTime(self):
return self.submittedAt

84 changes: 0 additions & 84 deletions buildbot/buildset.py

This file was deleted.

146 changes: 5 additions & 141 deletions buildbot/process/base.py
Expand Up @@ -8,144 +8,9 @@
from twisted.internet import reactor, defer, error

from buildbot import interfaces, locks
from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, EXCEPTION
from buildbot.status.builder import Results, BuildRequestStatus
from buildbot.status.builder import SUCCESS, WARNINGS, FAILURE, EXCEPTION, RETRY
from buildbot.status.builder import Results
from buildbot.status.progress import BuildProgress
from buildbot.process.properties import Properties

class BuildRequest:
"""I represent a request to a specific Builder to run a single build.
I have a SourceStamp which specifies what sources I will build. This may
specify a specific revision of the source tree (so source.branch,
source.revision, and source.patch are used). The .patch attribute is
either None or a tuple of (patchlevel, diff), consisting of a number to
use in 'patch -pN', and a unified-format context diff.
Alternatively, the SourceStamp may specify a set of Changes to be built,
contained in source.changes. In this case, I may be mergeable with other
BuildRequests on the same branch.
I may be part of a BuildSet, in which case I will report status results
to it.
I am paired with a BuildRequestStatus object, to which I feed status
information.
@type source: a L{buildbot.sourcestamp.SourceStamp} instance.
@ivar source: the source code that this BuildRequest use
@type reason: string
@ivar reason: the reason this Build is being requested. Schedulers
provide this, but for forced builds the user requesting the
build will provide a string.
@type properties: Properties object
@ivar properties: properties that should be applied to this build
'owner' property is used by Build objects to collect
the list returned by getInterestedUsers
@ivar status: the IBuildStatus object which tracks our status
@ivar submittedAt: a timestamp (seconds since epoch) when this request
was submitted to the Builder. This is used by the CVS
step to compute a checkout timestamp, as well as the
master to prioritize build requests from oldest to
newest.
"""

source = None
builder = None
startCount = 0 # how many times we have tried to start this build
submittedAt = None

implements(interfaces.IBuildRequestControl)

def __init__(self, reason, source, builderName, properties=None):
assert interfaces.ISourceStamp(source, None)
self.reason = reason
self.source = source

self.properties = Properties()
if properties:
self.properties.updateFromProperties(properties)

self.start_watchers = []
self.finish_watchers = []
self.status = BuildRequestStatus(source, builderName)

def canBeMergedWith(self, other):
return self.source.canBeMergedWith(other.source)

def mergeWith(self, others):
return self.source.mergeWith([o.source for o in others])

def mergeReasons(self, others):
"""Return a reason for the merged build request."""
reasons = []
for req in [self] + others:
if req.reason and req.reason not in reasons:
reasons.append(req.reason)
return ", ".join(reasons)

def waitUntilFinished(self):
"""Get a Deferred that will fire (with a
L{buildbot.interfaces.IBuildStatus} instance when the build
finishes."""
d = defer.Deferred()
self.finish_watchers.append(d)
return d

# these are called by the Builder

def requestSubmitted(self, builder):
# the request has been placed on the queue
self.builder = builder

def buildStarted(self, build, buildstatus):
"""This is called by the Builder when a Build has been started in the
hopes of satifying this BuildRequest. It may be called multiple
times, since interrupted builds and lost buildslaves may force
multiple Builds to be run until the fate of the BuildRequest is known
for certain."""
for o in self.start_watchers[:]:
# these observers get the IBuildControl
o(build)
# while these get the IBuildStatus
self.status.buildStarted(buildstatus)

def finished(self, buildstatus):
"""This is called by the Builder when the BuildRequest has been
retired. This happens when its Build has either succeeded (yay!) or
failed (boo!). TODO: If it is halted due to an exception (oops!), or
some other retryable error, C{finished} will not be called yet."""

for w in self.finish_watchers:
w.callback(buildstatus)
self.finish_watchers = []

# IBuildRequestControl

def subscribe(self, observer):
self.start_watchers.append(observer)
def unsubscribe(self, observer):
self.start_watchers.remove(observer)

def cancel(self):
"""Cancel this request. This can only be successful if the Build has
not yet been started.
@return: a boolean indicating if the cancel was successful."""
if self.builder:
return self.builder.cancelBuildRequest(self)
return False

def setSubmitTime(self, t):
self.submittedAt = t
self.status.setSubmitTime(t)

def getSubmitTime(self):
return self.submittedAt


class Build:
Expand Down Expand Up @@ -229,12 +94,9 @@ def allChanges(self):
def allFiles(self):
# return a list of all source files that were changed
files = []
havedirs = 0
for c in self.allChanges():
for f in c.files:
files.append(f)
if c.isdir:
havedirs = 1
return files

def __repr__(self):
Expand Down Expand Up @@ -444,7 +306,6 @@ def setupBuild(self, expectations):

# we are now ready to set up our BuildStatus.
self.build_status.setSourceStamp(self.source)
self.build_status.setRequests([req.status for req in self.requests])
self.build_status.setReason(self.reason)
self.build_status.setBlamelist(self.blamelist())
self.build_status.setProgress(self.progress)
Expand Down Expand Up @@ -528,6 +389,9 @@ def stepDone(self, result, step):
elif result == EXCEPTION:
self.result = EXCEPTION
terminate = True
elif result == RETRY:
self.result = RETRY
terminate = True
return terminate

def lostRemote(self, remote=None):
Expand Down

0 comments on commit 6f9156d

Please sign in to comment.