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 submittedAt = None 73 74 @classmethod
75 - def fromBrdict(cls, master, brdict):
76 """ 77 Construct a new L{BuildRequest} from a dictionary as returned by 78 L{BuildRequestsConnectorComponent.getBuildRequest}. 79 80 This method uses a cache, which may result in return of stale objects; 81 for the most up-to-date information, use the database connector 82 methods. 83 84 @param master: current build master 85 @param brdict: build request dictionary 86 87 @returns: L{BuildRequest}, via Deferred 88 """ 89 cache = master.caches.get_cache("BuildRequests", cls._make_br) 90 return cache.get(brdict['brid'], brdict=brdict, master=master)
91 92 @classmethod 93 @defer.deferredGenerator
94 - def _make_br(cls, brid, brdict, master):
95 buildrequest = cls() 96 buildrequest.id = brid 97 buildrequest.bsid = brdict['buildsetid'] 98 buildrequest.buildername = brdict['buildername'] 99 buildrequest.priority = brdict['priority'] 100 dt = brdict['submitted_at'] 101 buildrequest.submittedAt = dt and calendar.timegm(dt.utctimetuple()) 102 buildrequest.master = master 103 104 # fetch the buildset to get the reason 105 wfd = defer.waitForDeferred( 106 master.db.buildsets.getBuildset(brdict['buildsetid'])) 107 yield wfd 108 buildset = wfd.getResult() 109 assert buildset # schema should guarantee this 110 buildrequest.reason = buildset['reason'] 111 112 # fetch the buildset properties, and convert to Properties 113 wfd = defer.waitForDeferred( 114 master.db.buildsets.getBuildsetProperties(brdict['buildsetid'])) 115 yield wfd 116 buildset_properties = wfd.getResult() 117 118 pr = properties.Properties() 119 for name, (value, source) in buildset_properties.iteritems(): 120 pr.setProperty(name, value, source) 121 buildrequest.properties = pr 122 123 # fetch the sourcestamp dictionary 124 wfd = defer.waitForDeferred( 125 master.db.sourcestamps.getSourceStamp(buildset['sourcestampid'])) 126 yield wfd 127 ssdict = wfd.getResult() 128 assert ssdict # db schema should enforce this anyway 129 130 # and turn it into a SourceStamp 131 wfd = defer.waitForDeferred( 132 sourcestamp.SourceStamp.fromSsdict(master, ssdict)) 133 yield wfd 134 buildrequest.source = wfd.getResult() 135 136 yield buildrequest # return value
137
138 - def canBeMergedWith(self, other):
139 return self.source.canBeMergedWith(other.source)
140
141 - def mergeWith(self, others):
142 return self.source.mergeWith([o.source for o in others])
143
144 - def mergeReasons(self, others):
145 """Return a reason for the merged build request.""" 146 reasons = [] 147 for req in [self] + others: 148 if req.reason and req.reason not in reasons: 149 reasons.append(req.reason) 150 return ", ".join(reasons)
151
152 - def getSubmitTime(self):
153 return self.submittedAt
154 155 @defer.deferredGenerator
156 - def cancelBuildRequest(self):
157 # first, try to claim the request; if this fails, then it's too late to 158 # cancel the build anyway 159 try: 160 wfd = defer.waitForDeferred( 161 self.master.db.buildrequests.claimBuildRequests([self.id])) 162 yield wfd 163 wfd.getResult() 164 except buildrequests.AlreadyClaimedError: 165 log.msg("build request already claimed; cannot cancel") 166 return 167 168 # then complete it with 'FAILURE'; this is the closest we can get to 169 # cancelling a request without running into trouble with dangling 170 # references. 171 wfd = defer.waitForDeferred( 172 self.master.db.buildrequests.completeBuildRequests([self.id], 173 FAILURE)) 174 yield wfd 175 wfd.getResult() 176 177 # and let the master know that the enclosing buildset may be complete 178 wfd = defer.waitForDeferred( 179 self.master.maybeBuildsetComplete(self.bsid)) 180 yield wfd 181 wfd.getResult()
182
183 -class BuildRequestControl:
184 implements(interfaces.IBuildRequestControl) 185
186 - def __init__(self, builder, request):
187 self.original_builder = builder 188 self.original_request = request 189 self.brid = request.id
190
191 - def subscribe(self, observer):
192 raise NotImplementedError
193
194 - def unsubscribe(self, observer):
195 raise NotImplementedError
196
197 - def cancel(self):
198 d = self.original_request.cancelBuildRequest() 199 d.addErrback(log.err, 'while cancelling build request')
200