PR#1030: Create symlinks for builds imported onto non-default volumes
Merges #1030 https://pagure.io/koji/pull-request/1030 Fixes: #1025 https://pagure.io/koji/issue/1025 missing default volume symlink for imported builds affected by volume policy
This commit is contained in:
commit
fd4eb9fadc
2 changed files with 177 additions and 0 deletions
|
|
@ -4935,6 +4935,43 @@ def _set_build_volume(binfo, volinfo, strict=True):
|
|||
koji.plugin.run_callbacks('postBuildStateChange', attribute='volume_id', old=old_binfo['volume_id'], new=volinfo['id'], info=binfo)
|
||||
|
||||
|
||||
def ensure_volume_symlink(binfo):
|
||||
"""Ensure that a build has a symlink on the default volume if needed"""
|
||||
|
||||
# basic checks
|
||||
volname = binfo.get('volume_name')
|
||||
if volname is None:
|
||||
logger.warn('buildinfo has no volume data, cannot create symlink')
|
||||
return
|
||||
if volname == 'DEFAULT':
|
||||
# nothing to do
|
||||
return
|
||||
|
||||
# get the actual build dir
|
||||
build_dir = koji.pathinfo.build(binfo)
|
||||
|
||||
# get the default volume location for the symlink
|
||||
base_vol = lookup_name('volume', 'DEFAULT', strict=True)
|
||||
base_binfo = binfo.copy()
|
||||
base_binfo['volume_id'] = base_vol['id']
|
||||
base_binfo['volume_name'] = base_vol['name']
|
||||
basedir = koji.pathinfo.build(base_binfo)
|
||||
|
||||
# check/make the symlink
|
||||
relpath = os.path.relpath(build_dir, os.path.dirname(basedir))
|
||||
if os.path.islink(basedir):
|
||||
if os.readlink(basedir) == relpath:
|
||||
# already correct
|
||||
return
|
||||
os.unlink(basedir)
|
||||
elif os.path.exists(basedir):
|
||||
raise koji.GenericError('Unexpected build content: %s', basedir)
|
||||
else:
|
||||
# parent dir might not exist
|
||||
koji.ensuredir(os.path.dirname(basedir))
|
||||
os.symlink(relpath, basedir)
|
||||
|
||||
|
||||
def check_volume_policy(data, strict=False, default=None):
|
||||
"""Check volume policy for the given data
|
||||
|
||||
|
|
@ -5245,6 +5282,9 @@ def import_build(srpm, rpms, brmap=None, task_id=None, build_id=None, logs=None)
|
|||
for relpath in files:
|
||||
fn = "%s/%s" % (uploadpath, relpath)
|
||||
import_build_log(fn, binfo, subdir=key)
|
||||
|
||||
ensure_volume_symlink(binfo)
|
||||
|
||||
koji.plugin.run_callbacks('postImport', type='build', srpm=srpm, rpms=rpms, brmap=brmap,
|
||||
task_id=task_id, build_id=build_id, build=binfo, logs=logs)
|
||||
return binfo
|
||||
|
|
@ -5778,6 +5818,7 @@ class CG_Importer(object):
|
|||
self.import_log(self.buildinfo, fileinfo)
|
||||
else:
|
||||
self.import_archive(self.buildinfo, brinfo, fileinfo)
|
||||
ensure_volume_symlink(self.buildinfo)
|
||||
|
||||
|
||||
def prep_archive(self, fileinfo):
|
||||
|
|
@ -12007,6 +12048,7 @@ class HostExports(object):
|
|||
|
||||
|
||||
self.importImage(task_id, build_id, results)
|
||||
ensure_volume_symlink(build_info)
|
||||
|
||||
st_complete = koji.BUILD_STATES['COMPLETE']
|
||||
koji.plugin.run_callbacks('preBuildStateChange', attribute='state', old=build_info['state'], new=st_complete, info=build_info)
|
||||
|
|
@ -12131,6 +12173,8 @@ class HostExports(object):
|
|||
if rpm_results:
|
||||
_import_wrapper(rpm_results['task_id'], build_info, rpm_results)
|
||||
|
||||
ensure_volume_symlink(build_info)
|
||||
|
||||
# update build state
|
||||
st_complete = koji.BUILD_STATES['COMPLETE']
|
||||
koji.plugin.run_callbacks('preBuildStateChange', attribute='state', old=build_info['state'], new=st_complete, info=build_info)
|
||||
|
|
@ -12272,6 +12316,8 @@ class HostExports(object):
|
|||
if rpm_results:
|
||||
_import_wrapper(rpm_results['task_id'], build_info, rpm_results)
|
||||
|
||||
ensure_volume_symlink(build_info)
|
||||
|
||||
# update build state
|
||||
st_complete = koji.BUILD_STATES['COMPLETE']
|
||||
koji.plugin.run_callbacks('preBuildStateChange', attribute='state', old=build_info['state'], new=st_complete, info=build_info)
|
||||
|
|
|
|||
131
tests/test_hub/test_ensure_volume_symlink.py
Normal file
131
tests/test_hub/test_ensure_volume_symlink.py
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
from __future__ import absolute_import
|
||||
import mock
|
||||
import os
|
||||
import os.path
|
||||
import shutil
|
||||
import tempfile
|
||||
try:
|
||||
import unittest2 as unittest
|
||||
except ImportError:
|
||||
import unittest
|
||||
import koji
|
||||
import kojihub
|
||||
|
||||
|
||||
class TestEnsureVolumeSymlink(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.tempdir = tempfile.mkdtemp()
|
||||
self.pathinfo = koji.PathInfo(self.tempdir)
|
||||
mock.patch('koji.pathinfo', new=self.pathinfo).start()
|
||||
mock.patch('kojihub.lookup_name', new=self.my_lookup_name).start()
|
||||
self.check_volume_policy = mock.patch('kojihub.check_volume_policy',
|
||||
return_value={'id':0, 'name': 'DEFAULT'}).start()
|
||||
self.buildinfo = {
|
||||
'id': 137,
|
||||
'task_id': 'TASK_ID',
|
||||
'name': 'some-image',
|
||||
'version': '1.2.3.4',
|
||||
'release': '3',
|
||||
'epoch': None,
|
||||
'source': None,
|
||||
'state': koji.BUILD_STATES['BUILDING'],
|
||||
'volume_id': 0,
|
||||
'volume_name': 'DEFAULT',
|
||||
}
|
||||
|
||||
def tearDown(self):
|
||||
mock.patch.stopall()
|
||||
shutil.rmtree(self.tempdir)
|
||||
|
||||
def my_lookup_name(self, table, info, **kw):
|
||||
if table != 'volume':
|
||||
raise Exception("Cannot fake call")
|
||||
return {
|
||||
'id': 'VOLUMEID:%s' % info,
|
||||
'name': '%s' % info,
|
||||
}
|
||||
|
||||
def test_volume_symlink_no_action(self):
|
||||
kojihub.ensure_volume_symlink(self.buildinfo)
|
||||
if os.listdir(self.tempdir):
|
||||
raise Exception('call created unexpected files')
|
||||
|
||||
del self.buildinfo['volume_name']
|
||||
with mock.patch('kojihub.logger') as logger:
|
||||
kojihub.ensure_volume_symlink(self.buildinfo)
|
||||
logger.warn.assert_called_once()
|
||||
|
||||
def test_volume_symlink_create(self):
|
||||
basedir = self.pathinfo.build(self.buildinfo) # default volume
|
||||
self.buildinfo['volume_name'] = 'test'
|
||||
self.buildinfo['volume_id'] = 1
|
||||
kojihub.ensure_volume_symlink(self.buildinfo)
|
||||
files = list(find_files(self.tempdir))
|
||||
expected = [
|
||||
'packages',
|
||||
'packages/some-image',
|
||||
'packages/some-image/1.2.3.4',
|
||||
'packages/some-image/1.2.3.4/3',
|
||||
]
|
||||
self.assertEqual(files, expected)
|
||||
relpath = ('../../../vol/test/packages/'
|
||||
'%(name)s/%(version)s/%(release)s' % self.buildinfo)
|
||||
self.assertEqual(os.readlink(basedir), relpath)
|
||||
|
||||
|
||||
def test_volume_symlink_exists(self):
|
||||
basedir = self.pathinfo.build(self.buildinfo) # default volume
|
||||
oldpath = 'some/other/link'
|
||||
os.makedirs(os.path.dirname(basedir))
|
||||
os.symlink(oldpath, basedir)
|
||||
self.buildinfo['volume_name'] = 'test'
|
||||
self.buildinfo['volume_id'] = 1
|
||||
kojihub.ensure_volume_symlink(self.buildinfo)
|
||||
relpath = ('../../../vol/test/packages/'
|
||||
'%(name)s/%(version)s/%(release)s' % self.buildinfo)
|
||||
self.assertEqual(os.readlink(basedir), relpath)
|
||||
|
||||
def test_volume_symlink_exists_same(self):
|
||||
basedir = self.pathinfo.build(self.buildinfo) # default volume
|
||||
relpath = ('../../../vol/test/packages/'
|
||||
'%(name)s/%(version)s/%(release)s' % self.buildinfo)
|
||||
os.makedirs(os.path.dirname(basedir))
|
||||
os.symlink(relpath, basedir)
|
||||
self.buildinfo['volume_name'] = 'test'
|
||||
self.buildinfo['volume_id'] = 1
|
||||
with mock.patch('os.unlink') as unlink:
|
||||
kojihub.ensure_volume_symlink(self.buildinfo)
|
||||
unlink.assert_not_called()
|
||||
files = list(find_files(self.tempdir))
|
||||
expected = [
|
||||
'packages',
|
||||
'packages/some-image',
|
||||
'packages/some-image/1.2.3.4',
|
||||
'packages/some-image/1.2.3.4/3',
|
||||
]
|
||||
self.assertEqual(files, expected)
|
||||
|
||||
def test_volume_symlink_exists_error(self):
|
||||
basedir = self.pathinfo.build(self.buildinfo) # default volume
|
||||
# create default volume dir, should trigger an error
|
||||
os.makedirs(basedir)
|
||||
self.buildinfo['volume_name'] = 'test'
|
||||
self.buildinfo['volume_id'] = 1
|
||||
with self.assertRaises(koji.GenericError):
|
||||
kojihub.ensure_volume_symlink(self.buildinfo)
|
||||
files = list(find_files(self.tempdir))
|
||||
expected = [
|
||||
'packages',
|
||||
'packages/some-image',
|
||||
'packages/some-image/1.2.3.4',
|
||||
'packages/some-image/1.2.3.4/3',
|
||||
]
|
||||
self.assertEqual(files, expected)
|
||||
|
||||
|
||||
def find_files(dirpath):
|
||||
'''Find all files under dir, report relative paths'''
|
||||
for path, dirs, files in os.walk(dirpath):
|
||||
for fn in sorted(dirs + files):
|
||||
yield os.path.relpath(os.path.join(path, fn), dirpath)
|
||||
Loading…
Add table
Add a link
Reference in a new issue