# 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)