# HG changeset patch
# User wbell
# Date 1272121436 0
# Node ID b2272caf5ac440db7f9018e8f5d97a0460c66fa2
# Parent 306419d32527658819d2d8289d1baeb51b1f3f3f
Master now creates next step as in progress when the previous step is complete. While a build is running, the currently running build step now shows up in the ui as in-progress with an updating duration, which makes it much easier to figure out what's going on or if a build is hanging.
Closes #455.
diff --git a/bitten/master.py b/bitten/master.py
--- a/bitten/master.py
+++ b/bitten/master.py
@@ -242,6 +242,14 @@
self.log.info('Build slave %r initiated build %d', build.slave,
build.id)
+ # create the first step, mark it as in-progress.
+
+ recipe = Recipe(xmlio.parse(config.recipe))
+ stepname = recipe.__iter__().next().id
+
+ step = self._start_new_step(build, stepname)
+ step.insert()
+
self._send_response(req, 200, body, headers={
'Content-Type': 'application/x-bitten+xml',
'Content-Length': str(len(body)),
@@ -258,9 +266,11 @@
self._send_error(req, HTTP_BAD_REQUEST, 'XML parser error')
stepname = elem.attr['step']
+ # we should have created this step previously; if it hasn't,
+ # the master and slave are processing steps out of order.
step = BuildStep.fetch(self.env, build=build.id, name=stepname)
- if step:
- self._send_error(req, HTTP_CONFLICT, 'Build step already exists')
+ if not step:
+ self._send_error(req, HTTP_CONFLICT, 'Build step has not been created.')
recipe = Recipe(xmlio.parse(config.recipe))
index = None
@@ -280,14 +290,7 @@
db = self.env.get_db_cnx()
- step = BuildStep(self.env, build=build.id, name=stepname)
-
- # not a great way to determine the start/stop time of the
- # step, but it's a server time, which eliminates a bunch
- # of skew issues.
- now = int(time.time())
- step.started = now - float(elem.attr['duration'])
- step.stopped = now
+ step.stopped = int(time.time())
if elem.attr['status'] == 'failure':
self.log.warning('Build %s step %s failed', build.id, stepname)
@@ -297,6 +300,9 @@
else:
step.status = BuildStep.SUCCESS
step.errors += [error.gettext() for error in elem.children('error')]
+
+ # TODO: step.update(db=db)
+ step.delete(db=db)
step.insert(db=db)
# Collect log messages from the request body
@@ -362,6 +368,17 @@
build.status = Build.SUCCESS
build.update(db=db)
+ else:
+ # start the next step.
+ for num, recipe_step in enumerate(recipe):
+ if num == index + 1:
+ next_step = recipe_step
+ if next_step is None:
+ self._send_error(req, HTTP_FORBIDDEN,
+ 'Unable to find step after ' % stepname)
+
+ step = self._start_new_step(build, next_step.id)
+ step.insert(db=db)
db.commit()
@@ -376,3 +393,13 @@
'Location': req.abs_href.builds(
build.id, 'steps', stepname)})
+ def _start_new_step(self, build, stepname):
+ """Creates the in-memory representation for a newly started
+ step, ready to be persisted to the database.
+ """
+ step = BuildStep(self.env, build=build.id, name=stepname)
+ step.status = BuildStep.IN_PROGRESS
+ step.started = int(time.time())
+ step.stopped = 0
+
+ return step
diff --git a/bitten/tests/master.py b/bitten/tests/master.py
--- a/bitten/tests/master.py
+++ b/bitten/tests/master.py
@@ -261,7 +261,7 @@
def test_initiate_build(self):
config = BuildConfig(self.env, 'test', path='somepath', active=True,
- recipe='')
+ recipe='')
config.insert()
platform = TargetPlatform(self.env, config='test', name="Unix")
platform.rules.append(('family', 'posix'))
@@ -288,14 +288,14 @@
self.assertRaises(RequestDone, module.process_request, req)
self.assertEqual(200, outheaders['Status'])
- self.assertEqual('90', outheaders['Content-Length'])
+ self.assertEqual('112', outheaders['Content-Length'])
self.assertEqual('application/x-bitten+xml',
outheaders['Content-Type'])
self.assertEqual('attachment; filename=recipe_test_r123.xml',
outheaders['Content-Disposition'])
self.assertEqual('',
+ ' revision="123">',
outbody.getvalue())
# Make sure the started timestamp has been set
@@ -376,6 +376,9 @@
write=outbody.write,
incookie=Cookie('trac_auth=123'))
module = BuildMaster(self.env)
+
+ module._start_new_step(build, 'foo').insert()
+
assert module.match_request(req)
self.assertRaises(RequestDone, module.process_request, req)
@@ -428,6 +431,9 @@
write=outbody.write,
incookie=Cookie('trac_auth=123'))
module = BuildMaster(self.env)
+
+ module._start_new_step(build, 'foo').insert()
+
assert module.match_request(req)
self.assertRaises(RequestDone, module.process_request, req)
@@ -490,6 +496,9 @@
write=outbody.write,
incookie=Cookie('trac_auth=123'))
module = BuildMaster(self.env)
+
+ module._start_new_step(build, 'foo').insert()
+
assert module.match_request(req)
self.assertRaises(RequestDone, module.process_request, req)
@@ -563,6 +572,9 @@
write=outbody.write,
incookie=Cookie('trac_auth=123'))
module = BuildMaster(self.env)
+
+ module._start_new_step(build, 'foo').insert()
+
assert module.match_request(req)
self.assertRaises(RequestDone, module.process_request, req)
@@ -633,6 +645,9 @@
write=outbody.write,
incookie=Cookie('trac_auth='))
module = BuildMaster(self.env)
+
+ module._start_new_step(build, 'foo').insert()
+
assert module.match_request(req)
self.assertRaises(RequestDone, module.process_request, req)
@@ -646,7 +661,7 @@
assert not build.stopped
steps = list(BuildStep.select(self.env, build.id))
- self.assertEqual(0, len(steps))
+ self.assertEqual(1, len(steps))
def test_process_build_step_invalidated_build(self):
recipe = """
@@ -683,6 +698,9 @@
write=outbody.write,
incookie=Cookie('trac_auth=123'))
module = BuildMaster(self.env)
+
+ module._start_new_step(build, 'foo').insert()
+
assert module.match_request(req)
self.assertRaises(RequestDone, module.process_request, req)
@@ -692,7 +710,7 @@
assert not build.stopped
steps = list(BuildStep.select(self.env, build.id))
- self.assertEqual(1, len(steps))
+ self.assertEqual(2, len(steps))
# invalidate the build.
@@ -723,6 +741,9 @@
write=outbody.write,
incookie=Cookie('trac_auth=123'))
module = BuildMaster(self.env)
+
+ module._start_new_step(build, 'foo').insert()
+
assert module.match_request(req)
self.assertRaises(RequestDone, module.process_request, req)
@@ -763,6 +784,9 @@
write=outbody.write,
incookie=Cookie('trac_auth=123'))
module = BuildMaster(self.env)
+
+ module._start_new_step(build, 'foo').insert()
+
assert module.match_request(req)
self.assertRaises(RequestDone, module.process_request, req)
@@ -812,6 +836,9 @@
write=outbody.write,
incookie=Cookie('trac_auth=123'))
module = BuildMaster(self.env)
+
+ module._start_new_step(build, 'foo').insert()
+
assert module.match_request(req)
self.assertRaises(RequestDone, module.process_request, req)
@@ -906,6 +933,9 @@
incookie=Cookie('trac_auth='))
module = BuildMaster(self.env)
+
+ module._start_new_step(build, 'foo').insert()
+
assert module.match_request(req)
self.assertRaises(RequestDone, module.process_request, req)
@@ -933,6 +963,9 @@
incookie=Cookie('trac_auth='))
module = BuildMaster(self.env)
+
+ module._start_new_step(build, 'foo').insert()
+
assert module.match_request(req)
self.assertRaises(RequestDone, module.process_request, req)