From f2d9e6c06704addc00f58f254d6bc71e7018d32c Mon Sep 17 00:00:00 2001
From: Jana Cupova <jcupova@redhat.com>
Date: Apr 13 2021 17:20:55 +0000
Subject: Repo info with task id


Fixes: https://pagure.io/koji/issue/888

---

diff --git a/builder/kojid b/builder/kojid
index bad674d..9c2cd01 100755
--- a/builder/kojid
+++ b/builder/kojid
@@ -5374,7 +5374,7 @@ class NewRepoTask(BaseTaskHandler):
         # generate debuginfo repo if requested or if specified in sidetag's extra
         if debuginfo or tinfo['extra'].get('with_debuginfo'):
             kwargs['with_debuginfo'] = True
-        repo_id, event_id = self.session.host.repoInit(tinfo['id'], **kwargs)
+        repo_id, event_id = self.session.host.repoInit(tinfo['id'], task_id=self.id, **kwargs)
         path = koji.pathinfo.repo(repo_id, tinfo['name'])
         if not os.path.isdir(path):
             raise koji.GenericError("Repo directory missing: %s" % path)
diff --git a/docs/schema-upgrade-1.24-1.25.sql b/docs/schema-upgrade-1.24-1.25.sql
new file mode 100644
index 0000000..0ddf6f2
--- /dev/null
+++ b/docs/schema-upgrade-1.24-1.25.sql
@@ -0,0 +1,9 @@
+-- upgrade script to migrate the Koji database schema
+-- from version 1.24 to 1.25
+
+
+BEGIN;
+
+ALTER TABLE repo ADD COLUMN task_id INTEGER NULL REFERENCES task(id);
+
+COMMIT;
diff --git a/docs/schema.sql b/docs/schema.sql
index 3e5c446..d9eca28 100644
--- a/docs/schema.sql
+++ b/docs/schema.sql
@@ -454,7 +454,8 @@ CREATE TABLE repo (
 	create_event INTEGER NOT NULL REFERENCES events(id) DEFAULT get_event(),
 	tag_id INTEGER NOT NULL REFERENCES tag(id),
 	state INTEGER,
-	dist BOOLEAN DEFAULT 'false'
+	dist BOOLEAN DEFAULT 'false',
+	task_id INTEGER NULL REFERENCES task(id)
 ) WITHOUT OIDS;
 
 -- external yum repos
diff --git a/hub/kojihub.py b/hub/kojihub.py
index eba3fff..16d421b 100644
--- a/hub/kojihub.py
+++ b/hub/kojihub.py
@@ -2521,7 +2521,8 @@ def maven_tag_archives(tag_id, event_id=None, inherit=True):
     return _iter_archives()
 
 
