From 9544b865a42e50bd72606f073afcd9438d00c811 Mon Sep 17 00:00:00 2001 From: Tomas Kopecek Date: Jul 03 2024 11:20:14 +0000 Subject: PR#4092: handle volumes when clearing stray build dirs Merges #4092 https://pagure.io/koji/pull-request/4092 Fixes: #4091 https://pagure.io/koji/issue/4091 recycle_build doesn't clean up stray files on other volumes --- diff --git a/kojihub/kojihub.py b/kojihub/kojihub.py index 015cdde..3f17e5e 100644 --- a/kojihub/kojihub.py +++ b/kojihub/kojihub.py @@ -6481,9 +6481,20 @@ def recycle_build(old, data): update.set(cg_id=data['cg_id']) update.rawset(create_event='get_event()') update.execute() - builddir = koji.pathinfo.build(data) - if os.path.exists(builddir): - koji.util.rmtree(builddir) + + # delete stray files + for check_vol in list_volumes(): + check_binfo = data.copy() + check_binfo['volume_id'] = check_vol['id'] + check_binfo['volume_name'] = check_vol['name'] + checkdir = koji.pathinfo.build(check_binfo) + if os.path.islink(checkdir): + logger.warning(f'Removing stray build symlink: {checkdir}') + os.unlink(checkdir) + elif os.path.exists(checkdir): + logger.warning(f'Removing stray build directory: {checkdir}') + koji.util.rmtree(checkdir) + buildinfo = get_build(data['id'], strict=True) koji.plugin.run_callbacks('postBuildStateChange', attribute='state', old=old['state'], new=data['state'], info=buildinfo) diff --git a/tests/test_hub/test_recycle_build.py b/tests/test_hub/test_recycle_build.py index 24d3704..dcc5eb0 100644 --- a/tests/test_hub/test_recycle_build.py +++ b/tests/test_hub/test_recycle_build.py @@ -20,12 +20,16 @@ class TestRecycleBuild(unittest.TestCase): side_effect=self.getUpdate).start() self.run_callbacks = mock.patch('koji.plugin.run_callbacks').start() self.rmtree = mock.patch('koji.util.rmtree').start() - self.exists = mock.patch('os.path.exists').start() + self.unlink = mock.patch('os.unlink').start() + self.islink = mock.patch('os.path.islink', return_value=False).start() + self.exists = mock.patch('os.path.exists', return_value=False).start() self.updates = [] self.DeleteProcessor = mock.patch('kojihub.kojihub.DeleteProcessor', side_effect=self.getDelete).start() self.deletes = [] self.get_build = mock.patch('kojihub.kojihub.get_build').start() + self.list_volumes = mock.patch('kojihub.kojihub.list_volumes').start() + self.list_volumes.return_value = [{'id': 0, 'name': 'DEFAULT'}] def tearDown(self): mock.patch.stopall() @@ -276,3 +280,36 @@ class TestRecycleBuild(unittest.TestCase): self.get_build.assert_called_once_with(new['id'], strict=True) self.assertEqual(self.run_callbacks.call_count, 2) + + # our default data does not include stray files + self.rmtree.assert_not_called() + self.unlink.assert_not_called() + + def test_stray_link(self): + old = self.old.copy() + new = self.new.copy() + old['task_id'] = new['task_id'] = 137 + self.query_execute.side_effect = [[], [], []] + self.get_build.return_value = {'build_id': 2, 'name': 'GConf2', 'version': '3.2.6', + 'release': '15.fc23'} + + self.islink.return_value = True + kojihub.recycle_build(old, new) + self.rmtree.assert_not_called() + self.unlink.assert_called_once_with('/mnt/koji/packages/GConf2/3.2.6/15.fc23') + + def test_stray_dir(self): + old = self.old.copy() + new = self.new.copy() + old['task_id'] = new['task_id'] = 137 + self.query_execute.side_effect = [[], [], []] + self.get_build.return_value = {'build_id': 2, 'name': 'GConf2', 'version': '3.2.6', + 'release': '15.fc23'} + + self.exists.return_value = True + kojihub.recycle_build(old, new) + self.unlink.assert_not_called() + self.rmtree.assert_called_once_with('/mnt/koji/packages/GConf2/3.2.6/15.fc23') + + +# the end