buildinstall: Allow using external dire for runroot task
A new `buildinstall_topdir` option allows using buildinstall even when the compose is created on a different volume that Koji is using. The files are created in this external directory and then copies into the usual location. Merges: https://pagure.io/pungi/pull-request/807 Signed-off-by: Jan Kaluza <jkaluza@redhat.com>
This commit is contained in:
parent
cf114a7fab
commit
953fb4c54c
@ -496,6 +496,14 @@ Options
|
|||||||
**buildinstall_kickstart**
|
**buildinstall_kickstart**
|
||||||
(:ref:`scm_dict <scm_support>`) -- If specified, this kickstart file will
|
(:ref:`scm_dict <scm_support>`) -- If specified, this kickstart file will
|
||||||
be copied into each file and pointed to in boot configuration.
|
be copied into each file and pointed to in boot configuration.
|
||||||
|
**buildinstall_topdir**
|
||||||
|
(*str*) -- Full path to top directory where the runroot buildinstall
|
||||||
|
Koji tasks output should be stored. This is useful in situation when
|
||||||
|
the Pungi compose is not generated on the same storage as the Koji task
|
||||||
|
is running on. In this case, Pungi can provide input repository for runroot
|
||||||
|
task using HTTP and set the output directory for this task to
|
||||||
|
``buildinstall_topdir``. Once the runroot task finishes, Pungi will copy
|
||||||
|
the results of runroot tasks to the compose working directory.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
|
@ -709,6 +709,7 @@ def make_schema():
|
|||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["lorax", "buildinstall"],
|
"enum": ["lorax", "buildinstall"],
|
||||||
},
|
},
|
||||||
|
"buildinstall_topdir": {"type": "string"},
|
||||||
"buildinstall_kickstart": {"$ref": "#/definitions/str_or_scm_dict"},
|
"buildinstall_kickstart": {"$ref": "#/definitions/str_or_scm_dict"},
|
||||||
|
|
||||||
"global_ksurl": {"type": "string"},
|
"global_ksurl": {"type": "string"},
|
||||||
|
@ -203,14 +203,28 @@ class WorkPaths(object):
|
|||||||
makedirs(path)
|
makedirs(path)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
def buildinstall_dir(self, arch, create_dir=True):
|
def buildinstall_dir(self, arch, create_dir=True,
|
||||||
|
allow_topdir_override=False, variant=None):
|
||||||
"""
|
"""
|
||||||
|
:param bool allow_topdir_override: When True, the
|
||||||
|
"buildinstall_topdir" will be used (if set) instead of real
|
||||||
|
"topdir".
|
||||||
Examples:
|
Examples:
|
||||||
work/x86_64/buildinstall
|
work/x86_64/buildinstall
|
||||||
"""
|
"""
|
||||||
if arch == "global":
|
if arch == "global":
|
||||||
raise RuntimeError("Global buildinstall dir makes no sense.")
|
raise RuntimeError("Global buildinstall dir makes no sense.")
|
||||||
path = os.path.join(self.topdir(arch, create_dir=create_dir), "buildinstall")
|
|
||||||
|
buildinstall_topdir = self.compose.conf.get("buildinstall_topdir", "")
|
||||||
|
if allow_topdir_override and buildinstall_topdir:
|
||||||
|
topdir_basename = os.path.basename(self.compose.topdir)
|
||||||
|
path = os.path.join(
|
||||||
|
buildinstall_topdir, "buildinstall-%s" % topdir_basename, arch)
|
||||||
|
else:
|
||||||
|
path = os.path.join(self.topdir(arch, create_dir=create_dir), "buildinstall")
|
||||||
|
|
||||||
|
if variant:
|
||||||
|
path = os.path.join(path, variant.uid)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
def extra_files_dir(self, arch, variant, create_dir=True):
|
def extra_files_dir(self, arch, variant, create_dir=True):
|
||||||
|
@ -28,6 +28,7 @@ from six.moves import shlex_quote
|
|||||||
from pungi.arch import get_valid_arches
|
from pungi.arch import get_valid_arches
|
||||||
from pungi.util import get_volid, get_arch_variant_data
|
from pungi.util import get_volid, get_arch_variant_data
|
||||||
from pungi.util import get_file_size, get_mtime, failable, makedirs
|
from pungi.util import get_file_size, get_mtime, failable, makedirs
|
||||||
|
from pungi.util import copy_all, translate_path
|
||||||
from pungi.wrappers.lorax import LoraxWrapper
|
from pungi.wrappers.lorax import LoraxWrapper
|
||||||
from pungi.wrappers.kojiwrapper import get_buildroot_rpms, KojiWrapper
|
from pungi.wrappers.kojiwrapper import get_buildroot_rpms, KojiWrapper
|
||||||
from pungi.wrappers import iso
|
from pungi.wrappers import iso
|
||||||
@ -74,13 +75,24 @@ class BuildinstallPhase(PhaseBase):
|
|||||||
add_template_var.extend(data.get('add_template_var', []))
|
add_template_var.extend(data.get('add_template_var', []))
|
||||||
add_arch_template_var.extend(data.get('add_arch_template_var', []))
|
add_arch_template_var.extend(data.get('add_arch_template_var', []))
|
||||||
output_dir = os.path.join(output_dir, variant.uid)
|
output_dir = os.path.join(output_dir, variant.uid)
|
||||||
|
output_topdir = output_dir
|
||||||
|
|
||||||
# The paths module will modify the filename (by inserting arch). But we
|
# The paths module will modify the filename (by inserting arch). But we
|
||||||
# only care about the directory anyway.
|
# only care about the directory anyway.
|
||||||
log_filename = 'buildinstall-%s-logs/dumym' % variant.uid
|
log_filename = 'buildinstall-%s-logs/dummy' % variant.uid
|
||||||
log_dir = os.path.dirname(self.compose.paths.log.log_file(arch, log_filename))
|
log_dir = os.path.dirname(self.compose.paths.log.log_file(arch, log_filename))
|
||||||
makedirs(log_dir)
|
makedirs(log_dir)
|
||||||
|
|
||||||
|
# If the buildinstall_topdir is set, it means Koji is used for
|
||||||
|
# buildinstall phase and the filesystem with Koji is read-only.
|
||||||
|
# In that case, we have to write logs to buildinstall_topdir and
|
||||||
|
# later copy them back to our local log directory.
|
||||||
|
if self.compose.conf.get("buildinstall_topdir", None):
|
||||||
|
log_dir = self.compose.paths.work.buildinstall_dir(
|
||||||
|
arch, allow_topdir_override=True, variant=variant)
|
||||||
|
log_dir = os.path.join(log_dir, "logs")
|
||||||
|
output_dir = os.path.join(output_dir, "results")
|
||||||
|
|
||||||
lorax = LoraxWrapper()
|
lorax = LoraxWrapper()
|
||||||
lorax_cmd = lorax.get_lorax_cmd(self.compose.conf["release_name"],
|
lorax_cmd = lorax.get_lorax_cmd(self.compose.conf["release_name"],
|
||||||
self.compose.conf["release_version"],
|
self.compose.conf["release_version"],
|
||||||
@ -100,7 +112,7 @@ class BuildinstallPhase(PhaseBase):
|
|||||||
add_arch_template_var=add_arch_template_var,
|
add_arch_template_var=add_arch_template_var,
|
||||||
noupgrade=noupgrade,
|
noupgrade=noupgrade,
|
||||||
log_dir=log_dir)
|
log_dir=log_dir)
|
||||||
return 'rm -rf %s && %s' % (shlex_quote(output_dir),
|
return 'rm -rf %s && %s' % (shlex_quote(output_topdir),
|
||||||
' '.join([shlex_quote(x) for x in lorax_cmd]))
|
' '.join([shlex_quote(x) for x in lorax_cmd]))
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
@ -114,8 +126,11 @@ class BuildinstallPhase(PhaseBase):
|
|||||||
for arch in self.compose.get_arches():
|
for arch in self.compose.get_arches():
|
||||||
commands = []
|
commands = []
|
||||||
|
|
||||||
|
output_dir = self.compose.paths.work.buildinstall_dir(arch, allow_topdir_override=True)
|
||||||
|
final_output_dir = self.compose.paths.work.buildinstall_dir(arch, allow_topdir_override=False)
|
||||||
repo_baseurl = self.compose.paths.work.arch_repo(arch)
|
repo_baseurl = self.compose.paths.work.arch_repo(arch)
|
||||||
output_dir = self.compose.paths.work.buildinstall_dir(arch)
|
if final_output_dir != output_dir:
|
||||||
|
repo_baseurl = translate_path(self.compose, repo_baseurl)
|
||||||
|
|
||||||
if buildinstall_method == "lorax":
|
if buildinstall_method == "lorax":
|
||||||
buildarch = get_valid_arches(arch)[0]
|
buildarch = get_valid_arches(arch)[0]
|
||||||
@ -388,11 +403,13 @@ class BuildinstallThread(WorkerThread):
|
|||||||
|
|
||||||
msg = "Running buildinstall for arch %s, variant %s" % (arch, variant)
|
msg = "Running buildinstall for arch %s, variant %s" % (arch, variant)
|
||||||
|
|
||||||
output_dir = compose.paths.work.buildinstall_dir(arch)
|
output_dir = compose.paths.work.buildinstall_dir(
|
||||||
if variant:
|
arch, allow_topdir_override=True, variant=variant)
|
||||||
output_dir = os.path.join(output_dir, variant.uid)
|
final_output_dir = compose.paths.work.buildinstall_dir(
|
||||||
|
arch, variant=variant)
|
||||||
|
|
||||||
if os.path.isdir(output_dir) and os.listdir(output_dir):
|
if (os.path.isdir(output_dir) and os.listdir(output_dir) or
|
||||||
|
os.path.isdir(final_output_dir) and os.listdir(final_output_dir)):
|
||||||
# output dir is *not* empty -> SKIP
|
# output dir is *not* empty -> SKIP
|
||||||
self.pool.log_warning(
|
self.pool.log_warning(
|
||||||
'[SKIP ] Buildinstall for arch %s, variant %s' % (arch, variant))
|
'[SKIP ] Buildinstall for arch %s, variant %s' % (arch, variant))
|
||||||
@ -433,6 +450,20 @@ class BuildinstallThread(WorkerThread):
|
|||||||
# run locally
|
# run locally
|
||||||
run(cmd, show_cmd=True, logfile=log_file)
|
run(cmd, show_cmd=True, logfile=log_file)
|
||||||
|
|
||||||
|
if final_output_dir != output_dir:
|
||||||
|
if not os.path.exists(final_output_dir):
|
||||||
|
makedirs(final_output_dir)
|
||||||
|
results_dir = os.path.join(output_dir, "results")
|
||||||
|
copy_all(results_dir, final_output_dir)
|
||||||
|
|
||||||
|
# Get the log_dir into which we should copy the resulting log files.
|
||||||
|
log_fname = 'buildinstall-%s-logs/dummy' % variant.uid
|
||||||
|
final_log_dir = os.path.dirname(compose.paths.log.log_file(arch, log_fname))
|
||||||
|
if not os.path.exists(final_log_dir):
|
||||||
|
makedirs(final_log_dir)
|
||||||
|
log_dir = os.path.join(output_dir, "logs")
|
||||||
|
copy_all(log_dir, final_log_dir)
|
||||||
|
|
||||||
log_file = compose.paths.log.log_file(arch, log_filename + '-RPMs')
|
log_file = compose.paths.log.log_file(arch, log_filename + '-RPMs')
|
||||||
rpms = get_buildroot_rpms(compose, task_id)
|
rpms = get_buildroot_rpms(compose, task_id)
|
||||||
with open(log_file, "w") as f:
|
with open(log_file, "w") as f:
|
||||||
|
@ -345,6 +345,76 @@ class TestBuildinstallPhase(PungiTestCase):
|
|||||||
mock.call(compose, 'amd64', variant=compose.variants['Server'], disc_type='dvd')])
|
mock.call(compose, 'amd64', variant=compose.variants['Server'], disc_type='dvd')])
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('pungi.phases.buildinstall.ThreadPool')
|
||||||
|
@mock.patch('pungi.phases.buildinstall.LoraxWrapper')
|
||||||
|
@mock.patch('pungi.phases.buildinstall.get_volid')
|
||||||
|
def test_uses_lorax_options_buildinstall_topdir(self, get_volid, loraxCls, poolCls):
|
||||||
|
compose = BuildInstallCompose(self.topdir, {
|
||||||
|
'bootable': True,
|
||||||
|
'release_name': 'Test',
|
||||||
|
'release_short': 't',
|
||||||
|
'release_version': '1',
|
||||||
|
'release_is_layered': False,
|
||||||
|
'buildinstall_method': 'lorax',
|
||||||
|
'buildinstall_topdir': '/buildinstall_topdir',
|
||||||
|
'translate_paths': [(self.topdir, "http://localhost/")],
|
||||||
|
})
|
||||||
|
|
||||||
|
buildinstall_topdir = os.path.join(
|
||||||
|
"/buildinstall_topdir", "buildinstall-" + os.path.basename(self.topdir))
|
||||||
|
self.maxDiff = None
|
||||||
|
|
||||||
|
get_volid.return_value = 'vol_id'
|
||||||
|
loraxCls.return_value.get_lorax_cmd.return_value = ['lorax', '...']
|
||||||
|
|
||||||
|
phase = BuildinstallPhase(compose)
|
||||||
|
|
||||||
|
phase.run()
|
||||||
|
|
||||||
|
# Three items added for processing in total.
|
||||||
|
# Server.x86_64, Client.amd64, Server.x86_64
|
||||||
|
pool = poolCls.return_value
|
||||||
|
self.assertEqual(3, len(pool.queue_put.mock_calls))
|
||||||
|
self.assertItemsEqual(
|
||||||
|
[call[0][0][3] for call in pool.queue_put.call_args_list],
|
||||||
|
['rm -rf %s/amd64/Client && lorax ...' % buildinstall_topdir,
|
||||||
|
'rm -rf %s/amd64/Server && lorax ...' % buildinstall_topdir,
|
||||||
|
'rm -rf %s/x86_64/Server && lorax ...' % buildinstall_topdir])
|
||||||
|
|
||||||
|
# Obtained correct lorax commands.
|
||||||
|
self.assertItemsEqual(
|
||||||
|
loraxCls.return_value.get_lorax_cmd.mock_calls,
|
||||||
|
[mock.call('Test', '1', '1', 'http://localhost/work/x86_64/repo',
|
||||||
|
buildinstall_topdir + '/x86_64/Server/results',
|
||||||
|
buildarch='x86_64', is_final=True, nomacboot=True, noupgrade=True,
|
||||||
|
volid='vol_id', variant='Server', buildinstallpackages=['bash', 'vim'],
|
||||||
|
add_template=[], add_arch_template=[],
|
||||||
|
add_template_var=[], add_arch_template_var=[],
|
||||||
|
bugurl=None,
|
||||||
|
log_dir=buildinstall_topdir + '/x86_64/Server/logs'),
|
||||||
|
mock.call('Test', '1', '1', 'http://localhost/work/amd64/repo',
|
||||||
|
buildinstall_topdir + '/amd64/Server/results',
|
||||||
|
buildarch='amd64', is_final=True, nomacboot=True, noupgrade=True,
|
||||||
|
volid='vol_id', variant='Server', buildinstallpackages=['bash', 'vim'],
|
||||||
|
bugurl=None,
|
||||||
|
add_template=[], add_arch_template=[],
|
||||||
|
add_template_var=[], add_arch_template_var=[],
|
||||||
|
log_dir=buildinstall_topdir + '/amd64/Server/logs'),
|
||||||
|
mock.call('Test', '1', '1', 'http://localhost/work/amd64/repo',
|
||||||
|
buildinstall_topdir + '/amd64/Client/results',
|
||||||
|
buildarch='amd64', is_final=True, nomacboot=True, noupgrade=True,
|
||||||
|
volid='vol_id', variant='Client', buildinstallpackages=[],
|
||||||
|
bugurl=None,
|
||||||
|
add_template=[], add_arch_template=[],
|
||||||
|
add_template_var=[], add_arch_template_var=[],
|
||||||
|
log_dir=buildinstall_topdir + '/amd64/Client/logs')])
|
||||||
|
self.assertItemsEqual(
|
||||||
|
get_volid.mock_calls,
|
||||||
|
[mock.call(compose, 'x86_64', variant=compose.variants['Server'], disc_type='dvd'),
|
||||||
|
mock.call(compose, 'amd64', variant=compose.variants['Client'], disc_type='dvd'),
|
||||||
|
mock.call(compose, 'amd64', variant=compose.variants['Server'], disc_type='dvd')])
|
||||||
|
|
||||||
|
|
||||||
class TestCopyFiles(PungiTestCase):
|
class TestCopyFiles(PungiTestCase):
|
||||||
|
|
||||||
@mock.patch('pungi.phases.buildinstall.link_boot_iso')
|
@mock.patch('pungi.phases.buildinstall.link_boot_iso')
|
||||||
@ -706,6 +776,62 @@ class BuildinstallThreadTestCase(PungiTestCase):
|
|||||||
self.assertTrue(os.path.exists(dummy_file))
|
self.assertTrue(os.path.exists(dummy_file))
|
||||||
self.assertItemsEqual(self.pool.finished_tasks, [])
|
self.assertItemsEqual(self.pool.finished_tasks, [])
|
||||||
|
|
||||||
|
@mock.patch('pungi.phases.buildinstall.KojiWrapper')
|
||||||
|
@mock.patch('pungi.phases.buildinstall.get_buildroot_rpms')
|
||||||
|
@mock.patch('pungi.phases.buildinstall.run')
|
||||||
|
@mock.patch('pungi.phases.buildinstall.copy_all')
|
||||||
|
def test_buildinstall_thread_with_lorax_custom_buildinstall_topdir(
|
||||||
|
self, copy_all, run, get_buildroot_rpms, KojiWrapperMock):
|
||||||
|
compose = BuildInstallCompose(self.topdir, {
|
||||||
|
'buildinstall_method': 'lorax',
|
||||||
|
'runroot': True,
|
||||||
|
'runroot_tag': 'rrt',
|
||||||
|
'koji_profile': 'koji',
|
||||||
|
'runroot_weights': {'buildinstall': 123},
|
||||||
|
'buildinstall_topdir': '/buildinstall_topdir',
|
||||||
|
})
|
||||||
|
|
||||||
|
get_buildroot_rpms.return_value = ['bash', 'zsh']
|
||||||
|
|
||||||
|
get_runroot_cmd = KojiWrapperMock.return_value.get_runroot_cmd
|
||||||
|
|
||||||
|
run_runroot_cmd = KojiWrapperMock.return_value.run_runroot_cmd
|
||||||
|
run_runroot_cmd.return_value = {
|
||||||
|
'output': 'Foo bar baz',
|
||||||
|
'retcode': 0,
|
||||||
|
'task_id': 1234,
|
||||||
|
}
|
||||||
|
|
||||||
|
t = BuildinstallThread(self.pool)
|
||||||
|
|
||||||
|
with mock.patch('time.sleep'):
|
||||||
|
t.process((compose, 'x86_64', compose.variants['Server'], self.cmd), 0)
|
||||||
|
|
||||||
|
self.assertItemsEqual(
|
||||||
|
get_runroot_cmd.mock_calls,
|
||||||
|
[mock.call('rrt', 'x86_64', self.cmd, channel=None,
|
||||||
|
use_shell=True, task_id=True,
|
||||||
|
packages=['strace', 'lorax'], mounts=[self.topdir], weight=123)])
|
||||||
|
self.assertItemsEqual(
|
||||||
|
run_runroot_cmd.mock_calls,
|
||||||
|
[mock.call(get_runroot_cmd.return_value,
|
||||||
|
log_file=self.topdir + '/logs/x86_64/buildinstall-Server.x86_64.log')])
|
||||||
|
with open(self.topdir + '/logs/x86_64/buildinstall-Server-RPMs.x86_64.log') as f:
|
||||||
|
rpms = f.read().strip().split('\n')
|
||||||
|
self.assertItemsEqual(rpms, ['bash', 'zsh'])
|
||||||
|
self.assertItemsEqual(self.pool.finished_tasks, [('Server', 'x86_64')])
|
||||||
|
|
||||||
|
buildinstall_topdir = os.path.join(
|
||||||
|
"/buildinstall_topdir", "buildinstall-" + os.path.basename(self.topdir))
|
||||||
|
self.assertItemsEqual(
|
||||||
|
copy_all.mock_calls,
|
||||||
|
[mock.call(os.path.join(buildinstall_topdir, 'x86_64/Server/results'),
|
||||||
|
os.path.join(self.topdir, 'work/x86_64/buildinstall/Server')),
|
||||||
|
mock.call(os.path.join(buildinstall_topdir, 'x86_64/Server/logs'),
|
||||||
|
os.path.join(self.topdir, 'logs/x86_64/buildinstall-Server-logs'))
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class TestSymlinkIso(PungiTestCase):
|
class TestSymlinkIso(PungiTestCase):
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user