-def repo_init(tag, with_src=False, with_debuginfo=False, event=None, with_separate_src=False):
+def repo_init(tag, task_id=None, with_src=False, with_debuginfo=False, event=None,
+              with_separate_src=False):
     """Create a new repo entry in the INIT state, return full repo data
 
     Returns a dictionary containing
@@ -2532,7 +2533,7 @@ def repo_init(tag, with_src=False, with_debuginfo=False, event=None, with_separa
     tinfo = get_tag(tag, strict=True, event=event)
     koji.plugin.run_callbacks('preRepoInit', tag=tinfo, with_src=with_src,
                               with_debuginfo=with_debuginfo, event=event, repo_id=None,
-                              with_separate_src=with_separate_src)
+                              with_separate_src=with_separate_src, task_id=task_id)
     tag_id = tinfo['id']
     repo_arches = {}
     if with_separate_src:
@@ -2552,7 +2553,7 @@ def repo_init(tag, with_src=False, with_debuginfo=False, event=None, with_separa
         event_time = _singleValue(q, locals(), strict=True)
         event_id = event
     insert = InsertProcessor('repo')
-    insert.set(id=repo_id, create_event=event_id, tag_id=tag_id, state=state)
+    insert.set(id=repo_id, create_event=event_id, tag_id=tag_id, state=state, task_id=task_id)
     insert.execute()
     # Need to pass event_id because even though this is a single transaction,
     # it is possible to see the results of other committed transactions
@@ -2578,6 +2579,7 @@ def repo_init(tag, with_src=False, with_debuginfo=False, event=None, with_separa
         'id': repo_id,
         'tag': tinfo['name'],
         'tag_id': tinfo['id'],
+        'task_id': task_id,
         'event_id': event_id,
         'with_src': with_src,
         'with_separate_src': with_separate_src,
@@ -2671,7 +2673,7 @@ def repo_init(tag, with_src=False, with_debuginfo=False, event=None, with_separa
 
     koji.plugin.run_callbacks('postRepoInit', tag=tinfo, with_src=with_src,
                               with_debuginfo=with_debuginfo, event=event, repo_id=repo_id,
-                              with_separate_src=with_separate_src)
+                              with_separate_src=with_separate_src, task_id=task_id)
     return [repo_id, event_id]
 
 
@@ -2785,6 +2787,7 @@ def repo_info(repo_id, strict=False):
     fields = (
         ('repo.id', 'id'),
         ('repo.state', 'state'),
+        ('repo.task_id', 'task_id'),
         ('repo.create_event', 'create_event'),
         ('events.time', 'creation_time'),  # for compatibility with getRepo
         ('EXTRACT(EPOCH FROM events.time)', 'create_ts'),
@@ -2874,6 +2877,7 @@ def get_active_repos():
     fields = (
         ('repo.id', 'id'),
         ('repo.state', 'state'),
+        ('repo.task_id', 'task_id'),
         ('repo.create_event', 'create_event'),
         ('EXTRACT(EPOCH FROM events.time)', 'create_ts'),
         ('repo.tag_id', 'tag_id'),
@@ -12116,9 +12120,9 @@ class RootExports(object):
         else:
             id = get_tag_id(tag, strict=True)
 
-        fields = ['repo.id', 'repo.state', 'repo.create_event', 'events.time',
+        fields = ['repo.id', 'repo.state', 'repo.task_id', 'repo.create_event', 'events.time',
                   'EXTRACT(EPOCH FROM events.time)', 'repo.dist']
-        aliases = ['id', 'state', 'create_event', 'creation_time', 'create_ts', 'dist']
+        aliases = ['id', 'state', 'task_id', 'create_event', 'creation_time', 'create_ts', 'dist']
         joins = ['events ON repo.create_event = events.id']
         clauses = ['repo.tag_id = %(id)i']
         if dist:
@@ -14585,13 +14589,13 @@ class HostExports(object):
 
         return br.updateArchiveList(archives, project)
 
-    def repoInit(self, tag, with_src=False, with_debuginfo=False, event=None,
+    def repoInit(self, tag, task_id=None, with_src=False, with_debuginfo=False, event=None,
                  with_separate_src=False):
         """Initialize a new repo for tag"""
         host = Host()
         host.verify()
-        return repo_init(tag, with_src=with_src, with_debuginfo=with_debuginfo, event=event,
-                         with_separate_src=with_separate_src)
+        return repo_init(tag, task_id=task_id, with_src=with_src, with_debuginfo=with_debuginfo,
+                         event=event, with_separate_src=with_separate_src)
 
     def repoDone(self, repo_id, data, expire=False):
         """Finalize a repo
diff --git a/plugins/hub/protonmsg.py b/plugins/hub/protonmsg.py
index db57198..0f5521a 100644
--- a/plugins/hub/protonmsg.py
+++ b/plugins/hub/protonmsg.py
@@ -282,7 +282,8 @@ def prep_repo_init(cbtype, *args, **kws):
     address = 'repo.init'
     props = {'type': cbtype[4:],
              'tag': kws['tag']['name'],
-             'repo_id': kws['repo_id']}
+             'repo_id': kws['repo_id'],
+             'task_id': kws['task_id']}
     queue_msg(address, props, kws)
 
 
@@ -293,6 +294,7 @@ def prep_repo_done(cbtype, *args, **kws):
     props = {'type': cbtype[4:],
              'tag': kws['repo']['tag_name'],
              'repo_id': kws['repo']['id'],
+             'task_id': kws['repo']['task_id'],
              'expire': kws['expire']}
     queue_msg(address, props, kws)
 
diff --git a/tests/test_hub/test_get_active_repos.py b/tests/test_hub/test_get_active_repos.py
index 6f473be..c8e162e 100644
--- a/tests/test_hub/test_get_active_repos.py
+++ b/tests/test_hub/test_get_active_repos.py
@@ -32,9 +32,10 @@ class TestGetActiveRepos(unittest.TestCase):
         # make sure the following does not error
         str(query)
         self.assertEqual(query.tables, ['repo'])
-        columns = ['repo.id', 'repo.state', 'repo.create_event',
-                   'EXTRACT(EPOCH FROM events.time)', 'repo.tag_id',
-                   'repo.dist','tag.name']
+        columns = ['repo.id', 'repo.state', 'repo.task_id', 'repo.create_event',
+                   'EXTRACT(EPOCH FROM events.time)', 'repo.tag_id', 'repo.dist', 'tag.name']
         self.assertEqual(set(query.columns), set(columns))
         self.assertEqual(query.clauses, ['repo.state != %(st_deleted)s'])
+        self.assertEqual(query.joins, ['tag ON repo.tag_id=tag.id',
+                                       'events ON repo.create_event = events.id'])
         self.assertEqual(query.values['st_deleted'], koji.REPO_DELETED)
diff --git a/tests/test_hub/test_repos.py b/tests/test_hub/test_repos.py
index 771f0a9..99c66e0 100644
--- a/tests/test_hub/test_repos.py
+++ b/tests/test_hub/test_repos.py
@@ -1,6 +1,8 @@
 import mock
 
 import unittest
+import datetime
+import psycopg2
 
 import koji
 import kojihub
