changeset 761:b2272caf5ac4

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.
author wbell
date Sat, 24 Apr 2010 15:03:56 +0000
parents 306419d32527
children 5f0cfee44540
files bitten/master.py bitten/tests/master.py
diffstat 2 files changed, 75 insertions(+), 15 deletions(-) [+]
line wrap: on
line diff
--- 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
--- 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='<build></build>')
+                             recipe='<build><step id="s1"></step></build>')
         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('<build build="1" config="test" name="hal"'
                          ' path="somepath" platform="Unix"'
-                         ' revision="123"/>',
+                         ' revision="123"><step id="s1"/></build>',
                          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 = """<build>
@@ -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)
Copyright (C) 2012-2017 Edgewall Software