Skip to content

Commit

Permalink
Merge pull request #7261 from mokibit/buildset-tracks-its-rebuilt-bui…
Browse files Browse the repository at this point in the history
…ldid

Add 'rebuilt_buildid' to buildsets for tracking rebuilt build id
  • Loading branch information
p12tic committed Dec 8, 2023
2 parents a9500f7 + e20d4ae commit 9cb27b5
Show file tree
Hide file tree
Showing 20 changed files with 363 additions and 15 deletions.
12 changes: 12 additions & 0 deletions master/buildbot/data/buildrequests.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,24 @@ def rebuildBuildrequest(self, buildrequest):
buildset = yield self.master.data.get(('buildsets', buildrequest['buildsetid']))
properties = yield self.master.data.get(('buildsets', buildrequest['buildsetid'],
'properties'))
# use original build id: after rebuild, it is saved in new buildset `rebuilt_buildid` column
builds = yield self.master.data.get(('buildrequests', buildrequest['buildrequestid'],
'builds'))
# if already rebuilt build of the same initial build is rebuilt again only save the build
# id of the initial build
if len(builds) != 0 and buildset['rebuilt_buildid'] is None:
print("Length = ", len(builds))
rebuilt_buildid = builds[0]['buildid']
else:
rebuilt_buildid = buildset['rebuilt_buildid']

