Stages/dracut: prepare environment for running dracut in chroot

dracut expects the environment, in which it is run, to have properly
mounted /proc, /dev and /sys. Otherwise, some of its modules don't work
properly. E.g. dracut fails to embed the CA cert bundle into the initram
disk, which means that HTTPS won't work in it. dracut also prints a lot
of errors and warnings about this, but we used to ignore them until now.

The buildroot environment in which the stage runs is OK, but we actually
run dracut using 'chroot', which is the core of the problem. The runtime
environment in such case lacks the necessary mounts.

Add a context manager for setting up and cleaning up all the necessary
mounts in the image FS tree when running dracut.

This change is related to:
https://bugzilla.redhat.com/show_bug.cgi?id=1962975

And the implementation has been inspired by the fix in lorax:
https://github.com/weldr/lorax/pull/1151

Signed-off-by: Tomáš Hozza <thozza@redhat.com>
This commit is contained in:
Tomáš Hozza 2024-08-14 16:49:13 +02:00 committed by Achilleas Koutsou
parent a0b44c5c72
commit e1df8cea8f
2 changed files with 38 additions and 6 deletions

View file

@ -1,4 +1,5 @@
#!/usr/bin/python3
import os
import subprocess
import sys
@ -10,6 +11,36 @@ def yesno(name: str, value: bool) -> str:
return f"--{prefix}{name}"
class DracutMounts:
"""
Setup the mounts inside a chroot root directory, which will be used
for running dracut inside it. Cleanup mounts when done.
This mounts /proc, /dev, and /sys.
"""
def __init__(self, root: str):
self.root = root
def __enter__(self):
for d in ["/proc", "/dev", "/sys"]:
if not os.path.exists(self.root + d):
print(f"Making missing dracut chroot directory: {d}")
os.makedirs(self.root + d)
subprocess.check_call(["/usr/bin/mount", "-t", "proc", "-o",
"nosuid,noexec,nodev", "proc", f"{self.root}/proc"])
subprocess.check_call(["/usr/bin/mount", "-t", "devtmpfs", "-o",
"mode=0755,noexec,nosuid,strictatime", "devtmpfs", f"{self.root}/dev"])
subprocess.check_call(["/usr/bin/mount", "-t", "sysfs", "-o",
"nosuid,noexec,nodev", "sysfs", f"{self.root}/sys"])
return self
def __exit__(self, exc_type, exc_value, tracebk):
for d in ["/proc", "/dev", "/sys"]:
subprocess.check_call(["/usr/bin/umount", self.root + d])
# pylint: disable=too-many-branches
def main(tree, options):
kernels = options["kernel"]
@ -82,11 +113,12 @@ def main(tree, options):
if initoverlayfs:
initfs_bin = "/usr/bin/initoverlayfs-install"
subprocess.run(["/usr/sbin/chroot", tree, initfs_bin,
"--no-hostonly",
"--kver", kver]
+ opts,
check=True)
with DracutMounts(tree):
subprocess.run(["/usr/sbin/chroot", tree, initfs_bin,
"--no-hostonly",
"--kver", kver]
+ opts,
check=True)
return 0

View file

@ -20,7 +20,7 @@ def test_dracut_with_initoverlayfs(mocked_run, tmp_path, stage_module, with_init
"initoverlayfs": with_initoverlayfs,
}
stage_module.main(tmp_path, options)
stage_module.main(str(tmp_path), options)
assert len(mocked_run.call_args_list) == 1
args, kwargs = mocked_run.call_args_list[0]