@@ -15,15 +17,16 @@ class TestRepoFunctions(unittest.TestCase):
 
     def setUp(self):
         self.QueryProcessor = mock.patch('kojihub.QueryProcessor',
-                side_effect=self.getQuery).start()
+                                         side_effect=self.getQuery).start()
         self.queries = []
         self.InsertProcessor = mock.patch('kojihub.InsertProcessor',
-                side_effect=self.getInsert).start()
+                                          side_effect=self.getInsert).start()
         self.inserts = []
         self.UpdateProcessor = mock.patch('kojihub.UpdateProcessor',
-                side_effect=self.getUpdate).start()
+                                          side_effect=self.getUpdate).start()
         self.updates = []
         self._dml = mock.patch('kojihub._dml').start()
+        self.exports = kojihub.RootExports()
 
     def tearDown(self):
         mock.patch.stopall()
@@ -74,3 +77,32 @@ class TestRepoFunctions(unittest.TestCase):
             self.assertEqual(update.values['dist'], dist)
             if 'dist = %(dist)s' not in update.clauses:
                 raise Exception('Missing dist condition')
+
+    @mock.patch('kojihub._singleRow')
+    def test_repo_info(self, _singleRow):
+        repo_row = {'id': 10,
+                    'state': 0,
+                    'task_id': 15,
+                    'create_event': 32,
+                    'creation_time': datetime.datetime(2021, 3, 30, 12, 34, 5, 204023, tzinfo=psycopg2.tz.FixedOffsetTimezone(offset=0, name=None)),
+                    'create_ts': 1617107645.204023,
+                    'tag_id': 3,
+                    'tag_name': 'test-tag',
+                    'dist': False}
+        _singleRow.return_value = repo_row
+        rv = kojihub.repo_info(3)
+        self.assertEqual(rv, repo_row)
+
+    def test_get_repo(self):
+        rv = self.exports.getRepo(2)
+        self.assertEqual(len(self.queries), 1)
+        query = self.queries[0]
+        # make sure the following does not error
+        str(query)
+        self.assertEqual(query.tables, ['repo'])
+        columns = ['repo.id', 'repo.state', 'repo.task_id', 'repo.create_event',
+                   'EXTRACT(EPOCH FROM events.time)', 'repo.dist', 'events.time']
+        self.assertEqual(set(query.columns), set(columns))
+        self.assertEqual(query.joins, ['events ON repo.create_event = events.id'])
+        self.assertEqual(query.clauses, ['repo.dist is false', 'repo.state = %(state)s',
+                                         'repo.tag_id = %(id)i'])
diff --git a/tests/test_plugins/test_protonmsg.py b/tests/test_plugins/test_protonmsg.py
index 82642cd..25d59fe 100644
--- a/tests/test_plugins/test_protonmsg.py
+++ b/tests/test_plugins/test_protonmsg.py
@@ -186,13 +186,15 @@ extra_limit = 2048
 
     def test_prep_repo_init(self):
         protonmsg.prep_repo_init('postRepoInit', tag={'name': 'test-tag',
-            'arches': set(['x86_64', 'i386'])}, repo_id=1234)
-        self.assertMsg('repo.init', type='RepoInit', tag='test-tag', repo_id=1234)
+            'arches': set(['x86_64', 'i386'])}, repo_id=1234, task_id=25)
+        self.assertMsg('repo.init', type='RepoInit', tag='test-tag', repo_id=1234, task_id=25)
 
     def test_prep_repo_done(self):
-        protonmsg.prep_repo_done('postRepoDone', repo={'tag_name': 'test-tag', 'id': 1234},
+        protonmsg.prep_repo_done('postRepoDone',
+                                 repo={'tag_name': 'test-tag', 'id': 1234, 'task_id': 25},
                                  expire=False)
-        self.assertMsg('repo.done', type='RepoDone', tag='test-tag', repo_id=1234, expire=False)
+        self.assertMsg('repo.done', type='RepoDone', tag='test-tag', repo_id=1234,
+                       task_id=25, expire=False)
 
     @patch('protonmsg.Container')
     def test_send_queued_msgs_none(self, Container):
diff --git a/www/kojiweb/repoinfo.chtml b/www/kojiweb/repoinfo.chtml
index 9d8a4e0..3379559 100644
--- a/www/kojiweb/repoinfo.chtml
+++ b/www/kojiweb/repoinfo.chtml
@@ -9,6 +9,9 @@
 <table>
   <tr><th>ID</th><td>$repo.id</td><th></tr>
   <tr><th>Tag</th><td><a href="taginfo?tagID=$repo.tag_id">$repo.tag_name</a></td></tr>
+  #if $repo.task_id
+  <tr><th>Task ID</th><td><a href="taskinfo?taskID=$repo.task_id">$repo.task_id</a></td></tr>
+  #end if
   #set $state = $util.repoState($repo.state)
   <tr><th>State</th><td class="repo$state">$state</td></tr>
   <tr><th>Event</th><td>$repo.create_event ($util.formatTimeLong($repo.create_ts))</td></tr>