ssids = [ss['ssid'] for ss in buildset['sourcestamps']]
res = yield self.master.data.updates.addBuildset(
waited_for=False, scheduler='rebuild', sourcestamps=ssids, reason='rebuild',
properties=properties,
builderids=[buildrequest['builderid']],
external_idstring=buildset['external_idstring'],
rebuilt_buildid=rebuilt_buildid,
parent_buildid=buildset['parent_buildid'],
parent_relationship=buildset['parent_relationship'])
return res
8 changes: 6 additions & 2 deletions master/buildbot/data/buildsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def getSs(ssid):
'bsid': 'buildsets.id',
'external_idstring': 'buildsets.external_idstring',
'reason': 'buildsets.reason',
'rebuilt_buildid': 'buildsets.rebuilt_buildid',
'submitted_at': 'buildsets.submitted_at',
'complete': 'buildsets.complete',
'complete_at': 'buildsets.complete_at',
Expand Down Expand Up @@ -123,6 +124,7 @@ class EntityType(types.Entity):
bsid = types.Integer()
external_idstring = types.NoneOk(types.String())
reason = types.String()
rebuilt_buildid = types.NoneOk(types.Integer())
submitted_at = types.Integer()
complete = types.Boolean()
complete_at = types.NoneOk(types.Integer())
Expand All @@ -137,7 +139,7 @@ class EntityType(types.Entity):
@base.updateMethod
@defer.inlineCallbacks
def addBuildset(self, waited_for, scheduler=None, sourcestamps=None, reason='',
properties=None, builderids=None, external_idstring=None,
properties=None, builderids=None, external_idstring=None, rebuilt_buildid=None,
parent_buildid=None, parent_relationship=None, priority=0):
if sourcestamps is None:
sourcestamps = []
Expand All @@ -147,7 +149,7 @@ def addBuildset(self, waited_for, scheduler=None, sourcestamps=None, reason='',
builderids = []
submitted_at = int(self.master.reactor.seconds())
bsid, brids = yield self.master.db.buildsets.addBuildset(
sourcestamps=sourcestamps, reason=reason,
sourcestamps=sourcestamps, reason=reason, rebuilt_buildid=rebuilt_buildid,
properties=properties, builderids=builderids,
waited_for=waited_for, external_idstring=external_idstring,
submitted_at=epoch2datetime(submitted_at),
Expand All @@ -174,6 +176,7 @@ def addBuildset(self, waited_for, scheduler=None, sourcestamps=None, reason='',
"external_idstring": external_idstring,
"reason": reason,
"parent_buildid": parent_buildid,
"rebuilt_buildid": rebuilt_buildid,
"submitted_at": submitted_at,
"complete": False,
"complete_at": None,
Expand Down Expand Up @@ -245,6 +248,7 @@ def maybeBuildsetComplete(self, bsid):
"bsid": bsid,
"external_idstring": bsdict['external_idstring'],
"reason": bsdict['reason'],
"rebuilt_buildid": bsdict['rebuilt_buildid'],
"sourcestamps": sourcestamps,
"submitted_at": bsdict['submitted_at'],
"complete": True,
Expand Down
4 changes: 3 additions & 1 deletion master/buildbot/db/buildsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class BuildsetsConnectorComponent(base.DBConnectorComponent):

@defer.inlineCallbacks
def addBuildset(self, sourcestamps, reason, properties, builderids,
waited_for, external_idstring=None, submitted_at=None,
waited_for, external_idstring=None, submitted_at=None, rebuilt_buildid=None,
parent_buildid=None, parent_relationship=None, priority=0):
if submitted_at is not None:
submitted_at = datetime2epoch(submitted_at)
Expand Down Expand Up @@ -75,6 +75,7 @@ def thd(conn):
r = conn.execute(buildsets_tbl.insert(), {
"submitted_at": submitted_at,
"reason": reason,
"rebuilt_buildid": rebuilt_buildid,
"complete": 0,
"complete_at": None,
"results": -1,
Expand Down Expand Up @@ -250,6 +251,7 @@ def _thd_row2dict(self, conn, row):
submitted_at=epoch2datetime(row.submitted_at),
complete=bool(row.complete),
complete_at=epoch2datetime(row.complete_at),
rebuilt_buildid=row.rebuilt_buildid,
results=row.results,
bsid=row.id, sourcestamps=sourcestamps,
parent_buildid=row.parent_buildid,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# This file is part of Buildbot. Buildbot is free software: you can
# redistribute it and/or modify it under the terms of the GNU General Public
# License as published by the Free Software Foundation, version 2.
#
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along with
# this program; if not, write to the Free Software Foundation, Inc., 51
# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# Copyright Buildbot Team Members

"""add rebuilt_buildid column to buildsets table
Revision ID: 065
Revises: 064
"""
import sqlalchemy as sa
from alembic import op

# revision identifiers, used by Alembic.
revision = '065'
down_revision = '064'
branch_labels = None
depends_on = None


def upgrade():
with op.batch_alter_table("buildsets") as batch_op:
batch_op.add_column(
sa.Column("rebuilt_buildid", sa.Integer,
sa.ForeignKey("builds.id", use_alter=True, name="rebuilt_buildid",
ondelete='SET NULL'),
nullable=True),
)


def downgrade():
op.drop_column("buildsets", "rebuilt_buildid")
8 changes: 8 additions & 0 deletions master/buildbot/db/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,12 @@ class Model(base.DBConnectorComponent):
# text describing what is the relationship with the build
# could be 'triggered from', 'rebuilt from', 'inherited from'
sa.Column('parent_relationship', sa.Text),

# optional rebuilt build id
sa.Column('rebuilt_buildid', sa.Integer,
sa.ForeignKey('builds.id', use_alter=True,
name='rebuilt_buildid', ondelete='SET NULL'),
nullable=True),
)

# Tables related to change sources
Expand Down Expand Up @@ -982,6 +988,8 @@ class Model(base.DBConnectorComponent):
{"unique": False, "column_names": ['sourcestampid'], "name": 'sourcestampid'}),
('buildsets',
{"unique": False, "column_names": ['parent_buildid'], "name": 'parent_buildid'}),
('buildsets',
{"unique": False, "column_names": ['rebuilt_buildid'], "name": 'rebuilt_buildid'}),
('builders_tags',
{"unique": False, "column_names": ['tagid'], "name": 'tagid'}),
('changes', {
Expand Down
5 changes: 5 additions & 0 deletions master/buildbot/spec/types/buildset.raml
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,11 @@ properties:
reason:
description: the reason this buildset was scheduled
type: string
rebuilt_buildid?:
description: |
optional id of a build which was rebuilt or None if there was no rebuild. In case of
repeated rebuilds, only initial build id is tracked
type: integer
results?:
description: the results of the buildset (see :ref:`Build-Result-Codes`), or None if not complete
type: integer
Expand Down
3 changes: 2 additions & 1 deletion master/buildbot/test/fake/fakedata.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ def expireMasters(self, forceHouseKeeping=False):

@defer.inlineCallbacks
def addBuildset(self, waited_for, scheduler=None, sourcestamps=None, reason='',
properties=None, builderids=None, external_idstring=None,
properties=None, builderids=None, external_idstring=None, rebuilt_buildid=None,
parent_buildid=None, parent_relationship=None, priority=0):
if sourcestamps is None:
sourcestamps = []
Expand Down Expand Up @@ -167,6 +167,7 @@ def addBuildset(self, waited_for, scheduler=None, sourcestamps=None, reason='',
sourcestamps=sourcestamps, reason=reason,
properties=properties, builderids=builderids,
waited_for=waited_for, external_idstring=external_idstring,
rebuilt_buildid=rebuilt_buildid,
parent_buildid=parent_buildid, parent_relationship=parent_relationship)
return (bsid, brids)

Expand Down
11 changes: 6 additions & 5 deletions master/buildbot/test/fakedb/buildsets.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,17 @@

class Buildset(Row):
table = "buildsets"
foreignKeys = ('rebuilt_buildid',)

id_column = 'id'

def __init__(self, id=None, external_idstring='extid', reason='because',
submitted_at=12345678, complete=0, complete_at=None, results=-1,
parent_buildid=None, parent_relationship=None):
rebuilt_buildid=None, parent_buildid=None, parent_relationship=None):
super().__init__(id=id, external_idstring=external_idstring, reason=reason,
submitted_at=submitted_at, complete=complete, complete_at=complete_at,
results=results, parent_buildid=parent_buildid,
parent_relationship=parent_relationship)
results=results, rebuilt_buildid=rebuilt_buildid,
parent_buildid=parent_buildid, parent_relationship=parent_relationship)


class BuildsetProperty(Row):
Expand Down Expand Up @@ -97,7 +98,7 @@ def _newBsid(self):

@defer.inlineCallbacks
def addBuildset(self, sourcestamps, reason, properties, builderids, waited_for,
external_idstring=None, submitted_at=None,
external_idstring=None, submitted_at=None, rebuilt_buildid=None,
parent_buildid=None, parent_relationship=None, priority=0):
# We've gotten this wrong a couple times.
assert isinstance(
Expand All @@ -121,7 +122,7 @@ def addBuildset(self, sourcestamps, reason, properties, builderids, waited_for,
# make up a row and keep its dictionary, with the properties tacked on
bsrow = Buildset(id=bsid, reason=reason,
external_idstring=external_idstring,
submitted_at=submitted_at,
submitted_at=submitted_at, rebuilt_buildid=rebuilt_buildid,
parent_buildid=parent_buildid, parent_relationship=parent_relationship)

self.buildsets[bsid] = bsrow.values.copy()
Expand Down
3 changes: 2 additions & 1 deletion master/buildbot/test/fakedb/row.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,8 @@ def checkForeignKeys(self, db, t):
"schedulerid": db.schedulers.getScheduler,
"brid": db.buildrequests.getBuildRequest,
"stepid": db.steps.getStep,
"masterid": db.masters.getMaster
"masterid": db.masters.getMaster,
"rebuilt_buildid": db.builds.getBuild
}
for foreign_key in self.foreignKeys:
if foreign_key in accessors:
Expand Down
128 changes: 127 additions & 1 deletion master/buildbot/test/unit/data/test_buildrequests.py
Original file line number Diff line number Diff line change
Expand Up @@ -551,10 +551,136 @@ def testRebuildBuildrequest(self):
self.assertEqual(buildset, {'bsid': 200, 'complete_at': None, 'submitted_at': 0,
'sourcestamps': None, 'parent_buildid': None,
'results': -1, 'parent_relationship': None,
'reason': 'rebuild',
'reason': 'rebuild', 'rebuilt_buildid': None,
'external_idstring': 'extid',
'complete': False})

properties = yield self.master.data.get(('buildsets', new_bsid, 'properties'))
self.assertEqual(
properties, {'prop1': ('one', 'fake1'), 'prop2': ('two', 'fake2')})

@defer.inlineCallbacks
def test_rebuild_buildrequest_rebuilt_build(self):
self.master.db.insert_test_data(
[
fakedb.Builder(id=77, name="builder"),
fakedb.Master(id=88),
fakedb.Worker(id=13, name="wrk"),
fakedb.Buildset(id=8822),
fakedb.SourceStamp(id=234),
fakedb.BuildsetSourceStamp(buildsetid=8822, sourcestampid=234),
fakedb.BuildRequest(id=82, buildsetid=8822, builderid=77),
fakedb.Build(id=123, buildrequestid=82),
]
)
buildrequest = yield self.master.data.get(("buildrequests", 82))
new_bsid, brid_dict = yield self.rtype.rebuildBuildrequest(buildrequest)

self.assertEqual(list(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.assertEqual(
buildrequest,
{
"buildrequestid": 1001,
"complete": False,
"waited_for": False,
"claimed_at": None,
"results": -1,
"claimed": False,
"buildsetid": 200,
"complete_at": None,
"submitted_at": epoch2datetime(0),
"builderid": 77,
"claimed_by_masterid": None,
"priority": 0,
"properties": None,
},
)
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.assertEqual(
buildset,
{
"bsid": 200,
"complete_at": None,
"submitted_at": 0,
"sourcestamps": None,
"parent_buildid": None,
"results": -1,
"parent_relationship": None,
"reason": "rebuild",
"rebuilt_buildid": 123,
"external_idstring": "extid",
"complete": False,
},
)

@defer.inlineCallbacks
def test_rebuild_buildrequest_repeated_rebuilt_build(self):
self.master.db.insert_test_data(
[
fakedb.Builder(id=77, name="builder"),
fakedb.Master(id=88),
fakedb.Worker(id=13, name="wrk"),
fakedb.Buildset(id=8821),
# build already has been rebuilt from build_id = 122
fakedb.Buildset(id=8822, rebuilt_buildid=122),
fakedb.SourceStamp(id=234),
fakedb.BuildsetSourceStamp(buildsetid=8822, sourcestampid=234),
fakedb.BuildRequest(id=81, buildsetid=8821, builderid=77),
fakedb.BuildRequest(id=82, buildsetid=8822, builderid=77),
fakedb.Build(id=122, buildrequestid=81),
fakedb.Build(id=123, buildrequestid=82),
]
)
buildrequest = yield self.master.data.get(("buildrequests", 82))
new_bsid, brid_dict = yield self.rtype.rebuildBuildrequest(buildrequest)

self.assertEqual(list(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.assertEqual(
buildrequest,
{
"buildrequestid": 1001,
"complete": False,
"waited_for": False,
"claimed_at": None,
"results": -1,
"claimed": False,
"buildsetid": 200,
"complete_at": None,
"submitted_at": epoch2datetime(0),
"builderid": 77,
"claimed_by_masterid": None,
"priority": 0,
"properties": None,
},
)
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.assertEqual(
buildset,
{
"bsid": 200,
"complete_at": None,
"submitted_at": 0,
"sourcestamps": None,
"parent_buildid": None,
"results": -1,
"parent_relationship": None,
"reason": "rebuild",
"rebuilt_buildid": 122,
"external_idstring": "extid",
"complete": False,
},
)

0 comments on commit 9cb27b5

Please sign in to comment.