Michael Hudson-Doyle has proposed merging ~mwhudson/curtin:more-grub-config-object into curtin:master.
Commit message: extend grub config object to handle all config under the 'grub' key Pinched a hacked down version of my deserialization code from subiquity... I'm not sure this 100% preserves the behaviour of install_devices being an empty list, None or absent entirely, the semantics around all that seem to be pretty confused. Requested reviews: curtin developers (curtin-dev) For more details, see: https://code.launchpad.net/~mwhudson/curtin/+git/curtin/+merge/444608 -- Your team curtin developers is requested to review the proposed merge of ~mwhudson/curtin:more-grub-config-object into curtin:master.
diff --git a/curtin/commands/curthooks.py b/curtin/commands/curthooks.py index 868fbad..759ed5e 100644 --- a/curtin/commands/curthooks.py +++ b/curtin/commands/curthooks.py @@ -423,9 +423,8 @@ def install_kernel(cfg, target): " System may not boot.", package) -def uefi_remove_old_loaders(grubcfg: dict, target): +def uefi_remove_old_loaders(grubcfg: config.GrubConfig, target): """Removes the old UEFI loaders from efibootmgr.""" - grubcfg = config.fromdict(config.GrubConfig, grubcfg) efi_state = util.get_efibootmgr(target) LOG.debug('UEFI remove old olders efi state:\n%s', efi_state) @@ -517,7 +516,7 @@ def _reorder_new_entry( def uefi_reorder_loaders( - grubcfg: dict, + grubcfg: config.GrubConfig, target: str, efi_orig_state: util.EFIBootState, variant: str, @@ -533,8 +532,6 @@ def uefi_reorder_loaders( is installed after the the previous first entry (before we installed grub). """ - grubcfg = config.fromdict(config.GrubConfig, grubcfg) - if not grubcfg.reorder_uefi: LOG.debug("Skipped reordering of UEFI boot methods.") LOG.debug("Currently booted UEFI loader might no longer boot.") @@ -578,9 +575,10 @@ def uefi_reorder_loaders( in_chroot.subp(['efibootmgr', '-o', new_boot_order]) -def uefi_remove_duplicate_entries(grubcfg: dict, target: str) -> None: - grubcfg = config.fromdict(config.GrubConfig, grubcfg) - +def uefi_remove_duplicate_entries( + grubcfg: config.GrubConfig, + target: str, + ) -> None: if not grubcfg.remove_duplicate_entries: LOG.debug("Skipped removing duplicate UEFI boot entries per config.") return @@ -725,7 +723,12 @@ def uefi_find_grub_device_ids(sconfig): return grub_device_ids -def setup_grub(cfg, target, osfamily, variant): +def setup_grub( + cfg: dict, + target: str, + osfamily: str, + variant: str, + ) -> None: # target is the path to the mounted filesystem # FIXME: these methods need moving to curtin.block @@ -733,11 +736,13 @@ def setup_grub(cfg, target, osfamily, variant): from curtin.commands.block_meta import (extract_storage_ordered_dict, get_path_to_storage_volume) - grubcfg = cfg.get('grub', {}) + grubcfg_d = cfg.get('grub', {}) # copy legacy top level name - if 'grub_install_devices' in cfg and 'install_devices' not in grubcfg: - grubcfg['install_devices'] = cfg['grub_install_devices'] + if 'grub_install_devices' in cfg and 'install_devices' not in grubcfg_d: + grubcfg_d['install_devices'] = cfg['grub_install_devices'] + + grubcfg = config.fromdict(config.GrubConfig, grubcfg_d) LOG.debug("setup grub on target %s", target) # if there is storage config, look for devices tagged with 'grub_device' @@ -763,19 +768,19 @@ def setup_grub(cfg, target, osfamily, variant): get_path_to_storage_volume(item_id, storage_cfg_odict)) if len(storage_grub_devices) > 0: - if len(grubcfg.get('install_devices', [])): + if grubcfg.install_devices is not None and \ + len(grubcfg.install_devices) > 0: LOG.warn("Storage Config grub device config takes precedence " "over grub 'install_devices' value, ignoring: %s", grubcfg['install_devices']) - grubcfg['install_devices'] = storage_grub_devices - - LOG.debug("install_devices: %s", grubcfg.get('install_devices')) - if 'install_devices' in grubcfg: - instdevs = grubcfg.get('install_devices') - if isinstance(instdevs, str): - instdevs = [instdevs] - if instdevs is None: - LOG.debug("grub installation disabled by config") + grubcfg.install_devices = storage_grub_devices + + LOG.debug("install_devices: %s", grubcfg.install_devices) + if grubcfg.install_devices is None: + LOG.debug("grub installation disabled by config") + instdevs = grubcfg.install_devices + elif len(grubcfg.install_devices) > 0: + instdevs = grubcfg.install_devices else: # If there were no install_devices found then we try to do the right # thing. That right thing is basically installing on all block @@ -823,7 +828,7 @@ def setup_grub(cfg, target, osfamily, variant): else: instdevs = ["none"] - update_nvram = grubcfg.get('update_nvram', True) + update_nvram = grubcfg.update_nvram if uefi_bootable and update_nvram: efi_orig_state = util.get_efibootmgr(target) uefi_remove_old_loaders(grubcfg, target) diff --git a/curtin/commands/install_grub.py b/curtin/commands/install_grub.py index 03b4670..2faa2c6 100644 --- a/curtin/commands/install_grub.py +++ b/curtin/commands/install_grub.py @@ -3,6 +3,7 @@ import re import platform import shutil import sys +from typing import List, Optional from curtin import block from curtin import config @@ -137,7 +138,7 @@ def prepare_grub_dir(target, grub_cfg): shutil.move(ci_cfg, ci_cfg + '.disabled') -def get_carryover_params(distroinfo): +def get_carryover_params(distroinfo) -> str: # return a string to append to installed systems boot parameters # it may include a '--' after a '---' # see LP: 1402042 for some history here. @@ -206,14 +207,17 @@ def replace_grub_cmdline_linux_default(target, new_args): LOG.debug('updated %s to set: %s', target_grubconf, newcontent) -def write_grub_config(target, grubcfg, grub_conf, new_params): - replace_default = config.value_as_boolean( - grubcfg.get('replace_linux_default', True)) +def write_grub_config( + target: str, + grubcfg: config.GrubConfig, + grub_conf: str, + new_params: str, + ) -> None: + replace_default = grubcfg.replace_linux_default if replace_default: replace_grub_cmdline_linux_default(target, new_params) - probe_os = config.value_as_boolean( - grubcfg.get('probe_additional_os', False)) + probe_os = grubcfg.probe_additional_os if not probe_os: probe_content = [ ('# Curtin disable grub os prober that might find other ' @@ -224,10 +228,7 @@ def write_grub_config(target, grubcfg, grub_conf, new_params): "\n".join(probe_content), omode='a+') # if terminal is present in config, but unset, then don't - grub_terminal = grubcfg.get('terminal', 'console') - if not isinstance(grub_terminal, str): - raise ValueError("Unexpected value %s for 'terminal'. " - "Value must be a string" % grub_terminal) + grub_terminal = grubcfg.terminal if not grub_terminal.lower() == "unmodified": terminal_content = [ '# Curtin configured GRUB_TERMINAL value', @@ -394,7 +395,13 @@ def check_target_arch_machine(target, arch=None, machine=None, uefi=None): raise RuntimeError(errmsg) -def install_grub(devices, target, uefi=None, grubcfg=None): +def install_grub( + devices: List[str], + target: str, + *, + grubcfg: config.GrubConfig, + uefi: Optional[bool] = None, + ): """Install grub to devices inside target chroot. :param: devices: List of block device paths to install grub upon. @@ -411,8 +418,8 @@ def install_grub(devices, target, uefi=None, grubcfg=None): raise ValueError("Invalid parameter 'target': %s" % target) LOG.debug("installing grub to target=%s devices=%s [replace_defaults=%s]", - target, devices, grubcfg.get('replace_default')) - update_nvram = config.value_as_boolean(grubcfg.get('update_nvram', True)) + target, devices, grubcfg.replace_linux_default) + update_nvram = grubcfg.update_nvram distroinfo = distro.get_distroinfo(target=target) target_arch = distro.get_architecture(target=target) rhel_ver = (distro.rpm_get_dist_id(target) @@ -460,7 +467,7 @@ def install_grub_main(args): cfg = config.load_command_config(args, state) stack_prefix = state.get('report_stack_prefix', '') uefi = util.is_uefi_bootable() - grubcfg = cfg.get('grub') + grubcfg = config.fromdict(config.GrubConfig, cfg.get('grub')) with events.ReportEventStack( name=stack_prefix, reporting_enabled=True, level="INFO", description="Installing grub to target devices"): diff --git a/curtin/config.py b/curtin/config.py index b65410b..39898a4 100644 --- a/curtin/config.py +++ b/curtin/config.py @@ -1,6 +1,7 @@ # This file is part of curtin. See LICENSE file for copyright and license info. import json +import typing import attr import yaml @@ -129,23 +130,142 @@ def value_as_boolean(value): return value not in false_values +def _convert_install_devices(value): + if isinstance(value, str): + return [value] + return value + + @attr.s(auto_attribs=True) class GrubConfig: - # This is not yet every option that appears under the "grub" config key, - # but it is a work in progress. + install_devices: typing.Optional[typing.List[str]] = attr.ib( + converter=_convert_install_devices, default=attr.Factory(list)) + probe_additional_os: bool = attr.ib( + default=False, converter=value_as_boolean) + remove_duplicate_entries: bool = True remove_old_uefi_loaders: bool = True reorder_uefi: bool = True reorder_uefi_force_fallback: bool = attr.ib( default=False, converter=value_as_boolean) - remove_duplicate_entries: bool = True + replace_linux_default: bool = attr.ib( + default=True, converter=value_as_boolean) + terminal: str = "console" + update_nvram: bool = attr.ib(default=True, converter=value_as_boolean) -def fromdict(cls, d): - kw = {} - for field in attr.fields(cls): - if field.name in d: - kw[field.name] = d[field.name] - return cls(**kw) +class SerializationError(Exception): + def __init__(self, obj, path, message): + self.obj = obj + self.path = path + self.message = message + + def __str__(self): + p = self.path + if not p: + p = 'top-level' + return f"processing {self.obj}: at {p}, {self.message}" + + +@attr.s(auto_attribs=True) +class SerializationContext: + obj: typing.Any + cur: typing.Any + path: str + metadata: typing.Optional[typing.Dict] + + @classmethod + def new(cls, obj): + return SerializationContext(obj, obj, '', {}) + + def child(self, path, cur, metadata=None): + if metadata is None: + metadata = self.metadata + return attr.evolve( + self, path=self.path + path, cur=cur, metadata=metadata) + + def error(self, message): + raise SerializationError(self.obj, self.path, message) + + def assert_type(self, typ): + if type(self.cur) is not typ: + self.error("{!r} is not a {}".format(self.cur, typ)) + + +class Deserializer: + + def __init__(self): + self.typing_walkers = { + list: self._walk_List, + typing.List: self._walk_List, + typing.Union: self._walk_Union, + } + self.type_deserializers = {} + for typ in int, str, bool, list, dict, type(None): + self.type_deserializers[typ] = self._scalar + + def _scalar(self, annotation, context): + context.assert_type(annotation) + return context.cur + + def _walk_List(self, meth, args, context): + return [ + meth(args[0], context.child(f'[{i}]', v)) + for i, v in enumerate(context.cur) + ] + + def _walk_Union(self, meth, args, context): + NoneType = type(None) + if NoneType in args: + args = [a for a in args if a is not NoneType] + if len(args) == 1: + # I.e. Optional[thing] + if context.cur is None: + return context.cur + return meth(args[0], context) + context.error(f"cannot serialize Union[{args}]") + + def _deserialize_attr(self, annotation, context): + context.assert_type(dict) + args = {} + fields = { + field.name: field for field in attr.fields(annotation) + } + for key, value in context.cur.items(): + if key not in fields: + continue + field = fields[key] + if field.converter: + value = field.converter(value) + args[field.name] = self._deserialize( + field.type, + context.child(f'[{key!r}]', value, field.metadata)) + return annotation(**args) + + def _deserialize(self, annotation, context): + if annotation is None: + context.assert_type(type(None)) + return None + if annotation is typing.Any: + return context.cur + if attr.has(annotation): + return self._deserialize_attr(annotation, context) + origin = getattr(annotation, '__origin__', None) + if origin is not None: + return self.typing_walkers[origin]( + self._deserialize, annotation.__args__, context) + return self.type_deserializers[annotation](annotation, context) + + def deserialize(self, annotation, value): + context = SerializationContext.new(value) + return self._deserialize(annotation, context) + + +T = typing.TypeVar("T") + + +def fromdict(cls: typing.Type[T], d) -> T: + deserializer = Deserializer() + return deserializer.deserialize(cls, d) # vi: ts=4 expandtab syntax=python diff --git a/tests/unittests/test_commands_install_grub.py b/tests/unittests/test_commands_install_grub.py index 00004d7..fc19da3 100644 --- a/tests/unittests/test_commands_install_grub.py +++ b/tests/unittests/test_commands_install_grub.py @@ -1,5 +1,6 @@ # This file is part of curtin. See LICENSE file for copyright and license info. +from curtin import config from curtin import distro from curtin import util from curtin import paths @@ -456,7 +457,7 @@ class TestWriteGrubConfig(CiTestCase): self.assertEqual(expected, found) def test_write_grub_config_defaults(self): - grubcfg = {} + grubcfg = config.GrubConfig() new_params = ['foo=bar', 'wark=1'] expected_default = "\n".join([ 'GRUB_CMDLINE_LINUX_DEFAULT="foo=bar wark=1"', '']) @@ -473,7 +474,7 @@ class TestWriteGrubConfig(CiTestCase): self._verify_expected(expected_default, expected_curtin) def test_write_grub_config_no_replace(self): - grubcfg = {'replace_linux_default': False} + grubcfg = config.GrubConfig(replace_linux_default=False) new_params = ['foo=bar', 'wark=1'] expected_default = "\n".join([]) expected_curtin = "\n".join([ @@ -489,7 +490,7 @@ class TestWriteGrubConfig(CiTestCase): self._verify_expected(expected_default, expected_curtin) def test_write_grub_config_disable_probe(self): - grubcfg = {'probe_additional_os': False} # DISABLE_OS_PROBER=1 + grubcfg = config.GrubConfig(probe_additional_os=False) new_params = ['foo=bar', 'wark=1'] expected_default = "\n".join([ 'GRUB_CMDLINE_LINUX_DEFAULT="foo=bar wark=1"', '']) @@ -506,7 +507,7 @@ class TestWriteGrubConfig(CiTestCase): self._verify_expected(expected_default, expected_curtin) def test_write_grub_config_enable_probe(self): - grubcfg = {'probe_additional_os': True} # DISABLE_OS_PROBER=0, default + grubcfg = config.GrubConfig(probe_additional_os=True) new_params = ['foo=bar', 'wark=1'] expected_default = "\n".join([ 'GRUB_CMDLINE_LINUX_DEFAULT="foo=bar wark=1"', '']) @@ -520,10 +521,9 @@ class TestWriteGrubConfig(CiTestCase): self._verify_expected(expected_default, expected_curtin) def test_write_grub_config_no_grub_settings_file(self): - grubcfg = { - 'probe_additional_os': True, - 'terminal': 'unmodified', - } + grubcfg = config.GrubConfig( + probe_additional_os=True, + terminal='unmodified') new_params = [] install_grub.write_grub_config( self.target, grubcfg, self.grubconf, new_params) @@ -531,7 +531,8 @@ class TestWriteGrubConfig(CiTestCase): self.assertFalse(os.path.exists(self.target_grubconf)) def test_write_grub_config_specify_terminal(self): - grubcfg = {'terminal': 'serial'} + grubcfg = config.GrubConfig( + terminal='serial') new_params = ['foo=bar', 'wark=1'] expected_default = "\n".join([ 'GRUB_CMDLINE_LINUX_DEFAULT="foo=bar wark=1"', '']) @@ -548,7 +549,7 @@ class TestWriteGrubConfig(CiTestCase): self._verify_expected(expected_default, expected_curtin) def test_write_grub_config_terminal_unmodified(self): - grubcfg = {'terminal': 'unmodified'} + grubcfg = config.GrubConfig(terminal='unmodified') new_params = ['foo=bar', 'wark=1'] expected_default = "\n".join([ 'GRUB_CMDLINE_LINUX_DEFAULT="foo=bar wark=1"', '']) @@ -562,13 +563,6 @@ class TestWriteGrubConfig(CiTestCase): self._verify_expected(expected_default, expected_curtin) - def test_write_grub_config_invalid_terminal(self): - grubcfg = {'terminal': ['color-tv']} - new_params = ['foo=bar', 'wark=1'] - with self.assertRaises(ValueError): - install_grub.write_grub_config( - self.target, grubcfg, self.grubconf, new_params) - class TestFindEfiLoader(CiTestCase): @@ -1119,39 +1113,43 @@ class TestInstallGrub(CiTestCase): def test_grub_install_raise_exception_on_no_devices(self): devices = [] with self.assertRaises(ValueError): - install_grub.install_grub(devices, self.target, False, {}) + install_grub.install_grub( + devices, self.target, uefi=False, grubcfg=config.GrubConfig()) def test_grub_install_raise_exception_on_no_target(self): devices = ['foobar'] with self.assertRaises(ValueError): - install_grub.install_grub(devices, None, False, {}) + install_grub.install_grub( + devices, None, uefi=False, grubcfg=config.GrubConfig()) def test_grub_install_raise_exception_on_s390x(self): self.m_distro_get_architecture.return_value = 's390x' self.m_platform_machine.return_value = 's390x' devices = ['foobar'] with self.assertRaises(RuntimeError): - install_grub.install_grub(devices, self.target, False, {}) + install_grub.install_grub( + devices, self.target, uefi=False, grubcfg=config.GrubConfig()) def test_grub_install_raise_exception_on_armv7(self): self.m_distro_get_architecture.return_value = 'armhf' self.m_platform_machine.return_value = 'armv7l' devices = ['foobar'] with self.assertRaises(RuntimeError): - install_grub.install_grub(devices, self.target, False, {}) + install_grub.install_grub( + devices, self.target, uefi=False, grubcfg=config.GrubConfig()) def test_grub_install_raise_exception_on_arm64_no_uefi(self): self.m_distro_get_architecture.return_value = 'arm64' self.m_platform_machine.return_value = 'aarch64' - uefi = False devices = ['foobar'] with self.assertRaises(RuntimeError): - install_grub.install_grub(devices, self.target, uefi, {}) + install_grub.install_grub( + devices, self.target, uefi=False, grubcfg=config.GrubConfig()) def test_grub_install_ubuntu(self): devices = ['/dev/disk-a-part1'] uefi = False - grubcfg = {} + grubcfg = config.GrubConfig() grub_conf = self.tmp_path('grubconf') new_params = [] self.m_get_grub_package_name.return_value = ('grub-pc', 'i386-pc') @@ -1161,7 +1159,8 @@ class TestInstallGrub(CiTestCase): self.m_gen_install_commands.return_value = ( [['/bin/true']], [['/bin/false']]) - install_grub.install_grub(devices, self.target, uefi, grubcfg) + install_grub.install_grub( + devices, self.target, uefi=uefi, grubcfg=grubcfg) self.m_distro_get_distroinfo.assert_called_with(target=self.target) self.m_distro_get_architecture.assert_called_with(target=self.target) @@ -1189,8 +1188,7 @@ class TestInstallGrub(CiTestCase): def test_uefi_grub_install_ubuntu(self): devices = ['/dev/disk-a-part1'] uefi = True - update_nvram = True - grubcfg = {'update_nvram': update_nvram} + grubcfg = config.GrubConfig(update_nvram=True) grub_conf = self.tmp_path('grubconf') new_params = [] grub_name = 'grub-efi-amd64' @@ -1203,7 +1201,8 @@ class TestInstallGrub(CiTestCase): self.m_gen_uefi_install_commands.return_value = ( [['/bin/true']], [['/bin/false']]) - install_grub.install_grub(devices, self.target, uefi, grubcfg) + install_grub.install_grub( + devices, self.target, uefi=uefi, grubcfg=grubcfg) self.m_distro_get_distroinfo.assert_called_with(target=self.target) self.m_distro_get_architecture.assert_called_with(target=self.target) @@ -1219,8 +1218,8 @@ class TestInstallGrub(CiTestCase): self.m_get_grub_install_command.assert_called_with( uefi, self.distroinfo, self.target) self.m_gen_uefi_install_commands.assert_called_with( - grub_name, grub_target, grub_cmd, update_nvram, self.distroinfo, - devices, self.target) + grub_name, grub_target, grub_cmd, grubcfg.update_nvram, + self.distroinfo, devices, self.target) self.m_subp.assert_has_calls([ mock.call(['/bin/true'], env=self.env, capture=True, @@ -1232,8 +1231,7 @@ class TestInstallGrub(CiTestCase): def test_uefi_grub_install_ubuntu_multiple_esp(self): devices = ['/dev/disk-a-part1'] uefi = True - update_nvram = True - grubcfg = {'update_nvram': update_nvram} + grubcfg = config.GrubConfig(update_nvram=True) grub_conf = self.tmp_path('grubconf') new_params = [] grub_name = 'grub-efi-amd64' @@ -1246,7 +1244,8 @@ class TestInstallGrub(CiTestCase): self.m_gen_uefi_install_commands.return_value = ( [['/bin/true']], [['/bin/false']]) - install_grub.install_grub(devices, self.target, uefi, grubcfg) + install_grub.install_grub( + devices, self.target, uefi=uefi, grubcfg=grubcfg) self.m_distro_get_distroinfo.assert_called_with(target=self.target) self.m_distro_get_architecture.assert_called_with(target=self.target) @@ -1262,8 +1261,8 @@ class TestInstallGrub(CiTestCase): self.m_get_grub_install_command.assert_called_with( uefi, self.distroinfo, self.target) self.m_gen_uefi_install_commands.assert_called_with( - grub_name, grub_target, grub_cmd, update_nvram, self.distroinfo, - devices, self.target) + grub_name, grub_target, grub_cmd, grubcfg.update_nvram, + self.distroinfo, devices, self.target) self.m_subp.assert_has_calls([ mock.call(['/bin/true'], env=self.env, capture=True, diff --git a/tests/unittests/test_config.py b/tests/unittests/test_config.py index af7f251..ae51744 100644 --- a/tests/unittests/test_config.py +++ b/tests/unittests/test_config.py @@ -3,6 +3,9 @@ import copy import json import textwrap +import typing + +import attr from curtin import config from .helpers import CiTestCase @@ -139,4 +142,58 @@ def _replace_consts(cfgstr): cfgstr = cfgstr.replace(k, v) return cfgstr + +class TestDeserializer(CiTestCase): + + def test_scalar(self): + deserializer = config.Deserializer() + self.assertEqual(1, deserializer.deserialize(int, 1)) + self.assertEqual("a", deserializer.deserialize(str, "a")) + + def test_attr(self): + deserializer = config.Deserializer() + + @attr.s(auto_attribs=True) + class Point: + x: int + y: int + + self.assertEqual( + Point(x=1, y=2), + deserializer.deserialize(Point, {'x': 1, 'y': 2})) + + def test_list(self): + deserializer = config.Deserializer() + self.assertEqual( + [1, 2, 3], + deserializer.deserialize(typing.List[int], [1, 2, 3])) + + def test_optional(self): + deserializer = config.Deserializer() + self.assertEqual( + 1, + deserializer.deserialize(typing.Optional[int], 1)) + self.assertEqual( + None, + deserializer.deserialize(typing.Optional[int], None)) + + def test_converter(self): + deserializer = config.Deserializer() + + @attr.s(auto_attribs=True) + class WithoutConverter: + val: bool + + with self.assertRaises(config.SerializationError): + deserializer.deserialize(WithoutConverter, {"val": "on"}) + + @attr.s(auto_attribs=True) + class WithConverter: + val: bool = attr.ib(converter=config.value_as_boolean) + + self.assertEqual( + WithConverter(val=True), + deserializer.deserialize(WithConverter, {"val": "on"})) + + # vi: ts=4 expandtab syntax=python diff --git a/tests/unittests/test_curthooks.py b/tests/unittests/test_curthooks.py index 6067ea4..d6b5445 100644 --- a/tests/unittests/test_curthooks.py +++ b/tests/unittests/test_curthooks.py @@ -636,13 +636,11 @@ class TestSetupGrub(CiTestCase): cfg = { 'grub_install_devices': ['/dev/vdb'] } - updated_cfg = { - 'install_devices': ['/dev/vdb'] - } curthooks.setup_grub(cfg, self.target, osfamily=self.distro_family, variant=self.variant) self.m_install_grub.assert_called_with( - ['/dev/vdb'], self.target, uefi=False, grubcfg=updated_cfg) + ['/dev/vdb'], self.target, uefi=False, + grubcfg=config.GrubConfig(install_devices=['/dev/vdb'])) def test_uses_install_devices_in_grubcfg(self): cfg = { @@ -654,7 +652,8 @@ class TestSetupGrub(CiTestCase): cfg, self.target, osfamily=self.distro_family, variant=self.variant) self.m_install_grub.assert_called_with( - ['/dev/vdb'], self.target, uefi=False, grubcfg=cfg.get('grub')) + ['/dev/vdb'], self.target, uefi=False, + grubcfg=config.fromdict(config.GrubConfig, cfg.get('grub'))) @patch('curtin.commands.block_meta.multipath') @patch('curtin.commands.curthooks.os.path.exists') @@ -678,7 +677,7 @@ class TestSetupGrub(CiTestCase): variant=self.variant) self.m_install_grub.assert_called_with( ['/dev/vdb'], self.target, uefi=False, - grubcfg={'install_devices': ['/dev/vdb']}) + grubcfg=config.GrubConfig(install_devices=['/dev/vdb'])) @patch('curtin.commands.block_meta.multipath') @patch('curtin.block.is_valid_device') @@ -729,8 +728,9 @@ class TestSetupGrub(CiTestCase): variant='centos') self.m_install_grub.assert_called_with( ['/dev/vdb1'], self.target, uefi=True, - grubcfg={'update_nvram': False, 'install_devices': ['/dev/vdb1']} - ) + grubcfg=config.GrubConfig( + update_nvram=False, + install_devices=['/dev/vdb1'])) def test_grub_install_installs_to_none_if_install_devices_None(self): cfg = { @@ -742,7 +742,7 @@ class TestSetupGrub(CiTestCase): variant=self.variant) self.m_install_grub.assert_called_with( ['none'], self.target, uefi=False, - grubcfg={'install_devices': None} + grubcfg=config.GrubConfig(install_devices=None), ) @patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a) @@ -772,7 +772,8 @@ class TestSetupGrub(CiTestCase): curthooks.setup_grub(cfg, self.target, osfamily=self.distro_family, variant=self.variant) self.m_install_grub.assert_called_with( - ['/dev/vdb'], self.target, uefi=True, grubcfg=cfg.get('grub') + ['/dev/vdb'], self.target, uefi=True, + grubcfg=config.fromdict(config.GrubConfig, cfg.get('grub')) ) @patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a) @@ -1101,7 +1102,7 @@ class TestUefiRemoveDuplicateEntries(CiTestCase): @patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a) def test_uefi_remove_duplicate_entries(self): - grubcfg = {} + grubcfg = config.GrubConfig() curthooks.uefi_remove_duplicate_entries(grubcfg, self.target) self.assertEqual([ call(['efibootmgr', '--bootnum=0001', '--delete-bootnum'], @@ -1112,7 +1113,7 @@ class TestUefiRemoveDuplicateEntries(CiTestCase): @patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a) def test_uefi_remove_duplicate_entries_no_bootcurrent(self): - grubcfg = {} + grubcfg = config.GrubConfig() efiout = copy_efi_state(self.efibootmgr_output) efiout.current = '' self.m_efibootmgr.return_value = efiout @@ -1126,15 +1127,15 @@ class TestUefiRemoveDuplicateEntries(CiTestCase): @patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a) def test_uefi_remove_duplicate_entries_disabled(self): - grubcfg = { - 'remove_duplicate_entries': False, - } + grubcfg = config.GrubConfig( + remove_duplicate_entries=False, + ) curthooks.uefi_remove_duplicate_entries(grubcfg, self.target) self.assertEquals([], self.m_subp.call_args_list) @patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a) def test_uefi_remove_duplicate_entries_skip_bootcurrent(self): - grubcfg = {} + grubcfg = config.GrubConfig() efiout = copy_efi_state(self.efibootmgr_output) efiout.current = '0003' self.m_efibootmgr.return_value = efiout @@ -1148,7 +1149,7 @@ class TestUefiRemoveDuplicateEntries(CiTestCase): @patch.object(util.ChrootableTarget, "__enter__", new=lambda a: a) def test_uefi_remove_duplicate_entries_no_change(self): - grubcfg = {} + grubcfg = config.GrubConfig() self.m_efibootmgr.return_value = util.EFIBootState( order=[], timeout='',
-- Mailing list: https://launchpad.net/~curtin-dev Post to : curtin-dev@lists.launchpad.net Unsubscribe : https://launchpad.net/~curtin-dev More help : https://help.launchpad.net/ListHelp