Package buildbot :: Package schedulers :: Module trysched
[frames] | no frames]

Source Code for Module buildbot.schedulers.trysched

  1  # ***** BEGIN LICENSE BLOCK ***** 
  2  # Version: MPL 1.1/GPL 2.0/LGPL 2.1 
  3  # 
  4  # The contents of this file are subject to the Mozilla Public License Version 
  5  # 1.1 (the "License"); you may not use this file except in compliance with 
  6  # the License. You may obtain a copy of the License at 
  7  # http://www.mozilla.org/MPL/ 
  8  # 
  9  # Software distributed under the License is distributed on an "AS IS" basis, 
 10  # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 
 11  # for the specific language governing rights and limitations under the 
 12  # License. 
 13  # 
 14  # The Original Code is Mozilla-specific Buildbot steps. 
 15  # 
 16  # The Initial Developer of the Original Code is 
 17  # Mozilla Foundation. 
 18  # Portions created by the Initial Developer are Copyright (C) 2009 
 19  # the Initial Developer. All Rights Reserved. 
 20  # 
 21  # Contributor(s): 
 22  #   Brian Warner <warner@lothar.com> 
 23  # 
 24  # Alternatively, the contents of this file may be used under the terms of 
 25  # either the GNU General Public License Version 2 or later (the "GPL"), or 
 26  # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 
 27  # in which case the provisions of the GPL or the LGPL are applicable instead 
 28  # of those above. If you wish to allow use of your version of this file only 
 29  # under the terms of either the GPL or the LGPL, and not to allow others to 
 30  # use your version of this file under the terms of the MPL, indicate your 
 31  # decision by deleting the provisions above and replace them with the notice 
 32  # and other provisions required by the GPL or the LGPL. If you do not delete 
 33  # the provisions above, a recipient may use your version of this file under 
 34  # the terms of any one of the MPL, the GPL or the LGPL. 
 35  # 
 36  # ***** END LICENSE BLOCK ***** 
 37   
 38  import os.path 
 39   
 40  from zope.interface import implements 
 41  from twisted.application import strports 
 42  from twisted.python import log, runtime 
 43  from twisted.protocols import basic 
 44  from twisted.cred import portal, checkers 
 45  from twisted.spread import pb 
 46   
 47  from buildbot import pbutil 
 48  from buildbot.sourcestamp import SourceStamp 
 49  from buildbot.changes.maildir import MaildirService 
 50  from buildbot.process.properties import Properties 
 51  from buildbot.schedulers import base 
 52  from buildbot.status.builder import BuildSetStatus 
 53   
 54   
