[Buildbot-commits] buildbot/buildbot/status builder.py,1.69,1.70
Brian Warner
warner at users.sourceforge.net
Sun Oct 23 05:06:05 UTC 2005
Update of /cvsroot/buildbot/buildbot/buildbot/status
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv4351/buildbot/status
Modified Files:
builder.py
Log Message:
Revision: arch at buildbot.sf.net--2004/buildbot--dev--0--patch-358
Creator: Brian Warner <warner at lothar.com>
fix some upgrade problems, fix the forgotten-builds problem
* buildbot/status/builder.py (BuildStatus): derive from
styles.Versioned, fix upgrade of .sourceStamp attribute. Also set
the default (i.e. unknown) .slavename to "???" instead of None,
since even unknown slavenames need to be printed eventually.
(BuilderStatus): also derive from styles.Versioned . More
importantly, determine .nextBuildNumber at creation/unpickling
time by scanning the directory of saved BuildStatus instances and
choosing one larger than the highest-numbered one found. This
should fix the problem where random errors during upgrades cause
the buildbot to forget about earlier builds. .nextBuildNumber is
no longer stored in the pickle.
(Status.builderAdded): if we can't unpickle the BuilderStatus,
at least log the error. Also call Builder.determineNextBuildNumber
once the basedir is set.
* buildbot/master.py (BuildMaster.loadChanges): do
styles.doUpgrade afterwards, in case I decide to make Changes
derived from styles.Versioned some day and forget to make this
change later.
--This line, and those below, will be ignored--
Files to commit:
<can't compute list>
This list might be incomplete or outdated if editing the log
message was not invoked from an up-to-date changes buffer!
Index: builder.py
===================================================================
RCS file: /cvsroot/buildbot/buildbot/buildbot/status/builder.py,v
retrieving revision 1.69
retrieving revision 1.70
diff -u -d -r1.69 -r1.70
--- builder.py 16 Oct 2005 06:25:34 -0000 1.69
+++ builder.py 23 Oct 2005 05:06:02 -0000 1.70
@@ -894,11 +894,12 @@
loog.step = self
-class BuildStatus:
+class BuildStatus(styles.Versioned):
if implements:
implements(interfaces.IBuildStatus, interfaces.IStatusEvent)
else:
__implements__ = interfaces.IBuildStatus, interfaces.IStatusEvent
+ persistenceVersion = 1
source = None
reason = None
@@ -911,7 +912,7 @@
text = []
color = None
results = None
- slavename = None
+ slavename = "???"
# these lists/dicts are defined here so that unserialized instances have
# (empty) values. They are set in __init__ to new objects to make sure
@@ -1186,7 +1187,7 @@
return filename
def __getstate__(self):
- d = self.__dict__.copy()
+ d = styles.Versioned.__getstate__(self)
# for now, a serialized Build is always "finished". We will never
# save unfinished builds.
if not self.finished:
@@ -1202,18 +1203,21 @@
return d
def __setstate__(self, d):
- self.__dict__ = d
+ styles.Versioned.__setstate__(self, d)
# self.builder must be filled in by our parent when loading
for step in self.steps:
step.build = self
self.watchers = []
self.updates = {}
self.finishedWatchers = []
- if d.has_key('sourceStamp'):
- revision, patch = d['sourceStamp']
- changes = d.get('changes', [])
+
+ def upgradeToVersion1(self):
+ if hasattr(self, "sourceStamp"):
+ # the old .sourceStamp attribute wasn't actually very useful
+ maxChangeNumber, patch = self.sourceStamp
+ changes = getattr(self, 'changes', [])
source = sourcestamp.SourceStamp(branch=None,
- revision=revision,
+ revision=None,
patch=patch,
changes=changes)
self.source = source
@@ -1258,7 +1262,7 @@
-class BuilderStatus:
+class BuilderStatus(styles.Versioned):
"""I handle status information for a single process.base.Builder object.
That object sends status changes to me (frequently as Events), and I
provide them on demand to the various status recipients, like the HTML
@@ -1281,6 +1285,7 @@
implements(interfaces.IBuilderStatus)
else:
__implements__ = interfaces.IBuilderStatus,
+ persistenceVersion = 1
# these limit the amount of memory we consume, as well as the size of the
# main Builder pickle. The Build and LogFile pickles on disk must be
@@ -1291,7 +1296,6 @@
category = None
currentBigState = "offline" # or idle/waiting/interlocked/building
- nextBuildNumber = 0
basedir = None # filled in by our parent
def __init__(self, buildername, category=None):
@@ -1312,10 +1316,13 @@
self.buildCache = [] # TODO: age builds out of the cache
# persistence
- # TODO: why am I not using styles.Versioned for this?
def __getstate__(self):
- d = self.__dict__.copy()
+ # when saving, don't record transient stuff like what builds are
+ # currently running, because they won't be there when we start back
+ # up. Nor do we save self.watchers, nor anything that gets set by our
+ # parent like .basedir and .status
+ d = styles.Versioned.__getstate__(self)
d['watchers'] = []
del d['buildCache']
for b in self.currentBuilds:
@@ -1326,20 +1333,42 @@
del d['currentBigState']
del d['basedir']
del d['status']
+ del d['nextBuildNumber']
return d
def __setstate__(self, d):
- self.__dict__ = d
+ # when loading, re-initialize the transient stuff. Remember that
+ # upgradeToVersion1 and such will be called after this finishes.
+ styles.Versioned.__setstate__(self, d)
self.buildCache = []
self.currentBuilds = []
self.pendingBuilds = []
self.watchers = []
- if d.has_key('slavename'):
- self.slavenames = [self.slavename]
- del self.slavename
+ self.slavenames = []
# self.basedir must be filled in by our parent
# self.status must be filled in by our parent
+ def upgradeToVersion1(self):
+ if hasattr(self, 'slavename'):
+ self.slavenames = [self.slavename]
+ del self.slavename
+ if hasattr(self, 'nextBuildNumber'):
+ del self.nextBuildNumber # determineNextBuildNumber chooses this
+
+ def determineNextBuildNumber(self):
+ """Scan our directory of saved BuildStatus instances to determine
+ what our self.nextBuildNumber should be. Set it one larger than the
+ highest-numbered build we discover. This is called by the top-level
+ Status object shortly after we are created or loaded from disk.
+ """
+ existing_builds = [int(f)
+ for f in os.listdir(self.basedir)
+ if re.match("^\d+$", f)]
+ if existing_builds:
+ self.nextBuildNumber = max(existing_builds) + 1
+ else:
+ self.nextBuildNumber = 0
+
def saveYourself(self):
for b in self.buildCache:
if not b.isFinished:
@@ -1379,6 +1408,7 @@
filename = os.path.join(self.basedir, "%d" % number)
try:
build = pickle.load(open(filename, "r"))
+ styles.doUpgrade()
build.builder = self
# handle LogFiles from after 0.5.0 and before 0.6.5
build.upgradeLogfiles()
@@ -1523,7 +1553,9 @@
number = self.nextBuildNumber
self.nextBuildNumber += 1
# TODO: self.saveYourself(), to make sure we don't forget about the
- # build number we've just allocated
+ # build number we've just allocated. This is not quite as important
+ # as it was before we switch to determineNextBuildNumber, but I think
+ # it may still be useful to have the new build save itself.
s = BuildStatus(self, number)
s.waitUntilFinished().addCallback(self._buildFinished)
return s
@@ -1816,10 +1848,13 @@
builder_status = None
try:
builder_status = pickle.load(open(filename, "r"))
+ styles.doUpgrade()
except IOError:
log.msg("no saved status pickle, creating a new one")
except:
log.msg("error while loading status pickle, creating a new one")
+ log.msg("error follows:")
+ log.err()
if not builder_status:
builder_status = BuilderStatus(name, category)
builder_status.addPointEvent(["builder", "created"])
@@ -1833,6 +1868,8 @@
if not os.path.isdir(builder_status.basedir):
os.mkdir(builder_status.basedir)
+ builder_status.determineNextBuildNumber()
+
builder_status.setBigState("offline")
for t in self.watchers:
More information about the Commits
mailing list