Package buildbot :: Package process :: Module buildrequest
[frames] | no frames]

Source Code for Module buildbot.process.buildrequest

  1  # This file is part of Buildbot.  Buildbot is free software: you can 
  2  # redistribute it and/or modify it under the terms of the GNU General Public 
  3  # License as published by the Free Software Foundation, version 2. 
  4  # 
  5  # This program is distributed in the hope that it will be useful, but WITHOUT 
  6  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
  7  # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
  8  # details. 
  9  # 
 10  # You should have received a copy of the GNU General Public License along with 
 11  # this program; if not, write to the Free Software Foundation, Inc., 51 
 12  # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 13  # 
 14  # Copyright Buildbot Team Members 
 15   
 16  import calendar 
 17  from zope.interface import implements 
 18  from twisted.python import log 
 19  from twisted.internet import defer 
 20  from buildbot import interfaces, sourcestamp 
 21  from buildbot.process import properties 
 22  from buildbot.status.results import FAILURE 
 23  from buildbot.db import buildrequests 
24 25 -class BuildRequest(object):
26 """ 27 28 A rolled-up encapsulation of all of the data relevant to a build request. 29 30 This class is used by the C{nextBuild} and C{mergeRequests} configuration 31 parameters, as well as in starting a build. Construction of a BuildRequest 32 object is a heavyweight process involving a lot of database queries, so 33 it should be avoided where possible. See bug #1894. 34 35 Build requests have a SourceStamp which specifies what sources to build. 36 This may specify a specific revision of the source tree (so source.branch, 37 source.revision, and source.patch are used). The .patch attribute is either 38 None or a tuple of (patchlevel, diff), consisting of a number to use in 39 'patch -pN', and a unified-format context diff. 40 41 Alternatively, the SourceStamp may specify a set of Changes to be built, 42 contained in source.changes. In this case, the requeset may be mergeable 43 with other BuildRequests on the same branch. 44 45 @type source: L{buildbot.sourcestamp.SourceStamp} 46 @ivar source: the source stamp that this BuildRequest use 47 48 @type reason: string 49 @ivar reason: the reason this Build is being requested. Schedulers provide 50 this, but for forced builds the user requesting the build will provide a 51 string. It comes from the buildsets table. 52 53 @type properties: L{properties.Properties} 54 @ivar properties: properties that should be applied to this build, taken 55 from the buildset containing this build request 56 57 @ivar submittedAt: a timestamp (seconds since epoch) when this request was 58 submitted to the Builder. This is used by the CVS step to compute a 59 checkout timestamp, as well as by the master to prioritize build requests 60 from oldest to newest. 61 62 @ivar buildername: name of the requested builder 63 64 @ivar priority: request priority 65 66 @ivar id: build request ID 67 68 @ivar bsid: ID of the parent buildset 69 """ 70 71 source = None 72 sources = None 73 submittedAt = None 74 75 @classmethod
76 - def fromBrdict(cls, master, brdict):
77 """ 78 Construct a new L{BuildRequest} from a dictionary as returned by 79 L{BuildRequestsConnectorComponent.getBuildRequest}. 80 81 This method uses a cache, which may result in return of stale objects; 82 for the most up-to-date information, use the database connector 83 methods. 84 85 @param master: current build master 86 @param brdict: build request dictionary 87 88 @returns: L{BuildRequest}, via Deferred 89 """ 90 cache = master.caches.get_cache("BuildRequests", cls._make_br) 91 return cache.get(brdict['brid'], brdict=brdict, master=master)
92 93 @classmethod 94 @defer.deferredGenerator
95 - def _make_br(cls, brid, brdict, master):
96 buildrequest = cls() 97 buildrequest.id = brid 98 buildrequest.bsid = brdict['buildsetid'] 99 buildrequest.buildername = brdict['buildername'] 100 buildrequest.priority = brdict['priority'] 101 dt = brdict['submitted_at'] 102 buildrequest.submittedAt = dt and calendar.timegm(dt.utctimetuple()) 103 buildrequest.master = master 104 105 # fetch the buildset to get the reason 106 wfd = defer.waitForDeferred( 107 master.db.buildsets.getBuildset(brdict['buildsetid'])) 108 yield wfd 109 buildset = wfd.getResult() 110 assert buildset # schema should guarantee this 111 buildrequest.reason = buildset['reason'] 112 113 # fetch the buildset properties, and convert to Properties 114 wfd = defer.waitForDeferred( 115 master.db.buildsets.getBuildsetProperties(brdict['buildsetid'])) 116 yield wfd 117 buildset_properties = wfd.getResult() 118 119 pr = properties.Properties() 120 for name, (value, source) in buildset_properties.iteritems(): 121 pr.setProperty(name, value, source) 122 buildrequest.properties = pr 123 124 # fetch the sourcestamp dictionary 125 wfd = defer.waitForDeferred( 126 master.db.sourcestamps.getSourceStamps(buildset['sourcestampsetid'])) 127 yield wfd 128 sslist = wfd.getResult() 129 assert len(sslist) > 0, "Empty sourcestampset: db schema enforces set to exist but cannot enforce a non empty set" 130 131 # and turn it into a SourceStamp 132 buildrequest.sources = [] 133 for ssdict in sslist: 134 wfd = defer.waitForDeferred( 135 sourcestamp.SourceStamp.fromSsdict(master, ssdict)) 136 yield wfd 137 buildrequest.sources.append(wfd.getResult()) 138 139 # support old interface where only one source could exist 140 # TODO: remove and change all client objects that use this property 141 if len(buildrequest.sources) > 0: 142 buildrequest.source = buildrequest.sources[0] 143 144 yield buildrequest # return value
145
146 - def canBeMergedWith(self, other):
147 return self.source.canBeMergedWith(other.source)
148
149 - def mergeWith(self, others):
150 return self.source.mergeWith([o.source for o in others])
151
152 - def mergeReasons(self, others):
153 """Return a reason for the merged build request.""" 154 reasons = [] 155 for req in [self] + others: 156 if req.reason and req.reason not in reasons: 157 reasons.append(req.reason) 158 return ", ".join(reasons)
159
160 - def getSubmitTime(self):
161 return self.submittedAt
162 163 @defer.deferredGenerator
164 - def cancelBuildRequest(self):
165 # first, try to claim the request; if this fails, then it's too late to 166 # cancel the build anyway 167 try: 168 wfd = defer.waitForDeferred( 169 self.master.db.buildrequests.claimBuildRequests([self.id])) 170 yield wfd 171 wfd.getResult() 172 except buildrequests.AlreadyClaimedError: 173 log.msg("build request already claimed; cannot cancel") 174 return 175 176 # then complete it with 'FAILURE'; this is the closest we can get to 177 # cancelling a request without running into trouble with dangling 178 # references. 179 wfd = defer.waitForDeferred( 180 self.master.db.buildrequests.completeBuildRequests([self.id], 181 FAILURE)) 182 yield wfd 183 wfd.getResult() 184 185 # and let the master know that the enclosing buildset may be complete 186 wfd = defer.waitForDeferred( 187 self.master.maybeBuildsetComplete(self.bsid)) 188 yield wfd 189 wfd.getResult()
190
191 -class BuildRequestControl:
192 implements(interfaces.IBuildRequestControl) 193
194 - def __init__(self, builder, request):
195 self.original_builder = builder 196 self.original_request = request 197 self.brid = request.id
198
199 - def subscribe(self, observer):
200 raise NotImplementedError
201
202 - def unsubscribe(self, observer):
203 raise NotImplementedError
204
205 - def cancel(self):
206 d = self.original_request.cancelBuildRequest() 207 d.addErrback(log.err, 'while cancelling build request')
208