Hereby I submit an extension for Buildbot to allow exclusive claiming of a
lock. The patch consists of two parts. The first part, harmless2.patch
contains some rather harmless changes that you may want to do even if the
extension is not accepted.
The second part lockaccess2.patch builds on the first part and contains the actual extension.
The fundamental extension is that with using a lock you now need to specify
how (in which mode) you want access.
The extension allows two modes, namely 'counting' and 'exclusive'. The former
is what Buildbot 0.7.7 is currently supporting, namely allowing up-to
'maxCount' users. The latter claims the lock exclusively (with a MasterLock one
user globally, with a SlaveLock, one user per slave).
For backwards compability, using a lock without specifying an access mode
defaults to 'counting' mode.
Example:
In the next example, a limit of at most two builds is enforced. The cleanup
process however needs exclusive access to prevent interference with the
build processes.
from buildbot import locks
from buildbot.steps import source, shell
from buildbot.process import factory
build_lock = locks.MasterLock("builds", maxCount=2)
fb = factory.BuildFactory()
fb.addStep(source.SVN(svnurl="http://example.org/svn/Trunk"))
fb.addStep(shell.ShellCommand(command=["make", "all"]))
b1 = {'name': 'b1', 'slavename': 'bot-1', 'builddir' : 'b1',
'factory': fb, 'locks': [build_lock.access('counting')] }
b2 = {'name': 'b2', 'slavename': 'bot-2', 'builddir' : 'b2',
'factory': fb, 'locks': [build_lock.access('counting')] }
b3 = {'name': 'b3', 'slavename': 'bot-3', 'builddir' : 'b3',
'factory': fb, 'locks': [build_lock.access('counting')] }
fc = factory.BuildFactory()
fc.addStep(shell.ShellCommand(command=["cleanup"]))
cl = {'name': 'clean', 'slavename': 'bot-1', 'builddir' :clean',
'factory': fc, 'locks': [build_lock.access('exclusive')] }
c['builders'] = [b1, b2, b3, cl]
Note the addition of ".access(MODE)" while using the lock.
harmless2.patch:
- Added several doc-strings in locks.BaseLock class
- Added a locks.BaseLockId abstract base class on top of the MasterLock and SlaveLock classes
- Added some assertion checking in master.py. Needed to import 'locks'. That name was already in use, so renamed a few variables to prevent clashes.
- util.ComparableMixin class: Computed comparisons more than once. Stored the result in an intermediate variable
- Several buildbot.texinfo fixes (a typo, fixing of "builddir='XXX'" to "'builddir': 'XXX'" which works better in a dictionary, and replaced deprecated html.WaterFall() use with html.WebStatus() call).
lockaccess2.patch:
- Added a locks.LockAccess class, which represents in which a lock should be accessed. It contains the mode, and a lockid (a reference to MasterLock or SlaveLock instance).
- Almost complete rewrite of the locks.BaseLock class. The methods associated with use now have an additional 'access' parameter for the LockAccess instance. This data is also stored in the 'waiting' and 'owners' lists.
- Extension of the locks.BaseLockId class with an 'access(mode)' method, so the user can specify the access mode upon use. There is also a 'defaultAccess()' method that is used when the user doesn't specify the mode.
- In master.py, the LockAccess instance is removed during checking of locks.
- In process/base.py, the self.locks for step-locks now contains a tuple (real-lock, LockAccess instance). This combination is also used when querying/getting/releasing the locks.
- Same in process/buildstep.py for builder locks.
- Fixed unit-tests to also use a LockAccess instance. Some tests are duplicated so both modes are tested. The names of the tests have been extended to state more precisely what is tested. In some cases, only the counting mode is tested (exclusive tests with sharing locks makes no sense).
- Added 2 tests to verify that an exclsuive lock and a counting lock cannot exist together.
- Extended buildbot.texinfo with the new functionality. I didn't re-format the lines to make more clear what I have changed. You may want to do this after reviewing my additions.