167 lines
6.7 KiB
Diff
167 lines
6.7 KiB
Diff
From c0f2329c2ed71f373aad26c7f1786494f6e75b76 Mon Sep 17 00:00:00 2001
|
|
From: Evan Goode <mail@evangoo.de>
|
|
Date: Wed, 15 Jan 2025 21:43:58 +0000
|
|
Subject: [PATCH 14/17] bootc: Use ostree GObject API to get deployment status
|
|
MIME-Version: 1.0
|
|
Content-Type: text/plain; charset=UTF-8
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
Upstream commit: f3abee56452e40ce475f714666c3a16426759c96
|
|
|
|
Using libostree gives us more detail about the current state of the
|
|
deployment than only checking whether /usr is writable.
|
|
|
|
Resolves: https://issues.redhat.com/browse/RHEL-76849
|
|
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
|
---
|
|
dnf/cli/cli.py | 13 ++++----
|
|
dnf/util.py | 80 +++++++++++++++++++++++++++++++++++---------------
|
|
2 files changed, 65 insertions(+), 28 deletions(-)
|
|
|
|
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
|
index 53f2d9d50..bc11505fc 100644
|
|
--- a/dnf/cli/cli.py
|
|
+++ b/dnf/cli/cli.py
|
|
@@ -218,18 +218,22 @@ class BaseCli(dnf.Base):
|
|
logger.info(_("{prog} will only download packages, install gpg keys, and check the "
|
|
"transaction.").format(prog=dnf.util.MAIN_PROG_UPPER))
|
|
|
|
- is_bootc_transaction = dnf.util._is_bootc_host() and \
|
|
+ is_bootc_transaction = dnf.util._Bootc.is_bootc_host() and \
|
|
os.path.realpath(self.conf.installroot) == "/" and \
|
|
not self.conf.downloadonly
|
|
|
|
# Handle bootc transactions. `--transient` must be specified if
|
|
# /usr is not already writeable.
|
|
+ bootc = None
|
|
if is_bootc_transaction:
|
|
if self.conf.persistence == "persist":
|
|
logger.info(_("Persistent transactions aren't supported on bootc systems."))
|
|
raise CliError(_("Operation aborted."))
|
|
assert self.conf.persistence in ("auto", "transient")
|
|
- if not dnf.util._is_bootc_unlocked():
|
|
+
|
|
+ bootc = dnf.util._Bootc()
|
|
+
|
|
+ if not bootc.is_unlocked():
|
|
if self.conf.persistence == "auto":
|
|
logger.info(_("This bootc system is configured to be read-only. Pass --transient to "
|
|
"perform this and subsequent transactions in a transient overlay which "
|
|
@@ -239,7 +243,6 @@ class BaseCli(dnf.Base):
|
|
logger.info(_("A transient overlay will be created on /usr that will be discarded on reboot. "
|
|
"Keep in mind that changes to /etc and /var will still persist, and packages "
|
|
"commonly modify these directories."))
|
|
- bootc_unlock_requested = True
|
|
elif self.conf.persistence == "transient":
|
|
raise CliError(_("Transient transactions are only supported on bootc systems."))
|
|
|
|
@@ -247,8 +250,8 @@ class BaseCli(dnf.Base):
|
|
if self.conf.assumeno or not self.output.userconfirm():
|
|
raise CliError(_("Operation aborted."))
|
|
|
|
- if bootc_unlock_requested:
|
|
- dnf.util._bootc_unlock()
|
|
+ if bootc:
|
|
+ bootc.unlock_and_prepare()
|
|
else:
|
|
logger.info(_('Nothing to do.'))
|
|
return
|
|
diff --git a/dnf/util.py b/dnf/util.py
|
|
index 2e270890c..51f853d8b 100644
|
|
--- a/dnf/util.py
|
|
+++ b/dnf/util.py
|
|
@@ -642,33 +642,67 @@ def _is_file_pattern_present(specs):
|
|
return False
|
|
|
|
|
|
-def _is_bootc_host():
|
|
- """Returns true is the system is managed as an immutable container, false
|
|
- otherwise."""
|
|
- ostree_booted = "/run/ostree-booted"
|
|
- return os.path.isfile(ostree_booted)
|
|
+class _Bootc:
|
|
+ usr = "/usr"
|
|
|
|
+ def __init__(self):
|
|
+ if not self.is_bootc_host():
|
|
+ raise RuntimeError(_("Not running on a bootc system."))
|
|
|
|
-def _is_bootc_unlocked():
|
|
- """Check whether /usr is writeable, e.g. if we are in a normal mutable
|
|
- system or if we are in a bootc after `bootc usr-overlay` or `ostree admin
|
|
- unlock` was run."""
|
|
- usr = "/usr"
|
|
- return os.access(usr, os.W_OK)
|
|
+ import gi
|
|
+ self._gi = gi
|
|
|
|
+ gi.require_version("OSTree", "1.0")
|
|
+ from gi.repository import OSTree
|
|
|
|
-def _bootc_unlock():
|
|
- """Set up a writeable overlay on bootc systems."""
|
|
+ self._OSTree = OSTree
|
|
|
|
- if _is_bootc_unlocked():
|
|
- return
|
|
+ self._sysroot = self._OSTree.Sysroot.new_default()
|
|
+ assert self._sysroot.load(None)
|
|
|
|
- unlock_command = ["bootc", "usr-overlay"]
|
|
+ self._booted_deployment = self._sysroot.require_booted_deployment()
|
|
+ assert self._booted_deployment is not None
|
|
|
|
- try:
|
|
- completed_process = subprocess.run(unlock_command, text=True)
|
|
- completed_process.check_returncode()
|
|
- except FileNotFoundError:
|
|
- raise dnf.exceptions.Error(_("bootc command not found. Is this a bootc system?"))
|
|
- except subprocess.CalledProcessError:
|
|
- raise dnf.exceptions.Error(_("Failed to unlock system: %s", completed_process.stderr))
|
|
+ @staticmethod
|
|
+ def is_bootc_host():
|
|
+ """Returns true is the system is managed as an immutable container, false
|
|
+ otherwise."""
|
|
+ ostree_booted = "/run/ostree-booted"
|
|
+ return os.path.isfile(ostree_booted)
|
|
+
|
|
+ def _get_unlocked_state(self):
|
|
+ return self._booted_deployment.get_unlocked()
|
|
+
|
|
+ def is_unlocked(self):
|
|
+ return self._get_unlocked_state() != self._OSTree.DeploymentUnlockedState.NONE
|
|
+
|
|
+ def unlock_and_prepare(self):
|
|
+ """Set up a writeable overlay on bootc systems."""
|
|
+
|
|
+ bootc_unlocked_state = self._get_unlocked_state()
|
|
+
|
|
+ valid_bootc_unlocked_states = (
|
|
+ self._OSTree.DeploymentUnlockedState.NONE,
|
|
+ self._OSTree.DeploymentUnlockedState.DEVELOPMENT,
|
|
+ # self._OSTree.DeploymentUnlockedState.TRANSIENT,
|
|
+ # self._OSTree.DeploymentUnlockedState.HOTFIX,
|
|
+ )
|
|
+
|
|
+ if bootc_unlocked_state not in valid_bootc_unlocked_states:
|
|
+ raise ValueError(_("Unhandled bootc unlocked state: %s") % bootc_unlocked_state.value_nick)
|
|
+
|
|
+ if bootc_unlocked_state == self._OSTree.DeploymentUnlockedState.DEVELOPMENT:
|
|
+ # System is already unlocked.
|
|
+ pass
|
|
+ elif bootc_unlocked_state == self._OSTree.DeploymentUnlockedState.NONE:
|
|
+ unlock_command = ["ostree", "admin", "unlock"]
|
|
+
|
|
+ try:
|
|
+ completed_process = subprocess.run(unlock_command, text=True)
|
|
+ completed_process.check_returncode()
|
|
+ except FileNotFoundError:
|
|
+ raise dnf.exceptions.Error(_("ostree command not found. Is this a bootc system?"))
|
|
+ except subprocess.CalledProcessError:
|
|
+ raise dnf.exceptions.Error(_("Failed to unlock system: %s", completed_process.stderr))
|
|
+
|
|
+ assert os.access(self.usr, os.W_OK)
|
|
--
|
|
2.48.1
|
|
|