55 -class TryBase(base.BaseScheduler):
56
57 - def run(self):
58 # triggered by external events, not DB changes or timers 59 return None
60
61 - def filterBuilderList(self, builderNames):
62 # self.builderNames is the configured list of builders 63 # available for try. If the user supplies a list of builders, 64 # it must be restricted to the configured list. If not, build 65 # on all of the configured builders. 66 if builderNames: 67 for b in builderNames: 68 if not b in self.builderNames: 69 log.msg("%s got with builder %s" % (self, b)) 70 log.msg(" but that wasn't in our list: %s" 71 % (self.builderNames,)) 72 return [] 73 else: 74 builderNames = self.builderNames 75 return builderNames
76
77 -class BadJobfile(Exception):
78 pass
79
80 -class JobFileScanner(basic.NetstringReceiver):
81 - def __init__(self):
82 self.strings = [] 83 self.transport = self # so transport.loseConnection works 84 self.error = False
85
86 - def stringReceived(self, s):
87 self.strings.append(s)
88
89 - def loseConnection(self):
90 self.error = True
91
92 -class Try_Jobdir(TryBase):
93 compare_attrs = ( 'name', 'builderNames', 'jobdir', 'properties' ) 94
95 - def __init__(self, name, builderNames, jobdir, 96 properties={}):
97 base.BaseScheduler.__init__(self, name, builderNames, properties) 98 self.jobdir = jobdir 99 self.watcher = MaildirService() 100 self.watcher.setServiceParent(self)
101
102 - def setServiceParent(self, parent):
103 sm = parent 104 m = sm.parent 105 self.watcher.setBasedir(os.path.join(m.basedir, self.jobdir)) 106 TryBase.setServiceParent(self, parent)
107
108 - def parseJob(self, f):
109 # jobfiles are serialized build requests. Each is a list of 110 # serialized netstrings, in the following order: 111 # "1", the version number of this format 112 # buildsetID, arbitrary string, used to find the buildSet later 113 # branch name, "" for default-branch 114 # base revision, "" for HEAD 115 # patchlevel, usually "1" 116 # patch 117 # builderNames... 118 p = JobFileScanner() 119 p.dataReceived(f.read()) 120 if p.error: 121 raise BadJobfile("unable to parse netstrings") 122 s = p.strings 123 ver = s.pop(0) 124 if ver == "1": 125 buildsetID, branch, baserev, patchlevel, diff = s[:5] 126 builderNames = s[5:] 127 if branch == "": 128 branch = None 129 if baserev == "": 130 baserev = None 131 patchlevel = int(patchlevel) 132 patch = (patchlevel, diff) 133 ss = SourceStamp("Old client", branch, baserev, patch) 134 elif ver == "2": # introduced the repository and project property 135 buildsetID, branch, baserev, patchlevel, diff, repository, project = s[:7] 136 builderNames = s[7:] 137 if branch == "": 138 branch = None 139 if baserev == "": 140 baserev = None 141 patchlevel = int(patchlevel) 142 patch = (patchlevel, diff) 143 ss = SourceStamp(branch, baserev, patch, repository=repository, 144 project=project) 145 else: 146 raise BadJobfile("unknown version '%s'" % ver) 147 return builderNames, ss, buildsetID
148
149 - def messageReceived(self, filename):
150 md = os.path.join(self.parent.parent.basedir, self.jobdir) 151 if runtime.platformType == "posix": 152 # open the file before moving it, because I'm afraid that once 153 # it's in cur/, someone might delete it at any moment 154 path = os.path.join(md, "new", filename) 155 f = open(path, "r") 156 os.rename(os.path.join(md, "new", filename), 157 os.path.join(md, "cur", filename)) 158 else: 159 # do this backwards under windows, because you can't move a file 160 # that somebody is holding open. This was causing a Permission 161 # Denied error on bear's win32-twisted1.3 buildslave. 162 os.rename(os.path.join(md, "new", filename), 163 os.path.join(md, "cur", filename)) 164 path = os.path.join(md, "cur", filename) 165 f = open(path, "r") 166 167 try: 168 builderNames, ss, jobid = self.parseJob(f) 169 except BadJobfile: 170 log.msg("%s reports a bad jobfile in %s" % (self, filename)) 171 log.err() 172 return 173 # Validate/fixup the builder names. 174 builderNames = self.filterBuilderList(builderNames) 175 if not builderNames: 176 return 177 reason = "'try' job" 178 d = self.parent.db.runInteraction(self._try, ss, builderNames, reason) 179 def _done(ign): 180 self.parent.loop_done() # so it will notify builder loop
181 d.addCallback(_done) 182 return d
183
184 - def _try(self, t, ss, builderNames, reason):
185 db = self.parent.db 186 ssid = db.get_sourcestampid(ss, t) 187 bsid = self.create_buildset(ssid, reason, t, builderNames=builderNames) 188 return bsid
189
190 -class Try_Userpass(TryBase):
191 compare_attrs = ( 'name', 'builderNames', 'port', 'userpass', 'properties' ) 192 implements(portal.IRealm) 193
194 - def __init__(self, name, builderNames, port, userpass, 195 properties={}):
196 base.BaseScheduler.__init__(self, name, builderNames, properties) 197 if type(port) is int: 198 port = "tcp:%d" % port 199 self.port = port 200 self.userpass = userpass 201 c = checkers.InMemoryUsernamePasswordDatabaseDontUse() 202 for user,passwd in self.userpass: 203 c.addUser(user, passwd) 204 205 p = portal.Portal(self) 206 p.registerChecker(c) 207 f = pb.PBServerFactory(p) 208 s = strports.service(port, f) 209 s.setServiceParent(self)
210
211 - def getPort(self):
212 # utility method for tests: figure out which TCP port we just opened. 213 return self.services[0]._port.getHost().port
214
215 - def requestAvatar(self, avatarID, mind, interface):
216 log.msg("%s got connection from user %s" % (self, avatarID)) 217 assert interface == pb.IPerspective 218 p = Try_Userpass_Perspective(self, avatarID) 219 return (pb.IPerspective, p, lambda: None)
220
221 -class Try_Userpass_Perspective(pbutil.NewCredPerspective):
222 - def __init__(self, parent, username):
223 self.parent = parent 224 self.username = username
225
226 - def perspective_try(self, branch, revision, patch, repository, project, 227 builderNames, properties={}, ):
228 log.msg("user %s requesting build on builders %s" % (self.username, 229 builderNames)) 230 # build the intersection of the request and our configured list 231 builderNames = self.parent.filterBuilderList(builderNames) 232 if not builderNames: 233 return 234 ss = SourceStamp(branch, revision, patch, repository=repository, 235 project=project) 236 reason = "'try' job from user %s" % self.username 237 238 # roll the specified props in with our inherited props 239 combined_props = Properties() 240 combined_props.updateFromProperties(self.parent.properties) 241 combined_props.update(properties, "try build") 242 243 status = self.parent.parent.parent.status 244 db = self.parent.parent.db 245 d = db.runInteraction(self._try, ss, builderNames, reason, 246 combined_props, db) 247 def _done(bsid): 248 # return a remotely-usable BuildSetStatus object 249 bss = BuildSetStatus(bsid, status, db) 250 from buildbot.status.client import makeRemote 251 r = makeRemote(bss) 252 #self.parent.parent.loop_done() # so it will notify builder loop 253 return r
254 d.addCallback(_done) 255 return d
256
257 - def _try(self, t, ss, builderNames, reason, combined_props, db):
258 ssid = db.get_sourcestampid(ss, t) 259 bsid = self.parent.create_buildset(ssid, reason, t, 260 props=combined_props, 261 builderNames=builderNames) 262 return bsid
263
264 - def perspective_getAvailableBuilderNames(self):
265 # Return a list of builder names that are configured 266 # for the try service 267 # This is mostly intended for integrating try services 268 # into other applications 269 return self.parent.listBuilderNames()
270