From f2107dd64c0d9173a401420cd61df89b252f555c Mon Sep 17 00:00:00 2001 From: Pierre Tardy Date: Sun, 6 Sep 2015 14:11:24 +0200 Subject: [PATCH] data api for rebuild Signed-off-by: Pierre Tardy --- master/buildbot/data/buildrequests.py | 15 ++++++++ master/buildbot/data/builds.py | 31 ++++++++++++--- master/buildbot/test/fake/fakedata.py | 3 ++ .../test/unit/test_data_buildrequests.py | 38 +++++++++++++++++++ master/buildbot/test/unit/test_data_builds.py | 22 ++++++++++- master/buildbot/test/util/endpoint.py | 6 ++- 6 files changed, 107 insertions(+), 8 deletions(-) diff --git a/master/buildbot/data/buildrequests.py b/master/buildbot/data/buildrequests.py index c546739283d..72da2a83562 100644 --- a/master/buildbot/data/buildrequests.py +++ b/master/buildbot/data/buildrequests.py @@ -243,3 +243,18 @@ def completeBuildRequests(self, brids, results, complete_at=None, @defer.inlineCallbacks def unclaimExpiredRequests(self, old, _reactor=reactor): yield self.master.db.buildrequests.unclaimExpiredRequests(old, _reactor=_reactor) + + @base.updateMethod + @defer.inlineCallbacks + def rebuildBuildrequest(self, buildrequest): + + # goal is to make a copy of the original buildset + buildset = yield self.master.data.get(('buildsets', buildrequest['buildsetid'])) + properties = yield self.master.data.get(('buildsets', buildrequest['buildsetid'], 'properties')) + ssids = [ss['ssid'] for ss in buildset['sourcestamps']] + res = yield self.master.data.updates.addBuildset(waited_for=False, scheduler=u'rebuild', + sourcestamps=ssids, reason=u'rebuild', + properties=properties, builderids=[buildrequest['builderid']], external_idstring=buildset['external_idstring'], + parent_buildid=buildset['parent_buildid'], parent_relationship=buildset['parent_relationship'], + ) + defer.returnValue(res) diff --git a/master/buildbot/data/builds.py b/master/buildbot/data/builds.py index ef85b2f8d2f..cede94bd9d2 100644 --- a/master/buildbot/data/builds.py +++ b/master/buildbot/data/builds.py @@ -13,9 +13,11 @@ # # Copyright Buildbot Team Members +from twisted.internet import defer + from buildbot.data import base from buildbot.data import types -from twisted.internet import defer +from buildbot.data.resultspec import ResultSpec class Db2DataMixin(object): @@ -100,12 +102,31 @@ def startConsuming(self, callback, options, kwargs): ('builds', str(buildid), None)) def control(self, action, args, kwargs): - if action != "stop": + # we convert the action into a mixedCase method name + action_method = getattr(self, "action" + action[0].upper() + action[1:]) + if action_method is None: raise ValueError("action: {} is not supported".format(action)) + return action_method(args, kwargs) + + @defer.inlineCallbacks + def actionStop(self, args, kwargs): + buildid = kwargs.get('buildid') + if buildid is None: + bldr = kwargs['builderid'] + num = kwargs['number'] + dbdict = yield self.master.db.builds.getBuildByNumber(bldr, num) + buildid = dbdict['id'] self.master.mq.produce(("control", "builds", - str(kwargs['buildid']), 'stop'), - dict(reason=kwargs.get('reason', 'no reason'))) - return defer.succeed(None) + str(buildid), 'stop'), + dict(reason=kwargs.get('reason', args.get('reason', 'no reason')))) + + @defer.inlineCallbacks + def actionRebuild(self, args, kwargs): + # we use the self.get and not self.data.get to be able to support all the pathPatterns of this endpoint + build = yield self.get(ResultSpec(), kwargs) + buildrequest = yield self.master.data.get(('buildrequests', build['buildrequestid'])) + res = yield self.master.data.updates.rebuildBuildrequest(buildrequest) + defer.returnValue(res) class BuildsEndpoint(Db2DataMixin, base.Endpoint): diff --git a/master/buildbot/test/fake/fakedata.py b/master/buildbot/test/fake/fakedata.py index 0060951e5e3..284a4bc10b4 100644 --- a/master/buildbot/test/fake/fakedata.py +++ b/master/buildbot/test/fake/fakedata.py @@ -225,6 +225,9 @@ def unclaimExpiredRequests(self, old, _reactor=reactor): validation.verifyType(self.testcase, "old", old, validation.IntValidator()) return defer.succeed(None) + def rebuildBuildrequest(self, buildrequest): + return defer.succeed(None) + def updateBuilderList(self, masterid, builderNames): self.testcase.assertEqual(masterid, self.master.masterid) for n in builderNames: diff --git a/master/buildbot/test/unit/test_data_buildrequests.py b/master/buildbot/test/unit/test_data_buildrequests.py index 1c70d9e90c4..5e92e07d957 100644 --- a/master/buildbot/test/unit/test_data_buildrequests.py +++ b/master/buildbot/test/unit/test_data_buildrequests.py @@ -494,3 +494,41 @@ def testUnclaimExpiredRequests(self): methodkwargs=dict(_reactor=reactor), expectedRes=None, expectedException=None) + + @defer.inlineCallbacks + def testRebuildBuildrequest(self): + self.master.db.insertTestData([ + fakedb.Builder(id=77, name='builder'), + fakedb.Master(id=88), + fakedb.Buildslave(id=13, name='sl'), + fakedb.Buildset(id=8822), + fakedb.SourceStamp(id=234), + fakedb.BuildsetSourceStamp(buildsetid=8822, sourcestampid=234), + fakedb.BuildRequest(id=82, buildsetid=8822, builderid=77), + fakedb.BuildsetProperty(buildsetid=8822, property_name='prop1', + property_value='["one", "fake1"]'), + fakedb.BuildsetProperty(buildsetid=8822, property_name='prop2', + property_value='["two", "fake2"]'), + ]) + buildrequest = yield self.master.data.get(('buildrequests', 82)) + new_bsid, brid_dict = yield self.rtype.rebuildBuildrequest(buildrequest) + + self.assertEqual(brid_dict.keys(), [77]) + buildrequest = yield self.master.data.get(('buildrequests', brid_dict[77])) + # submitted_at is the time of the test, so better not depend on it + self.assertIsNotNone(buildrequest['submitted_at']) + buildrequest['submitted_at'] = None + self.assertEqual(buildrequest, {'buildrequestid': 1001, 'complete': False, 'waited_for': False, + 'claimed_at': None, 'results': -1, 'claimed': False, + 'buildsetid': 200, 'complete_at': None, 'submitted_at': None, + 'builderid': 77, 'claimed_by_masterid': None, 'priority': 0}) + buildset = yield self.master.data.get(('buildsets', new_bsid)) + oldbuildset = yield self.master.data.get(('buildsets', 8822)) + + # assert same sourcestamp + self.assertEqual(buildset['sourcestamps'], oldbuildset['sourcestamps']) + buildset['sourcestamps'] = None + self.assertIsNotNone(buildset['submitted_at']) + buildset['submitted_at'] = None + self.assertEqual(buildset, {'bsid': 200, 'complete_at': None, 'submitted_at': None, + 'sourcestamps': None, 'parent_buildid': None, 'results': -1, 'parent_relationship': None, 'reason': u'rebuild', 'external_idstring': u'extid', 'complete': False}) diff --git a/master/buildbot/test/unit/test_data_builds.py b/master/buildbot/test/unit/test_data_builds.py index 3361ac8a4fb..b93b196a04b 100644 --- a/master/buildbot/test/unit/test_data_builds.py +++ b/master/buildbot/test/unit/test_data_builds.py @@ -46,7 +46,7 @@ def setUp(self): fakedb.Master(id=88), fakedb.Buildslave(id=13, name='sl'), fakedb.Buildset(id=8822), - fakedb.BuildRequest(id=82, buildsetid=8822), + fakedb.BuildRequest(id=82, buildsetid=8822, builderid=77), fakedb.Build(id=13, builderid=77, masterid=88, buildslaveid=13, buildrequestid=82, number=3), fakedb.Build(id=14, builderid=77, masterid=88, buildslaveid=13, @@ -92,6 +92,26 @@ def test_properties_injection(self): self.validateData(build) self.assertIn('properties', build) + @defer.inlineCallbacks + def test_action_stop(self): + yield self.callControl("stop", {}, ('builders', 77, 'builds', 5)) + self.master.mq.assertProductions([(('control', 'builds', '15', 'stop'), {'reason': 'no reason'})]) + + @defer.inlineCallbacks + def test_action_stop_reason(self): + yield self.callControl("stop", {'reason': 'because'}, ('builders', 77, 'builds', 5)) + self.master.mq.assertProductions([(('control', 'builds', '15', 'stop'), {'reason': 'because'})]) + + @defer.inlineCallbacks + def test_action_rebuild(self): + self.patch(self.master.data.updates, "rebuildBuildrequest", + mock.Mock(spec=self.master.data.updates.rebuildBuildrequest, return_value=(1, [2]))) + r = yield self.callControl("rebuild", {}, ('builders', 77, 'builds', 5)) + self.assertEqual(r, (1, [2])) + + buildrequest = yield self.master.data.get(('buildrequests', 82)) + self.master.data.updates.rebuildBuildrequest.assert_called_with(buildrequest) + class BuildsEndpoint(endpoint.EndpointMixin, unittest.TestCase): diff --git a/master/buildbot/test/util/endpoint.py b/master/buildbot/test/util/endpoint.py index 4e89643a6db..cabca19cf15 100644 --- a/master/buildbot/test/util/endpoint.py +++ b/master/buildbot/test/util/endpoint.py @@ -100,8 +100,10 @@ def callStartConsuming(self, options, kwargs, expected_filter=None): self.assertIdentical(qref.callback, cb) self.assertEqual(qref.filter, expected_filter) - def callControl(self, action, args, kwargs): - self.assertIn(set(kwargs), self.pathArgs) + def callControl(self, action, args, path): + self.assertIsInstance(path, tuple) + endpoint, kwargs = self.matcher[path] + self.assertIdentical(endpoint, self.ep) d = self.ep.control(action, args, kwargs) self.assertIsInstance(d, defer.Deferred) return d