# HG changeset patch # User wbell # Date 1205450672 0 # Node ID 07148aa7667e0498b563ee673e70bef697ae3723 # Parent 6718f9a5c1f1d52e330f2926f54f185135eb5124 Don't accept build step results for invalidated builds or from the wrong slave. Remerge of [520] with fixed tests and new functional tests. diff --git a/bitten/master.py b/bitten/master.py --- a/bitten/master.py +++ b/bitten/master.py @@ -193,6 +193,9 @@ raise HTTPBadRequest('XML parser error') stepname = elem.attr['step'] + # make sure it's the right slave. + if build.status != Build.IN_PROGRESS or build.slave_info.get(Build.IP_ADDRESS) != req.remote_addr: + raise HTTPForbidden('Build %s has been invalidated for host %s.' % (build.id, req.remote_addr)) step = BuildStep.fetch(self.env, build=build.id, name=stepname) if step: diff --git a/bitten/tests/master.py b/bitten/tests/master.py --- a/bitten/tests/master.py +++ b/bitten/tests/master.py @@ -18,7 +18,7 @@ from trac.perm import PermissionCache, PermissionSystem from trac.test import EnvironmentStub, Mock from trac.web.api import HTTPBadRequest, HTTPMethodNotAllowed, HTTPNotFound, \ - RequestDone + HTTPForbidden, RequestDone from trac.web.href import Href from bitten.master import BuildMaster @@ -267,7 +267,8 @@ BuildConfig(self.env, 'test', path='somepath', active=True, recipe=recipe).insert() build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42, - started=42) + started=42, status=Build.IN_PROGRESS) + build.slave_info[Build.IP_ADDRESS] = '127.0.0.1'; build.insert() inbody = StringIO(""" + + +""" + BuildConfig(self.env, 'test', path='somepath', active=True, + recipe=recipe).insert() + build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42, + started=42, status=Build.IN_PROGRESS) + build.slave_info[Build.IP_ADDRESS] = '192.168.1.1'; + build.insert() + + inbody = StringIO(""" + + Doing stuff + Ouch that hurt + +""") + outheaders = {} + outbody = StringIO() + req = Mock(method='POST', base_path='', + path_info='/builds/%d/steps/' % build.id, + href=Href('/trac'), abs_href=Href('http://example.org/trac'), + remote_addr='127.0.0.1', args={}, + perm=PermissionCache(self.env, 'hal'), + read=inbody.read, + send_response=lambda x: outheaders.setdefault('Status', x), + send_header=lambda x, y: outheaders.setdefault(x, y), + write=outbody.write) + module = BuildMaster(self.env) + assert module.match_request(req) + try: + module.process_request(req) + self.fail('Expected HTTPForbidden') + except HTTPForbidden, e: + self.assertEqual('Build 1 has been invalidated for host 127.0.0.1.', e.detail) + + build = Build.fetch(self.env, build.id) + self.assertEqual(Build.IN_PROGRESS, build.status) + assert not build.stopped + + steps = list(BuildStep.select(self.env, build.id)) + self.assertEqual(0, len(steps)) + + + def test_process_build_step_invalidated_build(self): + recipe = """ + + + + +""" + BuildConfig(self.env, 'test', path='somepath', active=True, + recipe=recipe).insert() + build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42, + started=42, status=Build.IN_PROGRESS) + build.slave_info[Build.IP_ADDRESS] = '127.0.0.1'; + build.insert() + + inbody = StringIO(""" + + Doing stuff + Ouch that hurt + +""") + outheaders = {} + outbody = StringIO() + req = Mock(method='POST', base_path='', + path_info='/builds/%d/steps/' % build.id, + href=Href('/trac'), abs_href=Href('http://example.org/trac'), + remote_addr='127.0.0.1', args={}, + perm=PermissionCache(self.env, 'hal'), + read=inbody.read, + send_response=lambda x: outheaders.setdefault('Status', x), + send_header=lambda x, y: outheaders.setdefault(x, y), + write=outbody.write) + module = BuildMaster(self.env) + assert module.match_request(req) + try: + module.process_request(req) + self.fail('Expected RequestDone') + except RequestDone: + build = Build.fetch(self.env, build.id) + self.assertEqual(Build.IN_PROGRESS, build.status) + assert not build.stopped + + steps = list(BuildStep.select(self.env, build.id)) + self.assertEqual(1, len(steps)) + + # invalidate the build. + + build = Build.fetch(self.env, build.id) + build.slave = None + build.status = Build.PENDING + build.update() + + # have this slave submit more data. + inbody = StringIO(""" + + This is a step after invalidation + +""") + outheaders = {} + outbody = StringIO() + req = Mock(method='POST', base_path='', + path_info='/builds/%d/steps/' % build.id, + href=Href('/trac'), abs_href=Href('http://example.org/trac'), + remote_addr='127.0.0.1', args={}, + perm=PermissionCache(self.env, 'hal'), + read=inbody.read, + send_response=lambda x: outheaders.setdefault('Status', x), + send_header=lambda x, y: outheaders.setdefault(x, y), + write=outbody.write) + module = BuildMaster(self.env) + assert module.match_request(req) + try: + module.process_request(req) + self.fail('Build was invalidated. Should fail.'); + except HTTPForbidden, e: + self.assertEqual('Build 1 has been invalidated for host 127.0.0.1.', e.detail) + + build = Build.fetch(self.env, build.id) + self.assertEqual(Build.PENDING, build.status) + def test_process_build_step_failure(self): recipe = """ @@ -439,7 +572,8 @@ BuildConfig(self.env, 'test', path='somepath', active=True, recipe=recipe).insert() build = Build(self.env, 'test', '123', 1, slave='hal', rev_time=42, - started=42) + started=42, status=Build.IN_PROGRESS) + build.slave_info[Build.IP_ADDRESS] = '127.0.0.1'; build.insert() inbody = StringIO("""