From bddf25559d9a5c573c1ff945ea8c1b432cc10445 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Sch=C3=A4fer?= Date: Sat, 13 Jan 2024 23:25:41 +0100 Subject: [PATCH 1/2] Compat entry name for kernel-install in sd-boot systemd-boot tools like kernel-install expect a certain entry naming policy. This commit adapts kiwi to adapt to this policy. The name for the default entry is constructed out of the ID information from /etc/os-release followed by the name of the kernel as it is represented by the directory name in /lib/modules/... This Fixes #2417 --- kiwi/bootloader/config/systemd_boot.py | 26 +++++++++-- kiwi/bootloader/template/systemd_boot.py | 2 +- kiwi/exceptions.py | 6 +++ kiwi/utils/os_release.py | 46 +++++++++++++++++++ test/data/etc/os-release | 12 +++++ .../bootloader/config/systemd_boot_test.py | 42 ++++++++++++++--- .../bootloader/template/systemd_boot_test.py | 3 +- test/unit/utils/os_release_test.py | 19 ++++++++ 8 files changed, 143 insertions(+), 13 deletions(-) create mode 100644 kiwi/utils/os_release.py create mode 100644 test/data/etc/os-release create mode 100644 test/unit/utils/os_release_test.py diff --git a/kiwi/bootloader/config/systemd_boot.py b/kiwi/bootloader/config/systemd_boot.py index 5194a0025d2..5869c62abb2 100644 --- a/kiwi/bootloader/config/systemd_boot.py +++ b/kiwi/bootloader/config/systemd_boot.py @@ -16,6 +16,7 @@ # along with kiwi. If not, see # import os +import glob from string import Template from typing import Dict @@ -28,10 +29,12 @@ from kiwi.storage.loop_device import LoopDevice from kiwi.storage.disk import Disk from kiwi.command import Command +from kiwi.utils.os_release import OsRelease from kiwi.exceptions import ( KiwiTemplateError, - KiwiBootLoaderTargetError + KiwiBootLoaderTargetError, + KiwiKernelLookupError ) import kiwi.defaults as defaults @@ -83,10 +86,20 @@ def set_loader_entry(self, root_dir: str, target: str) -> None: :param str target: target identifier, one of disk, live(iso) or install(iso) """ + os_release = OsRelease(root_dir) + try: + kernel_name = os.path.basename( + list(glob.iglob(f'{root_dir}/lib/modules/*'))[0] + ) + except Exception as issue: + raise KiwiKernelLookupError( + f'Kernel lookup in {root_dir}/lib/modules failed with {issue}' + ) + default_entry = f'{os_release.get("ID")}-{kernel_name}.conf' BootLoaderSystemdBoot._write_config_file( BootLoaderTemplateSystemdBoot().get_entry_template(), - root_dir + '/boot/efi/loader/entries/main.conf', - self._get_template_parameters() + root_dir + f'/boot/efi/loader/entries/{default_entry}', + self._get_template_parameters(default_entry) ) def _create_embedded_fat_efi_image(self, path: str): @@ -192,14 +205,17 @@ def _run_bootctl(self, root_dir: str) -> None: self._get_template_parameters() ) - def _get_template_parameters(self) -> Dict[str, str]: + def _get_template_parameters( + self, default_entry: str = 'main.conf' + ) -> Dict[str, str]: return { 'kernel_file': self.custom_args['kernel'] or 'vmlinuz', 'initrd_file': self.custom_args['initrd'] or 'initrd', 'boot_options': self.cmdline, 'boot_timeout': self.timeout, 'bootpath': self.get_boot_path(), - 'title': self.get_menu_entry_title() + 'title': self.get_menu_entry_title(), + 'default_entry': default_entry } @staticmethod diff --git a/kiwi/bootloader/template/systemd_boot.py b/kiwi/bootloader/template/systemd_boot.py index 02cfe2e3d32..c52bfc26ae0 100644 --- a/kiwi/bootloader/template/systemd_boot.py +++ b/kiwi/bootloader/template/systemd_boot.py @@ -28,7 +28,7 @@ def __init__(self): self.loader = dedent(''' # kiwi generated loader config file - default main.conf + default ${default_entry} console-mode max editor no ''').strip() + self.cr diff --git a/kiwi/exceptions.py b/kiwi/exceptions.py index c62ead7542d..d15399d0de2 100644 --- a/kiwi/exceptions.py +++ b/kiwi/exceptions.py @@ -451,6 +451,12 @@ class KiwiOCIArchiveToolError(KiwiError): """ +class KiwiOSReleaseImportError(KiwiError): + """ + Exception raised if reading etc/os-release caused an issue + """ + + class KiwiPackageManagerSetupError(KiwiError): """ Exception raised if an attempt was made to create a package diff --git a/kiwi/utils/os_release.py b/kiwi/utils/os_release.py new file mode 100644 index 00000000000..a502bed402b --- /dev/null +++ b/kiwi/utils/os_release.py @@ -0,0 +1,46 @@ +# Copyright (c) 2024 SUSE Software Solutions Germany GmbH. All rights reserved. +# +# This file is part of kiwi. +# +# kiwi is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# kiwi is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with kiwi. If not, see +# +import csv + +# project +from kiwi.exceptions import KiwiOSReleaseImportError + + +class OsRelease: + """ + **Read os-release information** + """ + def __init__(self, root_dir: str): + self.data = {} + os_release = root_dir + '/etc/os-release' + try: + with open(os_release) as osdata: + reader = csv.reader(osdata, delimiter='=') + self.data = dict(reader) + except Exception as issue: + raise KiwiOSReleaseImportError( + f'Import of {os_release} failed with {issue}' + ) + + def get(self, key: str) -> str: + """ + Return value for key or an empty string if not present + + :param string key: key name from os-release + """ + return self.data.get(key) or '' diff --git a/test/data/etc/os-release b/test/data/etc/os-release new file mode 100644 index 00000000000..3d57eb7c5b3 --- /dev/null +++ b/test/data/etc/os-release @@ -0,0 +1,12 @@ +NAME="openSUSE Leap" +VERSION="15.5" +ID="opensuse-leap" +ID_LIKE="suse opensuse" +VERSION_ID="15.5" +PRETTY_NAME="openSUSE Leap 15.5" +ANSI_COLOR="0;32" +CPE_NAME="cpe:/o:opensuse:leap:15.5" +BUG_REPORT_URL="https://bugs.opensuse.org" +HOME_URL="https://www.opensuse.org/" +DOCUMENTATION_URL="https://en.opensuse.org/Portal:Leap" +LOGO="distributor-logo-Leap" diff --git a/test/unit/bootloader/config/systemd_boot_test.py b/test/unit/bootloader/config/systemd_boot_test.py index a0d15cd4d9c..4fa2294a95a 100644 --- a/test/unit/bootloader/config/systemd_boot_test.py +++ b/test/unit/bootloader/config/systemd_boot_test.py @@ -7,7 +7,8 @@ from kiwi.exceptions import ( KiwiTemplateError, - KiwiBootLoaderTargetError + KiwiBootLoaderTargetError, + KiwiKernelLookupError ) @@ -41,6 +42,8 @@ def test_setup_loader_raises_invalid_target(self): self.bootloader.setup_loader('iso') @patch('os.path.exists') + @patch('kiwi.bootloader.config.systemd_boot.OsRelease') + @patch('kiwi.bootloader.config.systemd_boot.glob.iglob') @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') @@ -54,8 +57,14 @@ def test_setup_loader( 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_iglob, + mock_OsRelease, mock_os_path_exists ): + mock_iglob.return_value = ['/lib/modules/5.3.18-59.10-default'] + os_release = Mock() + os_release.get.return_value = 'opensuse-leap' + mock_OsRelease.return_value = os_release kernel_info = Mock() kernel_info.kernel_version = 'kernel-version' kernel_info.kernel_filename = 'kernel-filename' @@ -65,13 +74,16 @@ def test_setup_loader( self.bootloader.setup_loader('disk') assert mock_write_config_file.call_args_list == [ call( - mock_BootLoaderTemplateSystemdBoot.return_value.get_loader_template.return_value, + 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_BootLoaderTemplateSystemdBoot. + return_value.get_entry_template.return_value, + 'system_root_mount/boot/efi/loader/entries/' + 'opensuse-leap-5.3.18-59.10-default.conf', mock_get_template_parameters.return_value ) ] @@ -104,10 +116,27 @@ def test_setup_loader( ) ] + @patch('kiwi.bootloader.config.systemd_boot.OsRelease') + @patch('kiwi.bootloader.config.systemd_boot.glob.iglob') @patch.object(BootLoaderSystemdBoot, '_write_config_file') - def test_set_loader_entry(self, mock_write_config_file): + def test_set_loader_entry( + self, mock_write_config_file, mock_iglob, mock_OsRelease + ): + mock_iglob.return_value = ['/lib/modules/5.3.18-59.10-default'] + os_release = Mock() + os_release.get.return_value = 'opensuse-leap' + mock_OsRelease.return_value = os_release self.bootloader.set_loader_entry('root_dir', 'disk') + @patch('kiwi.bootloader.config.systemd_boot.OsRelease') + @patch('kiwi.bootloader.config.systemd_boot.glob.iglob') + def test_set_loader_entry_kernel_lookup_raises( + self, mock_iglob, mock_OsRelease + ): + mock_iglob.return_value = None + with raises(KiwiKernelLookupError): + 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() @@ -120,7 +149,8 @@ def test_get_template_parameters(self): 'boot_options': '', 'boot_timeout': 0, 'bootpath': 'bootpath', - 'title': 'title' + 'title': 'title', + 'default_entry': 'main.conf' } @patch('kiwi.bootloader.config.systemd_boot.Path.create') diff --git a/test/unit/bootloader/template/systemd_boot_test.py b/test/unit/bootloader/template/systemd_boot_test.py index 2e0ec3613ad..ab3284aa052 100644 --- a/test/unit/bootloader/template/systemd_boot_test.py +++ b/test/unit/bootloader/template/systemd_boot_test.py @@ -10,5 +10,6 @@ def setup_method(self, cls): def test_get_loader_template(self): assert self.systemd_boot.get_loader_template().substitute( - boot_timeout='10' + boot_timeout='10', + default_entry='main.conf' ) diff --git a/test/unit/utils/os_release_test.py b/test/unit/utils/os_release_test.py new file mode 100644 index 00000000000..f90633497b5 --- /dev/null +++ b/test/unit/utils/os_release_test.py @@ -0,0 +1,19 @@ +from pytest import raises + +from kiwi.utils.os_release import OsRelease +from kiwi.exceptions import KiwiOSReleaseImportError + + +class TestOsRelease(object): + def setup(self): + self.os_release = OsRelease('../data') + + def setup_method(self, cls): + self.setup() + + def test_setup_raises(self): + with raises(KiwiOSReleaseImportError): + OsRelease('does-not-exist') + + def test_get(self): + assert self.os_release.get('ID') == 'opensuse-leap' From 8551ebca7a5c37fef85486eaf101ab363a7578ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcus=20Sch=C3=A4fer?= Date: Mon, 15 Jan 2024 17:57:51 +0100 Subject: [PATCH 2/2] Don't create default entry for sd-boot loader.conf --- kiwi/bootloader/template/systemd_boot.py | 1 - test/unit/bootloader/template/systemd_boot_test.py | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/kiwi/bootloader/template/systemd_boot.py b/kiwi/bootloader/template/systemd_boot.py index c52bfc26ae0..83f2d0a0622 100644 --- a/kiwi/bootloader/template/systemd_boot.py +++ b/kiwi/bootloader/template/systemd_boot.py @@ -28,7 +28,6 @@ def __init__(self): self.loader = dedent(''' # kiwi generated loader config file - default ${default_entry} console-mode max editor no ''').strip() + self.cr diff --git a/test/unit/bootloader/template/systemd_boot_test.py b/test/unit/bootloader/template/systemd_boot_test.py index ab3284aa052..2e0ec3613ad 100644 --- a/test/unit/bootloader/template/systemd_boot_test.py +++ b/test/unit/bootloader/template/systemd_boot_test.py @@ -10,6 +10,5 @@ def setup_method(self, cls): def test_get_loader_template(self): assert self.systemd_boot.get_loader_template().substitute( - boot_timeout='10', - default_entry='main.conf' + boot_timeout='10' )