kernel-install does a lot more then making the kernel available to the ESP. It calls dracut, it creates loader entries and all that is unexpected and also breaks the boot because the way dracut is called in the image build case where host != target leads to broken results. This commit refactors the systemd-boot support in kiwi to prevent the use of kernel-install
188 lines
7.6 KiB
Python
188 lines
7.6 KiB
Python
import io
|
|
from pytest import raises
|
|
from mock import (
|
|
Mock, patch, call, MagicMock
|
|
)
|
|
from kiwi.bootloader.config.systemd_boot import BootLoaderSystemdBoot
|
|
|
|
from kiwi.exceptions import (
|
|
KiwiTemplateError,
|
|
KiwiBootLoaderTargetError
|
|
)
|
|
|
|
|
|
class TestBootLoaderSystemdBoot:
|
|
def setup(self):
|
|
self.state = Mock()
|
|
self.state.xml_data.get_name.return_value = 'image-name'
|
|
self.state.get_image_version.return_value = 'image-version'
|
|
self.state.build_type.get_efifatimagesize.return_value = None
|
|
self.bootloader = BootLoaderSystemdBoot(self.state, 'root_dir')
|
|
self.bootloader.custom_args['kernel'] = None
|
|
self.bootloader.custom_args['initrd'] = None
|
|
self.bootloader.custom_args['boot_options'] = {}
|
|
self.bootloader.root_mount = Mock(
|
|
mountpoint='system_root_mount'
|
|
)
|
|
self.bootloader._mount_system = Mock()
|
|
self.bootloader.create_efi_path = Mock()
|
|
self.bootloader.get_boot_path = Mock(
|
|
return_value='bootpath'
|
|
)
|
|
self.bootloader.get_menu_entry_title = Mock(
|
|
return_value='title'
|
|
)
|
|
|
|
def setup_method(self, cls):
|
|
self.setup()
|
|
|
|
def test_setup_loader_raises_invalid_target(self):
|
|
with raises(KiwiBootLoaderTargetError):
|
|
self.bootloader.setup_loader('iso')
|
|
|
|
@patch('os.path.exists')
|
|
@patch('kiwi.bootloader.config.systemd_boot.BootImageBase.get_boot_names')
|
|
@patch('kiwi.bootloader.config.systemd_boot.Path.wipe')
|
|
@patch('kiwi.bootloader.config.systemd_boot.Path.create')
|
|
@patch('kiwi.bootloader.config.systemd_boot.Command.run')
|
|
@patch('kiwi.bootloader.config.systemd_boot.BootLoaderTemplateSystemdBoot')
|
|
@patch.object(BootLoaderSystemdBoot, '_write_config_file')
|
|
@patch.object(BootLoaderSystemdBoot, '_get_template_parameters')
|
|
@patch.object(BootLoaderSystemdBoot, '_write_kernel_cmdline_file')
|
|
def test_setup_loader(
|
|
self, mock_write_kernel_cmdline_file,
|
|
mock_get_template_parameters, mock_write_config_file,
|
|
mock_BootLoaderTemplateSystemdBoot, mock_Command_run,
|
|
mock_Path_create, mock_Path_wipe, mock_BootImageBase_get_boot_names,
|
|
mock_os_path_exists
|
|
):
|
|
kernel_info = Mock()
|
|
kernel_info.kernel_version = 'kernel-version'
|
|
kernel_info.kernel_filename = 'kernel-filename'
|
|
kernel_info.initrd_name = 'initrd-name'
|
|
mock_os_path_exists.return_value = True
|
|
mock_BootImageBase_get_boot_names.return_value = kernel_info
|
|
self.bootloader.setup_loader('disk')
|
|
assert mock_write_config_file.call_args_list == [
|
|
call(
|
|
mock_BootLoaderTemplateSystemdBoot.return_value.get_loader_template.return_value,
|
|
'system_root_mount/boot/efi/loader/loader.conf',
|
|
mock_get_template_parameters.return_value
|
|
),
|
|
call(
|
|
mock_BootLoaderTemplateSystemdBoot.return_value.get_entry_template.return_value,
|
|
'system_root_mount/boot/efi/loader/entries/main.conf',
|
|
mock_get_template_parameters.return_value
|
|
)
|
|
]
|
|
|
|
assert self.bootloader._mount_system.called
|
|
mock_write_kernel_cmdline_file.assert_called_once_with(
|
|
'system_root_mount'
|
|
)
|
|
assert mock_Command_run.call_args_list == [
|
|
call(
|
|
[
|
|
'chroot', 'system_root_mount',
|
|
'bootctl', 'install',
|
|
'--esp-path=/boot/efi',
|
|
'--no-variables',
|
|
'--entry-token', 'os-id'
|
|
]
|
|
),
|
|
call(
|
|
[
|
|
'cp', 'kernel-filename',
|
|
'system_root_mount/boot/efi/os'
|
|
]
|
|
),
|
|
call(
|
|
[
|
|
'cp', 'system_root_mount/boot/initrd-name',
|
|
'system_root_mount/boot/efi/os'
|
|
]
|
|
)
|
|
]
|
|
|
|
@patch.object(BootLoaderSystemdBoot, '_write_config_file')
|
|
def test_set_loader_entry(self, mock_write_config_file):
|
|
self.bootloader.set_loader_entry('root_dir', 'disk')
|
|
|
|
def test_create_loader_image(self):
|
|
self.bootloader.create_loader_image('disk')
|
|
self.bootloader.create_efi_path.assert_called_once_with()
|
|
|
|
def test_get_template_parameters(self):
|
|
self.bootloader.timeout = 0
|
|
assert self.bootloader._get_template_parameters() == {
|
|
'kernel_file': 'vmlinuz',
|
|
'initrd_file': 'initrd',
|
|
'boot_options': '',
|
|
'boot_timeout': 0,
|
|
'bootpath': 'bootpath',
|
|
'title': 'title'
|
|
}
|
|
|
|
@patch('kiwi.bootloader.config.systemd_boot.Path.create')
|
|
def test_write_config_file(self, mock_Path_create):
|
|
template = Mock()
|
|
template.substitute.return_value = 'data'
|
|
with patch('builtins.open', create=True) as mock_open:
|
|
mock_open.return_value = MagicMock(spec=io.IOBase)
|
|
file_handle = mock_open.return_value.__enter__.return_value
|
|
self.bootloader._write_config_file(template, 'path/some-file', {})
|
|
mock_Path_create.assert_called_once_with('path')
|
|
mock_open.assert_called_once_with('path/some-file', 'w')
|
|
file_handle.write.assert_called_once_with('data')
|
|
|
|
def test_write_config_file_raises(self):
|
|
template = Mock()
|
|
template.substitute.side_effect = Exception
|
|
with raises(KiwiTemplateError):
|
|
self.bootloader._write_config_file(template, 'some-file', {})
|
|
|
|
def test_write_kernel_cmdline_file(self):
|
|
with patch('builtins.open', create=True) as mock_open:
|
|
mock_open.return_value = MagicMock(spec=io.IOBase)
|
|
file_handle = mock_open.return_value.__enter__.return_value
|
|
self.bootloader._write_kernel_cmdline_file('system_root_mount')
|
|
mock_open.assert_called_once_with(
|
|
'system_root_mount/etc/kernel/cmdline', 'w'
|
|
)
|
|
file_handle.write.assert_called_once_with(self.bootloader.cmdline)
|
|
|
|
@patch('kiwi.bootloader.config.systemd_boot.Path.create')
|
|
@patch('kiwi.bootloader.config.systemd_boot.Command.run')
|
|
@patch('kiwi.bootloader.config.systemd_boot.LoopDevice')
|
|
@patch('kiwi.bootloader.config.systemd_boot.Disk')
|
|
@patch('kiwi.bootloader.config.systemd_boot.MountManager')
|
|
@patch.object(BootLoaderSystemdBoot, '_run_bootctl')
|
|
@patch.object(BootLoaderSystemdBoot, 'set_loader_entry')
|
|
def test_create_embedded_fat_efi_image(
|
|
self, mock_set_loader_entry, mock_run_bootctl, mock_MountManager,
|
|
mock_Disk, mock_LoopDevice, mock_Command_run, mock_Path_create
|
|
):
|
|
target = Mock()
|
|
self.bootloader.target = target
|
|
mock_Disk.return_value.partition_map = {
|
|
'efi': 'efi_device'
|
|
}
|
|
self.bootloader._create_embedded_fat_efi_image('ESP')
|
|
assert mock_MountManager.call_args_list == [
|
|
call(device='efi_device', mountpoint='root_dir/boot/efi'),
|
|
call(device='/dev', mountpoint='root_dir/dev'),
|
|
call(device='/proc', mountpoint='root_dir/proc'),
|
|
call(device='/sys', mountpoint='root_dir/sys')
|
|
]
|
|
assert mock_Command_run.call_args_list == [
|
|
call(['qemu-img', 'create', 'ESP', '20M']),
|
|
call(['sgdisk', '-n', ':1.0', '-t', '1:EF00', 'ESP']),
|
|
call(
|
|
['mkdosfs', '-n', 'BOOT', 'efi_device']
|
|
),
|
|
call(['dd', 'if=efi_device', 'of=ESP.img']),
|
|
call(['mv', 'ESP.img', 'ESP'])
|
|
]
|
|
mock_run_bootctl.assert_called_once_with('root_dir')
|
|
mock_set_loader_entry.assert_called_once_with('root_dir', target.live)
|