From 3ad22b2964cf011aae49c4b136a1f853b231e64b Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Thu, 11 Jan 2024 11:29:50 +0100 Subject: [PATCH] Enable RPM OSTree from container source in payload (#2125655) We have a new RPMOStreeContainer source now which enables us to use containers as installation source. In this commit we adapt the current rpm_ostree to be able to consume this new container source. Add a lot of rpm ostree tasks tests for the container source. Resolves: RHEL-2250 --- .../modules/payloads/payload/factory.py | 2 +- .../payload/rpm_ostree/installation.py | 109 +++++++++++++++--- .../payloads/payload/rpm_ostree/rpm_ostree.py | 3 +- pyanaconda/modules/payloads/source/factory.py | 2 + pyanaconda/payload/rpmostreepayload.py | 30 +++-- .../payloads/payload/test_rpm_ostree.py | 5 +- 6 files changed, 121 insertions(+), 30 deletions(-) diff --git a/pyanaconda/modules/payloads/payload/factory.py b/pyanaconda/modules/payloads/payload/factory.py index c32c9ebd3115..c6097287af27 100644 --- a/pyanaconda/modules/payloads/payload/factory.py +++ b/pyanaconda/modules/payloads/payload/factory.py @@ -58,7 +58,7 @@ def get_type_for_kickstart(cls, data): :param data: a kickstart data :return: a payload type """ - if data.ostreesetup.seen: + if data.ostreesetup.seen or data.ostreecontainer.seen: return PayloadType.RPM_OSTREE if data.liveimg.seen: diff --git a/pyanaconda/modules/payloads/payload/rpm_ostree/installation.py b/pyanaconda/modules/payloads/payload/rpm_ostree/installation.py index 63959a5c631c..d995a4d83534 100644 --- a/pyanaconda/modules/payloads/payload/rpm_ostree/installation.py +++ b/pyanaconda/modules/payloads/payload/rpm_ostree/installation.py @@ -51,6 +51,60 @@ def safe_exec_with_redirect(cmd, argv, successful_return_codes=(0,), **kwargs): ) +def _get_ref(data): + """Get ref or name based on source. + + OSTree container don't have ref because it's specified by the container. In that case let's + return just url for reporting. + + :param data: OSTree source structure + :return str: ref or name based on source + """ + # Variable substitute the ref: https://pagure.io/atomic-wg/issue/299 + if data.is_container(): + # we don't have ref with container; there are not multiple references in one container + return data.url + else: + return RpmOstree.varsubst_basearch(data.ref) + + +def _get_stateroot(data): + """Get stateroot. + + The OSTree renamed old osname to stateroot for containers. + + :param data: OSTree source structure + :return str: stateroot or osname value based on source + """ + if data.is_container(): + # osname was renamed to stateroot so let's use the new name + if data.stateroot: + return data.stateroot + else: + # The stateroot doesn't have to be defined + # https://github.com/ostreedev/ostree-rs-ext/pull/462/files + # However, it's working just for a subset of calls now. + # TODO: Remove this when all ostree commands undestarstands this + return "default" + else: + return data.osname + + +def _get_verification_enabled(data): + """Find out if source has enabled verification. + + OSTree sources has different names for enabled verification. This helper function + will make the access consistent. + + :param data: OSTree source structure + :return bool: True if verification is enabled + """ + if data.is_container(): + return data.signature_verification_enabled + else: + return data.gpg_verification_enabled + + class PrepareOSTreeMountTargetsTask(Task): """Task to prepare OSTree mount targets.""" @@ -119,7 +173,10 @@ def _handle_var_mount_point(self, existing_mount_points): :param [] existing_mount_points: a list of existing mount points """ - var_root = '/ostree/deploy/' + self._source_config.osname + '/var' + # osname was used for ostreesetup but ostreecontainer renamed it to stateroot + stateroot = _get_stateroot(self._source_config) + + var_root = '/ostree/deploy/' + stateroot + '/var' if existing_mount_points.get("/var") is None: self._setup_internal_bindmount(var_root, dest='/var', recurse=False) else: @@ -325,7 +382,7 @@ def run(self): remote_options = {} - if not self._data.gpg_verification_enabled: + if not _get_verification_enabled(self._data): remote_options['gpg-verify'] = Variant('b', False) if not conf.payload.verify_ssl: @@ -336,9 +393,12 @@ def run(self): else: root = None + # Remote is set or it should be named as stateroot is + remote = self._data.remote or _get_stateroot(self._data) + repo.remote_change(root, OSTree.RepoRemoteChange.ADD_IF_NOT_EXISTS, - self._data.remote, + remote, self._data.url, Variant('a{sv}', remote_options), cancellable) @@ -413,7 +473,8 @@ def name(self): def run(self): # Variable substitute the ref: https://pagure.io/atomic-wg/issue/299 - ref = RpmOstree.varsubst_basearch(self._data.ref) + ref = _get_ref(self._data) + stateroot = _get_stateroot(self._data) self.report_progress(_("Deployment starting: {}").format(ref)) @@ -422,21 +483,39 @@ def run(self): ["admin", "--sysroot=" + self._sysroot, "os-init", - self._data.osname] + stateroot] ) - log.info("ostree admin deploy starting") + if self._data.is_container(): + log.info("ostree image deploy starting") - safe_exec_with_redirect( - "ostree", - ["admin", - "--sysroot=" + self._sysroot, - "deploy", - "--os=" + self._data.osname, - self._data.remote + ':' + ref] - ) + args = ["container", "image", "deploy", + "--sysroot=" + self._sysroot, + "--image=" + ref] + + if self._data.transport: + args.append("--transport=" + self._data.transport) + if self._data.stateroot: + args.append("--stateroot=" + self._data.stateroot) + if not self._data.signature_verification_enabled: + args.append("--no-signature-verification") + + safe_exec_with_redirect( + "ostree", + args + ) + else: + log.info("ostree admin deploy starting") + safe_exec_with_redirect( + "ostree", + ["admin", + "--sysroot=" + self._sysroot, + "deploy", + "--os=" + stateroot, + self._data.remote + ':' + ref] + ) - log.info("ostree admin deploy complete") + log.info("ostree deploy complete") self.report_progress(_("Deployment complete: {}").format(ref)) diff --git a/pyanaconda/modules/payloads/payload/rpm_ostree/rpm_ostree.py b/pyanaconda/modules/payloads/payload/rpm_ostree/rpm_ostree.py index 5d4758ccf82d..024753d42151 100644 --- a/pyanaconda/modules/payloads/payload/rpm_ostree/rpm_ostree.py +++ b/pyanaconda/modules/payloads/payload/rpm_ostree/rpm_ostree.py @@ -45,7 +45,8 @@ def type(self): def supported_source_types(self): """Get list of sources supported by the RPM OSTree module.""" return [ - SourceType.RPM_OSTREE + SourceType.RPM_OSTREE, + SourceType.RPM_OSTREE_CONTAINER, ] def process_kickstart(self, data): diff --git a/pyanaconda/modules/payloads/source/factory.py b/pyanaconda/modules/payloads/source/factory.py index 2b24b3f330b2..898f75eb55cf 100644 --- a/pyanaconda/modules/payloads/source/factory.py +++ b/pyanaconda/modules/payloads/source/factory.py @@ -108,6 +108,8 @@ def get_rpm_ostree_type_for_kickstart(ks_data): :param ks_data: kickstart data from DNF payload :return: SourceType value """ + if ks_data.ostreecontainer.seen: + return SourceType.RPM_OSTREE_CONTAINER if ks_data.ostreesetup.seen: return SourceType.RPM_OSTREE diff --git a/pyanaconda/payload/rpmostreepayload.py b/pyanaconda/payload/rpmostreepayload.py index f2fe7ee15376..b929396f3edf 100644 --- a/pyanaconda/payload/rpmostreepayload.py +++ b/pyanaconda/payload/rpmostreepayload.py @@ -20,8 +20,10 @@ from subprocess import CalledProcessError -from pyanaconda.core.constants import PAYLOAD_TYPE_RPM_OSTREE, SOURCE_TYPE_RPM_OSTREE -from pyanaconda.modules.common.structures.rpm_ostree import RPMOSTreeConfigurationData +from pyanaconda.core.constants import PAYLOAD_TYPE_RPM_OSTREE, SOURCE_TYPE_RPM_OSTREE, \ + SOURCE_TYPE_RPM_OSTREE_CONTAINER +from pyanaconda.modules.common.structures.rpm_ostree import RPMOSTreeConfigurationData, \ + RPMOSTreeContainerConfigurationData from pyanaconda.progress import progressQ from pyanaconda.payload.base import Payload from pyanaconda.payload import utils as payload_utils @@ -62,13 +64,18 @@ def source_type(self): def _get_source_configuration(self): """Get the configuration of the RPM OSTree source. - :return: an instance of RPMOSTreeConfigurationData + :return: an instance of RPMOSTreeConfigurationData or RPMOSTreeContainerConfigurationData """ source_proxy = self.get_source_proxy() - return RPMOSTreeConfigurationData.from_structure( - source_proxy.Configuration - ) + if self.source_type == SOURCE_TYPE_RPM_OSTREE_CONTAINER: + return RPMOSTreeContainerConfigurationData.from_structure( + source_proxy.Configuration + ) + else: + return RPMOSTreeConfigurationData.from_structure( + source_proxy.Configuration + ) @property def kernel_version_list(self): @@ -124,11 +131,12 @@ def _install(self, data): ) task.run() - from pyanaconda.modules.payloads.payload.rpm_ostree.installation import \ - PullRemoteAndDeleteTask - task = PullRemoteAndDeleteTask(data) - task.progress_changed_signal.connect(self._progress_cb) - task.run() + if not data.is_container(): + from pyanaconda.modules.payloads.payload.rpm_ostree.installation import \ + PullRemoteAndDeleteTask + task = PullRemoteAndDeleteTask(data) + task.progress_changed_signal.connect(self._progress_cb) + task.run() from pyanaconda.modules.payloads.payload.rpm_ostree.installation import DeployOSTreeTask task = DeployOSTreeTask(data, conf.target.physical_root) diff --git a/tests/unit_tests/pyanaconda_tests/modules/payloads/payload/test_rpm_ostree.py b/tests/unit_tests/pyanaconda_tests/modules/payloads/payload/test_rpm_ostree.py index 7c4704187d2a..28a759e847e5 100644 --- a/tests/unit_tests/pyanaconda_tests/modules/payloads/payload/test_rpm_ostree.py +++ b/tests/unit_tests/pyanaconda_tests/modules/payloads/payload/test_rpm_ostree.py @@ -17,7 +17,7 @@ # import unittest -from pyanaconda.core.constants import SOURCE_TYPE_RPM_OSTREE +from pyanaconda.core.constants import SOURCE_TYPE_RPM_OSTREE, SOURCE_TYPE_RPM_OSTREE_CONTAINER from pyanaconda.modules.payloads.constants import PayloadType from pyanaconda.modules.payloads.payload.rpm_ostree.rpm_ostree import RPMOSTreeModule from pyanaconda.modules.payloads.payload.rpm_ostree.rpm_ostree_interface import RPMOSTreeInterface @@ -47,7 +47,8 @@ def test_type(self): def test_supported_sources(self): """Test the SupportedSourceTypes property.""" assert self.interface.SupportedSourceTypes == [ - SOURCE_TYPE_RPM_OSTREE + SOURCE_TYPE_RPM_OSTREE, + SOURCE_TYPE_RPM_OSTREE_CONTAINER, ]