From 6ca2245ca182d3afe10dbd64e0df699d5db99cfb Mon Sep 17 00:00:00 2001
From: Tomas Kopecek <tkopecek@redhat.com>
Date: Apr 19 2023 07:41:11 +0000
Subject: CG: allow reimports into failed/cancelled builds


Related: https://pagure.io/koji/issue/3776

---

diff --git a/kojihub/kojihub.py b/kojihub/kojihub.py
index 45d223d..fba0e9a 100644
--- a/kojihub/kojihub.py
+++ b/kojihub/kojihub.py
@@ -6708,10 +6708,12 @@ class CG_Importer(object):
             raise koji.GenericError('Reservation token given, but no build_id '
                                     'in metadata')
         else:
+            # no build reservation
             buildinfo = get_build(metadata['build'], strict=False)
-            if buildinfo and not metadata['build'].get('build_id'):
-                # TODO : allow in some cases
-                raise koji.GenericError("Build already exists: %r" % buildinfo)
+            if buildinfo:
+                if (koji.BUILD_STATES[buildinfo['state']] not in ('CANCELED', 'FAILED')):
+                    raise koji.GenericError("Build already exists: %r" % buildinfo)
+                # note: the checks in recycle_build will also apply when we call new_build later
         # gather needed data
         buildinfo = dslice(metadata['build'], ['name', 'version', 'release', 'extra', 'source'])
         if 'build_id' in metadata['build']:
diff --git a/tests/test_hub/test_cg_importer.py b/tests/test_hub/test_cg_importer.py
index e13cee3..e449000 100644
--- a/tests/test_hub/test_cg_importer.py
+++ b/tests/test_hub/test_cg_importer.py
@@ -27,6 +27,32 @@ class TestCGImporter(unittest.TestCase):
         self.lexists = mock.patch('os.path.lexists').start()
         self.path_build = mock.patch('koji.pathinfo.build').start()
         self.new_build = mock.patch('kojihub.kojihub.new_build').start()
+        self.buildinfo = {
+            'id': 43,
+            'package_id': 1,
+            'package_name': 'testpkg',
+            'name': 'testpkg',
+            'version': '1.0.1e',
+            'release': '42.el7',
+            'epoch': None,
+            'nvr': 'testpkg-1.0.1-1.fc24',
+            'state': koji.BUILD_STATES['COMPLETE'],
+            'task_id': 1,
+            'owner_id': 1,
+            'owner_name': 'jvasallo',
+            'volume_id': 'id-1212',
+            'volume_name':'testvolume',
+            'creation_event_id': '',
+            'creation_time': '',
+            'creation_ts': 424242424242,
+            'start_time': None,
+            'start_ts': None,
+            'completion_time': None,
+            'completion_ts': None,
+            'source': 'https://example.com',
+            'extra': {},
+        }
+
 
     def tearDown(self):
         if os.path.exists(self.TMP_PATH):
@@ -121,7 +147,7 @@ class TestCGImporter(unittest.TestCase):
 
     def test_prep_build_exists(self):
         self.path_work.return_value = os.path.dirname(__file__)
-        self.get_build.return_value = True
+        self.get_build.return_value = self.buildinfo
         x = kojihub.CG_Importer()
         x.get_metadata('default.json', 'cg_importer_json')
         with self.assertRaises(GenericError):
@@ -140,20 +166,7 @@ class TestCGImporter(unittest.TestCase):
         x.assert_cg_access()
         x.prep_build()
         x.prepped_outputs = []
-        self.get_build.return_value = {'id': 43, 'package_id': 1,
-                                       'package_name': 'testpkg',
-                                       'name': 'testpkg', 'version': '1.0.1e',
-                                       'release': '42.el7', 'epoch': None,
-                                       'nvr': 'testpkg-1.0.1-1.fc24',
-                                       'state': 'complete', 'task_id': 1,
-                                       'owner_id': 1, 'owner_name': 'jvasallo',
-                                       'volume_id': 'id-1212', 'volume_name': 'testvolume',
-                                       'creation_event_id': '', 'creation_time': '',
-                                       'creation_ts': 424242424242,
-                                       'start_time': None, 'start_ts': None,
-                                       'completion_time': None, 'completion_ts': None,
-                                       'source': 'https://example.com', 'extra': {}
-                                       }
+        self.get_build.return_value = self.buildinfo
         self.new_build.return_value = 43
         x.get_build()
         assert x.buildinfo

