import CS dnf-4.14.0-25.el9
This commit is contained in:
parent
cc2d726add
commit
82265e91f1
@ -0,0 +1,99 @@
|
||||
From 6af6633b3e1e92405ec63ef4d3d697891213f8cb Mon Sep 17 00:00:00 2001
|
||||
From: David Cantrell <dcantrell@redhat.com>
|
||||
Date: Thu, 15 Feb 2024 14:03:32 -0500
|
||||
Subject: [PATCH 1/4] Add detection for ostree-based systems and warn users
|
||||
about losing changes
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Upstream commit: 5c050ba2324c5fb95bf0e0501c7925f38f6a09dc
|
||||
|
||||
On ostree-based systems, users can use dnf to customize the
|
||||
environment but those changes will be lost at the next ostree-based
|
||||
image update. If you want to retain changes between ostree-updates
|
||||
you need to make use of rpm-ostree right now.
|
||||
|
||||
Signed-off-by: David Cantrell <dcantrell@redhat.com>
|
||||
Resolves: https://issues.redhat.com/browse/RHEL-49670
|
||||
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||
---
|
||||
dnf/cli/cli.py | 9 +++++++++
|
||||
dnf/util.py | 31 +++++++++++++++++++++++++++++++
|
||||
2 files changed, 40 insertions(+)
|
||||
|
||||
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||
index 0c4f4c6ad..1fd0e96c3 100644
|
||||
--- a/dnf/cli/cli.py
|
||||
+++ b/dnf/cli/cli.py
|
||||
@@ -214,6 +214,15 @@ class BaseCli(dnf.Base):
|
||||
elif 'test' in self.conf.tsflags:
|
||||
logger.info(_("{prog} will only download packages, install gpg keys, and check the "
|
||||
"transaction.").format(prog=dnf.util.MAIN_PROG_UPPER))
|
||||
+ if dnf.util.is_container():
|
||||
+ _container_msg = _("""
|
||||
+*** This system is managed with ostree. Changes to the system
|
||||
+*** made with dnf will be lost with the next ostree-based update.
|
||||
+*** If you do not want to lose these changes, use 'rpm-ostree'.
|
||||
+""")
|
||||
+ logger.info(_container_msg)
|
||||
+ raise CliError(_("Operation aborted."))
|
||||
+
|
||||
if self._promptWanted():
|
||||
if self.conf.assumeno or not self.output.userconfirm():
|
||||
raise CliError(_("Operation aborted."))
|
||||
diff --git a/dnf/util.py b/dnf/util.py
|
||||
index 16c5bc89c..9909f8fea 100644
|
||||
--- a/dnf/util.py
|
||||
+++ b/dnf/util.py
|
||||
@@ -33,11 +33,13 @@ import errno
|
||||
import functools
|
||||
import hawkey
|
||||
import itertools
|
||||
+import json
|
||||
import locale
|
||||
import logging
|
||||
import os
|
||||
import pwd
|
||||
import shutil
|
||||
+import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
@@ -631,3 +633,32 @@ def _post_transaction_output(base, transaction, action_callback):
|
||||
def _name_unset_wrapper(input_name):
|
||||
# returns <name-unset> for everything that evaluates to False (None, empty..)
|
||||
return input_name if input_name else _("<name-unset>")
|
||||
+
|
||||
+
|
||||
+def is_container():
|
||||
+ """Returns true is the system is managed as an immutable container,
|
||||
+ false otherwise. If msg is True, a warning message is displayed
|
||||
+ for the user.
|
||||
+ """
|
||||
+
|
||||
+ bootc = '/usr/bin/bootc'
|
||||
+ ostree = '/sysroot/ostree'
|
||||
+
|
||||
+ if os.path.isfile(bootc) and os.access(bootc, os.X_OK):
|
||||
+ p = subprocess.Popen([bootc, "status", "--json"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
+ (out, err) = p.communicate()
|
||||
+
|
||||
+ if p.returncode == 0:
|
||||
+ # check the output of 'bootc status'
|
||||
+ j = json.loads(out)
|
||||
+
|
||||
+ # XXX: the API from bootc status is evolving
|
||||
+ status = j.get("status", "")
|
||||
+ kind = j.get("kind", "")
|
||||
+
|
||||
+ if kind.lower() == "bootchost" and bool(status.get("isContainer", None)):
|
||||
+ return True
|
||||
+ elif os.path.isdir(ostree):
|
||||
+ return True
|
||||
+
|
||||
+ return False
|
||||
\ No newline at end of file
|
||||
--
|
||||
2.46.2
|
||||
|
107
SOURCES/0028-Update-ostree-bootc-host-system-check.patch
Normal file
107
SOURCES/0028-Update-ostree-bootc-host-system-check.patch
Normal file
@ -0,0 +1,107 @@
|
||||
From 6157248a5035a7752115143d8d29773384c1db3f Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Marrero <jmarrero@redhat.com>
|
||||
Date: Tue, 16 Jul 2024 15:48:41 -0400
|
||||
Subject: [PATCH 2/4] Update ostree/bootc host system check.
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Upstream commit: 6120fe52511775b60b6031d4169988c025610ab5
|
||||
|
||||
This changes the is_container() func for _is_bootc_host()
|
||||
and updates the logic and message. This should detect on
|
||||
all ostree and bootc hosts to date that are not using
|
||||
bootc usroverlay or ostree admin unlock for development
|
||||
purposes.
|
||||
|
||||
Resolves: https://issues.redhat.com/browse/RHEL-49670
|
||||
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||
---
|
||||
dnf/cli/cli.py | 11 +++++------
|
||||
dnf/util.py | 33 ++++++++-------------------------
|
||||
2 files changed, 13 insertions(+), 31 deletions(-)
|
||||
|
||||
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||
index 1fd0e96c3..8521dd351 100644
|
||||
--- a/dnf/cli/cli.py
|
||||
+++ b/dnf/cli/cli.py
|
||||
@@ -214,13 +214,12 @@ class BaseCli(dnf.Base):
|
||||
elif 'test' in self.conf.tsflags:
|
||||
logger.info(_("{prog} will only download packages, install gpg keys, and check the "
|
||||
"transaction.").format(prog=dnf.util.MAIN_PROG_UPPER))
|
||||
- if dnf.util.is_container():
|
||||
- _container_msg = _("""
|
||||
-*** This system is managed with ostree. Changes to the system
|
||||
-*** made with dnf will be lost with the next ostree-based update.
|
||||
-*** If you do not want to lose these changes, use 'rpm-ostree'.
|
||||
+ if dnf.util._is_bootc_host():
|
||||
+ _bootc_host_msg = _("""
|
||||
+*** Error: system is configured to be read-only; for more
|
||||
+*** information run `bootc status` or `ostree admin status`.
|
||||
""")
|
||||
- logger.info(_container_msg)
|
||||
+ logger.info(_bootc_host_msg)
|
||||
raise CliError(_("Operation aborted."))
|
||||
|
||||
if self._promptWanted():
|
||||
diff --git a/dnf/util.py b/dnf/util.py
|
||||
index 9909f8fea..e8f587a03 100644
|
||||
--- a/dnf/util.py
|
||||
+++ b/dnf/util.py
|
||||
@@ -33,13 +33,11 @@ import errno
|
||||
import functools
|
||||
import hawkey
|
||||
import itertools
|
||||
-import json
|
||||
import locale
|
||||
import logging
|
||||
import os
|
||||
import pwd
|
||||
import shutil
|
||||
-import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
@@ -635,30 +633,15 @@ def _name_unset_wrapper(input_name):
|
||||
return input_name if input_name else _("<name-unset>")
|
||||
|
||||
|
||||
-def is_container():
|
||||
+def _is_bootc_host():
|
||||
"""Returns true is the system is managed as an immutable container,
|
||||
false otherwise. If msg is True, a warning message is displayed
|
||||
for the user.
|
||||
"""
|
||||
-
|
||||
- bootc = '/usr/bin/bootc'
|
||||
- ostree = '/sysroot/ostree'
|
||||
-
|
||||
- if os.path.isfile(bootc) and os.access(bootc, os.X_OK):
|
||||
- p = subprocess.Popen([bootc, "status", "--json"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
- (out, err) = p.communicate()
|
||||
-
|
||||
- if p.returncode == 0:
|
||||
- # check the output of 'bootc status'
|
||||
- j = json.loads(out)
|
||||
-
|
||||
- # XXX: the API from bootc status is evolving
|
||||
- status = j.get("status", "")
|
||||
- kind = j.get("kind", "")
|
||||
-
|
||||
- if kind.lower() == "bootchost" and bool(status.get("isContainer", None)):
|
||||
- return True
|
||||
- elif os.path.isdir(ostree):
|
||||
- return True
|
||||
-
|
||||
- return False
|
||||
\ No newline at end of file
|
||||
+ ostree_booted = '/run/ostree-booted'
|
||||
+ usr = '/usr/'
|
||||
+ # Check if usr is writtable and we are in a running ostree system.
|
||||
+ # We want this code to return true only when the system is in locked state. If someone ran
|
||||
+ # bootc overlay or ostree admin unlock we would want normal DNF path to be ran as it will be
|
||||
+ # temporary changes (until reboot).
|
||||
+ return os.path.isfile(ostree_booted) and not os.access(usr, os.W_OK)
|
||||
--
|
||||
2.46.2
|
||||
|
@ -0,0 +1,32 @@
|
||||
From 0bab6b9620f9872271c9458212538eb03b8b085c Mon Sep 17 00:00:00 2001
|
||||
From: Joseph Marrero <jmarrero@redhat.com>
|
||||
Date: Mon, 22 Jul 2024 15:33:32 -0400
|
||||
Subject: [PATCH 3/4] Update bootc hosts message to point to bootc --help
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Upstream commit: e2535589ce16bc36b96b37369502a3c312f6056a
|
||||
Resolves: https://issues.redhat.com/browse/RHEL-49670
|
||||
|
||||
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||
---
|
||||
dnf/cli/cli.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||
index 8521dd351..99af9069b 100644
|
||||
--- a/dnf/cli/cli.py
|
||||
+++ b/dnf/cli/cli.py
|
||||
@@ -217,7 +217,7 @@ class BaseCli(dnf.Base):
|
||||
if dnf.util._is_bootc_host():
|
||||
_bootc_host_msg = _("""
|
||||
*** Error: system is configured to be read-only; for more
|
||||
-*** information run `bootc status` or `ostree admin status`.
|
||||
+*** information run `bootc --help`.
|
||||
""")
|
||||
logger.info(_bootc_host_msg)
|
||||
raise CliError(_("Operation aborted."))
|
||||
--
|
||||
2.46.2
|
||||
|
@ -0,0 +1,47 @@
|
||||
From ca4c52214b16fe298c44939cd7aaccd9b7549869 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= <ppisar@redhat.com>
|
||||
Date: Thu, 15 Aug 2024 14:04:55 +0200
|
||||
Subject: [PATCH 4/4] Allow --installroot on read-only bootc system
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Upstream commit: a1aa8d0e048751859a2bec1b2fb12fcca93c6e83
|
||||
|
||||
Some people use --installroot on a read-only bootc system to install
|
||||
a system into a chroot subtree. However, current bootc check did not
|
||||
take into account --installroot and rejected the operation.
|
||||
|
||||
This patch augments the check for the installroot being different
|
||||
from /.
|
||||
|
||||
It's pointless to check for installroot writability here because
|
||||
installroot is written before this check when updating the
|
||||
repositories and computing a transaction. Moving this check sooner
|
||||
would not help because some directories (/opt, /) are kept read-only
|
||||
even on writable bootc.
|
||||
|
||||
Resolves: #2108
|
||||
Resolves: https://issues.redhat.com/browse/RHEL-49670
|
||||
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||
---
|
||||
dnf/cli/cli.py | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||
index 99af9069b..36cfa74b5 100644
|
||||
--- a/dnf/cli/cli.py
|
||||
+++ b/dnf/cli/cli.py
|
||||
@@ -214,7 +214,8 @@ class BaseCli(dnf.Base):
|
||||
elif 'test' in self.conf.tsflags:
|
||||
logger.info(_("{prog} will only download packages, install gpg keys, and check the "
|
||||
"transaction.").format(prog=dnf.util.MAIN_PROG_UPPER))
|
||||
- if dnf.util._is_bootc_host():
|
||||
+ if dnf.util._is_bootc_host() and \
|
||||
+ os.path.realpath(self.conf.installroot) == "/":
|
||||
_bootc_host_msg = _("""
|
||||
*** Error: system is configured to be read-only; for more
|
||||
*** information run `bootc --help`.
|
||||
--
|
||||
2.46.2
|
||||
|
33
SOURCES/0031-smtplib-catch-OSError-not-SMTPException.patch
Normal file
33
SOURCES/0031-smtplib-catch-OSError-not-SMTPException.patch
Normal file
@ -0,0 +1,33 @@
|
||||
From ea2d17cc484c7c49686145f4b2e98e4b73b9c967 Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Mon, 13 Mar 2023 14:50:41 -0400
|
||||
Subject: [PATCH] smtplib: catch OSError, not SMTPException
|
||||
|
||||
Some, but not all, types of connection error are caught by smtplib and
|
||||
reraised as an smtplib.SMTPException. Notably, TimeoutError,
|
||||
socket.gaierror (name resolution failure), and ConnectionRefusedError
|
||||
and are not caught.
|
||||
|
||||
The more generic OSError should be caught here instead.
|
||||
|
||||
Resolves #1905
|
||||
---
|
||||
dnf/automatic/emitter.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dnf/automatic/emitter.py b/dnf/automatic/emitter.py
|
||||
index 4aea4b02..648f1a1d 100644
|
||||
--- a/dnf/automatic/emitter.py
|
||||
+++ b/dnf/automatic/emitter.py
|
||||
@@ -106,7 +106,7 @@ class EmailEmitter(Emitter):
|
||||
smtp = smtplib.SMTP(self._conf.email_host, timeout=300)
|
||||
smtp.sendmail(email_from, email_to, message.as_string())
|
||||
smtp.close()
|
||||
- except smtplib.SMTPException as exc:
|
||||
+ except OSError as exc:
|
||||
msg = _("Failed to send an email via '%s': %s") % (
|
||||
self._conf.email_host, exc)
|
||||
logger.error(msg)
|
||||
--
|
||||
2.46.1
|
||||
|
@ -0,0 +1,40 @@
|
||||
From fdeb208b7b6522bd458142867683b1bf68cd355d Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Petr=20P=C3=ADsa=C5=99?= <ppisar@redhat.com>
|
||||
Date: Thu, 10 Oct 2024 10:57:48 +0200
|
||||
Subject: [PATCH] Allow --downloadonly on read-only bootc system
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Upstream commit: 8d888d26e6da27ba37243d7504eb42472f389bde
|
||||
|
||||
"dnf install --downloadonly" failed on read-only bootc system despite
|
||||
not running the transaction. The downloaded packages are stored under
|
||||
writable /var or to a directory explicitly choosen by a user.
|
||||
|
||||
This patch suppresses the bootc read-only bailout if --downloadonly
|
||||
option is used.
|
||||
|
||||
https://issues.redhat.com/browse/RHEL-61745
|
||||
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||
---
|
||||
dnf/cli/cli.py | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||
index 36cfa74b5..3dc08d616 100644
|
||||
--- a/dnf/cli/cli.py
|
||||
+++ b/dnf/cli/cli.py
|
||||
@@ -215,7 +215,8 @@ 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))
|
||||
if dnf.util._is_bootc_host() and \
|
||||
- os.path.realpath(self.conf.installroot) == "/":
|
||||
+ os.path.realpath(self.conf.installroot) == "/" and \
|
||||
+ not self.conf.downloadonly:
|
||||
_bootc_host_msg = _("""
|
||||
*** Error: system is configured to be read-only; for more
|
||||
*** information run `bootc --help`.
|
||||
--
|
||||
2.47.0
|
||||
|
@ -0,0 +1,81 @@
|
||||
From d8bd8174426a2d053b20acc7c2bcd83a572fc1d5 Mon Sep 17 00:00:00 2001
|
||||
From: Marek Blaha <mblaha@redhat.com>
|
||||
Date: Thu, 17 Oct 2024 13:30:21 +0200
|
||||
Subject: [PATCH] automatic: Check availability of config file
|
||||
|
||||
Upstream commit: 13ecc3921fb1566c2af7b80d5cb515d8fc4e5ec9
|
||||
RHEL issue: https://issues.redhat.com/browse/RHEL-49743
|
||||
|
||||
If a configuration file is explicitly specified on the command line,
|
||||
ensure that it exists and is readable. If the file is not found, notify
|
||||
the user immediately and terminate the process.
|
||||
|
||||
This resolves issues where users may run dnf-automatic with unrecognized
|
||||
positional arguments, such as `dnf-automatic install`.
|
||||
|
||||
The most natural approach to handle a non-existing config file would be
|
||||
by catching the exception thrown by the `read()` method of the
|
||||
`libdnf.conf.ConfigParser` class. Unfortunately, the Python bindings
|
||||
override the `read()` method at the SWIG level, causing it to suppress any
|
||||
potentially raised IOError.
|
||||
For details see this section of the commit
|
||||
https://github.com/rpm-software-management/libdnf/commit/8f1fedf8551b72d6bc24018f5980714b3a103aeb
|
||||
|
||||
def ConfigParser__newRead(self, filenames):
|
||||
parsedFNames = []
|
||||
try:
|
||||
if isinstance(filenames, str) or isinstance(filenames, unicode):
|
||||
filenames = [filenames]
|
||||
except NameError:
|
||||
pass
|
||||
for fname in filenames:
|
||||
try:
|
||||
self.readFileName(fname)
|
||||
parsedFNames.append(fname)
|
||||
except IOError:
|
||||
pass
|
||||
except Exception as e:
|
||||
raise RuntimeError("Parsing file '%s' failed: %s" % (fname, str(e)))
|
||||
return parsedFNames
|
||||
ConfigParser.read = ConfigParser__newRead
|
||||
|
||||
Resolves: https://issues.redhat.com/browse/RHEL-46030
|
||||
---
|
||||
dnf/automatic/main.py | 14 ++++++++++++--
|
||||
1 file changed, 12 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/dnf/automatic/main.py b/dnf/automatic/main.py
|
||||
index caef627f..bb0bd493 100644
|
||||
--- a/dnf/automatic/main.py
|
||||
+++ b/dnf/automatic/main.py
|
||||
@@ -74,7 +74,7 @@ def build_emitters(conf):
|
||||
|
||||
def parse_arguments(args):
|
||||
parser = argparse.ArgumentParser()
|
||||
- parser.add_argument('conf_path', nargs='?', default=dnf.const.CONF_AUTOMATIC_FILENAME)
|
||||
+ parser.add_argument('conf_path', nargs='?')
|
||||
parser.add_argument('--timer', action='store_true')
|
||||
parser.add_argument('--installupdates', dest='installupdates', action='store_true')
|
||||
parser.add_argument('--downloadupdates', dest='downloadupdates', action='store_true')
|
||||
@@ -89,7 +89,17 @@ def parse_arguments(args):
|
||||
class AutomaticConfig(object):
|
||||
def __init__(self, filename=None, downloadupdates=None,
|
||||
installupdates=None):
|
||||
- if not filename:
|
||||
+ if filename:
|
||||
+ # Specific config file was explicitely requested. Check that it exists
|
||||
+ # and is readable.
|
||||
+ if os.access(filename, os.F_OK):
|
||||
+ if not os.access(filename, os.R_OK):
|
||||
+ raise dnf.exceptions.Error(
|
||||
+ "Configuration file \"{}\" is not readable.".format(filename))
|
||||
+ else:
|
||||
+ raise dnf.exceptions.Error(
|
||||
+ "Configuration file \"{}\" not found.".format(filename))
|
||||
+ else:
|
||||
filename = dnf.const.CONF_AUTOMATIC_FILENAME
|
||||
self.commands = CommandsConfig()
|
||||
self.email = EmailConfig()
|
||||
--
|
||||
2.47.0
|
||||
|
247
SOURCES/0034-automatic-emitters-send-error-messages.patch
Normal file
247
SOURCES/0034-automatic-emitters-send-error-messages.patch
Normal file
@ -0,0 +1,247 @@
|
||||
From 130e44556d49cf9fa4a0468bb73b56182691ab86 Mon Sep 17 00:00:00 2001
|
||||
From: derickdiaz <derickdiaz123@outlook.com>
|
||||
Date: Sun, 15 Oct 2023 10:58:03 -0500
|
||||
Subject: [PATCH 1/4] Added feature to allow emitters to invoke on dnf error
|
||||
|
||||
---
|
||||
AUTHORS | 1 +
|
||||
dnf/automatic/emitter.py | 22 ++++++++++++++++++----
|
||||
dnf/automatic/main.py | 5 +++++
|
||||
3 files changed, 24 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/AUTHORS b/AUTHORS
|
||||
index 699a92c4..2e16c8ae 100644
|
||||
--- a/AUTHORS
|
||||
+++ b/AUTHORS
|
||||
@@ -68,6 +68,7 @@ DNF CONTRIBUTORS
|
||||
Christopher Meng <cickumqt@gmail.com>
|
||||
Daniel Mach <dmach@redhat.com>
|
||||
Dave Johansen <davejohansen@gmail.com>
|
||||
+ Derick Diaz <derickdiaz123@outlook.com>
|
||||
Dylan Pindur <dylanpindur@gmail.com>
|
||||
Eduard Cuba <ecuba@redhat.com>
|
||||
Evan Goode <egoode@redhat.com>
|
||||
diff --git a/dnf/automatic/emitter.py b/dnf/automatic/emitter.py
|
||||
index 648f1a1d..673da082 100644
|
||||
--- a/dnf/automatic/emitter.py
|
||||
+++ b/dnf/automatic/emitter.py
|
||||
@@ -33,6 +33,7 @@ APPLIED = _("The following updates have been applied on '%s':")
|
||||
APPLIED_TIMESTAMP = _("Updates completed at %s")
|
||||
AVAILABLE = _("The following updates are available on '%s':")
|
||||
DOWNLOADED = _("The following updates were downloaded on '%s':")
|
||||
+ERROR = _("An error has occured on: '%s'")
|
||||
|
||||
logger = logging.getLogger('dnf')
|
||||
|
||||
@@ -44,10 +45,15 @@ class Emitter(object):
|
||||
self._downloaded = False
|
||||
self._system_name = system_name
|
||||
self._trans_msg = None
|
||||
+ self._error = False
|
||||
+ self._error_msg = None
|
||||
|
||||
def _prepare_msg(self):
|
||||
msg = []
|
||||
- if self._applied:
|
||||
+ if self._error:
|
||||
+ msg.append(ERROR % self._system_name)
|
||||
+ msg.append(self._error_msg)
|
||||
+ elif self._applied:
|
||||
msg.append(APPLIED % self._system_name)
|
||||
msg.append(self._available_msg)
|
||||
msg.append(APPLIED_TIMESTAMP % time.strftime("%c"))
|
||||
@@ -72,6 +78,10 @@ class Emitter(object):
|
||||
assert self._available_msg
|
||||
self._downloaded = True
|
||||
|
||||
+ def notify_error(self, msg):
|
||||
+ self._error = True
|
||||
+ self._error_msg = msg
|
||||
+
|
||||
|
||||
class EmailEmitter(Emitter):
|
||||
def __init__(self, system_name, conf):
|
||||
@@ -79,7 +89,9 @@ class EmailEmitter(Emitter):
|
||||
self._conf = conf
|
||||
|
||||
def _prepare_msg(self):
|
||||
- if self._applied:
|
||||
+ if self._error:
|
||||
+ subj = _("An error has occured on '%s'.") % self._system_name
|
||||
+ elif self._applied:
|
||||
subj = _("Updates applied on '%s'.") % self._system_name
|
||||
elif self._downloaded:
|
||||
subj = _("Updates downloaded on '%s'.") % self._system_name
|
||||
@@ -95,6 +107,8 @@ class EmailEmitter(Emitter):
|
||||
message.set_charset('utf-8')
|
||||
email_from = self._conf.email_from
|
||||
email_to = self._conf.email_to
|
||||
+ email_host = self._conf.email_host
|
||||
+ email_port = self._conf.email_port
|
||||
message['Date'] = email.utils.formatdate()
|
||||
message['From'] = email_from
|
||||
message['Subject'] = subj
|
||||
@@ -103,12 +117,12 @@ class EmailEmitter(Emitter):
|
||||
|
||||
# Send the email
|
||||
try:
|
||||
- smtp = smtplib.SMTP(self._conf.email_host, timeout=300)
|
||||
+ smtp = smtplib.SMTP(email_host, email_port, timeout=300)
|
||||
smtp.sendmail(email_from, email_to, message.as_string())
|
||||
smtp.close()
|
||||
except OSError as exc:
|
||||
msg = _("Failed to send an email via '%s': %s") % (
|
||||
- self._conf.email_host, exc)
|
||||
+ email_host, exc)
|
||||
logger.error(msg)
|
||||
|
||||
|
||||
diff --git a/dnf/automatic/main.py b/dnf/automatic/main.py
|
||||
index bb0bd493..8aca210a 100644
|
||||
--- a/dnf/automatic/main.py
|
||||
+++ b/dnf/automatic/main.py
|
||||
@@ -314,6 +314,7 @@ def main(args):
|
||||
try:
|
||||
conf = AutomaticConfig(opts.conf_path, opts.downloadupdates,
|
||||
opts.installupdates)
|
||||
+ emitters = None
|
||||
with dnf.Base() as base:
|
||||
cli = dnf.cli.Cli(base)
|
||||
cli._read_conf_file()
|
||||
@@ -376,9 +377,13 @@ def main(args):
|
||||
exit_code = os.waitstatus_to_exitcode(os.system(conf.commands.reboot_command))
|
||||
if exit_code != 0:
|
||||
logger.error('Error: reboot command returned nonzero exit code: %d', exit_code)
|
||||
+ emitters.notify_error('Error: reboot command returned nonzero exit code: %d', exit_code)
|
||||
+ emitters.commit()
|
||||
return 1
|
||||
except dnf.exceptions.Error as exc:
|
||||
logger.error(_('Error: %s'), ucd(exc))
|
||||
+ emitters.notify_error(_('Error: %s') % str(exc))
|
||||
+ emitters.commit()
|
||||
return 1
|
||||
return 0
|
||||
|
||||
--
|
||||
2.47.1
|
||||
|
||||
|
||||
From 1d18e7c6c03d6de084a6845db3a2dc50f02c8e4c Mon Sep 17 00:00:00 2001
|
||||
From: derickdiaz <derickdiaz123@outlook.com>
|
||||
Date: Thu, 19 Oct 2023 04:58:45 -0500
|
||||
Subject: [PATCH 2/4] Checks if emitter is null incase build_emitters throws a
|
||||
ConfigError
|
||||
|
||||
---
|
||||
dnf/automatic/main.py | 5 +++--
|
||||
1 file changed, 3 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/dnf/automatic/main.py b/dnf/automatic/main.py
|
||||
index 8aca210a..04926346 100644
|
||||
--- a/dnf/automatic/main.py
|
||||
+++ b/dnf/automatic/main.py
|
||||
@@ -382,8 +382,9 @@ def main(args):
|
||||
return 1
|
||||
except dnf.exceptions.Error as exc:
|
||||
logger.error(_('Error: %s'), ucd(exc))
|
||||
- emitters.notify_error(_('Error: %s') % str(exc))
|
||||
- emitters.commit()
|
||||
+ if conf.emitters != None:
|
||||
+ emitters.notify_error(_('Error: %s') % str(exc))
|
||||
+ emitters.commit()
|
||||
return 1
|
||||
return 0
|
||||
|
||||
--
|
||||
2.47.1
|
||||
|
||||
|
||||
From 3e45752f0b74d2c4297da429a57b3e5148374195 Mon Sep 17 00:00:00 2001
|
||||
From: derickdiaz <derickdiaz123@outlook.com>
|
||||
Date: Thu, 19 Oct 2023 05:21:23 -0500
|
||||
Subject: [PATCH 3/4] Added 'send_error_messages' Boolean Option and updated
|
||||
man docs
|
||||
|
||||
Added option 'send_error_messages'
|
||||
|
||||
Fixed Option String List
|
||||
|
||||
Changed option to Boolean
|
||||
---
|
||||
dnf/automatic/main.py | 8 +++-----
|
||||
doc/automatic.rst | 5 +++++
|
||||
2 files changed, 8 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/dnf/automatic/main.py b/dnf/automatic/main.py
|
||||
index 04926346..0a9d5041 100644
|
||||
--- a/dnf/automatic/main.py
|
||||
+++ b/dnf/automatic/main.py
|
||||
@@ -239,6 +239,7 @@ class EmittersConfig(Config):
|
||||
libdnf.conf.VectorString(['email', 'stdio'])))
|
||||
self.add_option('output_width', libdnf.conf.OptionNumberInt32(80))
|
||||
self.add_option('system_name', libdnf.conf.OptionString(socket.gethostname()))
|
||||
+ self.add_option('send_error_messages', libdnf.conf.OptionBool(False))
|
||||
|
||||
|
||||
def gpgsigcheck(base, pkgs):
|
||||
@@ -376,13 +377,10 @@ def main(args):
|
||||
(conf.commands.reboot == 'when-needed' and base.reboot_needed())):
|
||||
exit_code = os.waitstatus_to_exitcode(os.system(conf.commands.reboot_command))
|
||||
if exit_code != 0:
|
||||
- logger.error('Error: reboot command returned nonzero exit code: %d', exit_code)
|
||||
- emitters.notify_error('Error: reboot command returned nonzero exit code: %d', exit_code)
|
||||
- emitters.commit()
|
||||
- return 1
|
||||
+ raise dnf.exceptions.Error('reboot command returned nonzero exit code: %d', exit_code)
|
||||
except dnf.exceptions.Error as exc:
|
||||
logger.error(_('Error: %s'), ucd(exc))
|
||||
- if conf.emitters != None:
|
||||
+ if conf.emitters.send_error_messages and emitters != None:
|
||||
emitters.notify_error(_('Error: %s') % str(exc))
|
||||
emitters.commit()
|
||||
return 1
|
||||
diff --git a/doc/automatic.rst b/doc/automatic.rst
|
||||
index 329c2f46..2d514b78 100644
|
||||
--- a/doc/automatic.rst
|
||||
+++ b/doc/automatic.rst
|
||||
@@ -120,6 +120,11 @@ Choosing how the results should be reported.
|
||||
|
||||
How the system is called in the reports.
|
||||
|
||||
+``send_error_messages``
|
||||
+ boolean, default: False
|
||||
+
|
||||
+ Invokes emitters when an errors occurs
|
||||
+
|
||||
---------------------
|
||||
``[command]`` section
|
||||
---------------------
|
||||
--
|
||||
2.47.1
|
||||
|
||||
|
||||
From 33d52a8072c47b74603cf38ec807c26657799578 Mon Sep 17 00:00:00 2001
|
||||
From: derickdiaz <derickdiaz123@outlook.com>
|
||||
Date: Fri, 27 Oct 2023 12:00:21 -0500
|
||||
Subject: [PATCH 4/4] Fixed Typo in docs
|
||||
|
||||
---
|
||||
doc/automatic.rst | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/doc/automatic.rst b/doc/automatic.rst
|
||||
index 2d514b78..4f8eec68 100644
|
||||
--- a/doc/automatic.rst
|
||||
+++ b/doc/automatic.rst
|
||||
@@ -123,7 +123,7 @@ Choosing how the results should be reported.
|
||||
``send_error_messages``
|
||||
boolean, default: False
|
||||
|
||||
- Invokes emitters when an errors occurs
|
||||
+ Invokes emitters when an error occurs.
|
||||
|
||||
---------------------
|
||||
``[command]`` section
|
||||
--
|
||||
2.47.1
|
||||
|
60
SOURCES/0035-automatic-Enhance-errors-reporting.patch
Normal file
60
SOURCES/0035-automatic-Enhance-errors-reporting.patch
Normal file
@ -0,0 +1,60 @@
|
||||
From 2a1046f4dbf855902056e463a9d35612e93f786e Mon Sep 17 00:00:00 2001
|
||||
From: Marek Blaha <mblaha@redhat.com>
|
||||
Date: Mon, 9 Dec 2024 13:42:44 +0100
|
||||
Subject: [PATCH] automatic: Enhance errors reporting
|
||||
|
||||
Emitters must be initialized early to ensure they can report errors that
|
||||
might occur in earlier stages of execution, before transaction
|
||||
resolution. Also a broader range of exceptions must be caught to
|
||||
ensure they are communicated to the user through the configured
|
||||
emitters.
|
||||
For example "No space left on the device" error can be raised during the
|
||||
fill_sack() call. This patch should report it via configured emitters.
|
||||
|
||||
Resolves: https://issues.redhat.com/browse/RHEL-61882
|
||||
---
|
||||
dnf/automatic/main.py | 9 +++++----
|
||||
1 file changed, 5 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/dnf/automatic/main.py b/dnf/automatic/main.py
|
||||
index 0a9d5041..5ca4ad39 100644
|
||||
--- a/dnf/automatic/main.py
|
||||
+++ b/dnf/automatic/main.py
|
||||
@@ -311,11 +311,13 @@ def wait_for_network(repos, timeout):
|
||||
|
||||
def main(args):
|
||||
(opts, parser) = parse_arguments(args)
|
||||
+ conf = None
|
||||
+ emitters = None
|
||||
|
||||
try:
|
||||
conf = AutomaticConfig(opts.conf_path, opts.downloadupdates,
|
||||
opts.installupdates)
|
||||
- emitters = None
|
||||
+ emitters = build_emitters(conf)
|
||||
with dnf.Base() as base:
|
||||
cli = dnf.cli.Cli(base)
|
||||
cli._read_conf_file()
|
||||
@@ -349,7 +351,6 @@ def main(args):
|
||||
return 0
|
||||
|
||||
lst = output.list_transaction(trans, total_width=80)
|
||||
- emitters = build_emitters(conf)
|
||||
emitters.notify_available(lst)
|
||||
if not conf.commands.download_updates:
|
||||
emitters.commit()
|
||||
@@ -378,9 +379,9 @@ def main(args):
|
||||
exit_code = os.waitstatus_to_exitcode(os.system(conf.commands.reboot_command))
|
||||
if exit_code != 0:
|
||||
raise dnf.exceptions.Error('reboot command returned nonzero exit code: %d', exit_code)
|
||||
- except dnf.exceptions.Error as exc:
|
||||
+ except Exception as exc:
|
||||
logger.error(_('Error: %s'), ucd(exc))
|
||||
- if conf.emitters.send_error_messages and emitters != None:
|
||||
+ if conf is not None and conf.emitters.send_error_messages and emitters is not None:
|
||||
emitters.notify_error(_('Error: %s') % str(exc))
|
||||
emitters.commit()
|
||||
return 1
|
||||
--
|
||||
2.47.1
|
||||
|
29
SOURCES/0036-Update-need_reboot-for-dnf-automatic.patch
Normal file
29
SOURCES/0036-Update-need_reboot-for-dnf-automatic.patch
Normal file
@ -0,0 +1,29 @@
|
||||
From ed14b8c8425c6fb6dbade3028ac0118086052b1b Mon Sep 17 00:00:00 2001
|
||||
From: Klaas Demter <Klaas-@users.noreply.github.com>
|
||||
Date: Tue, 15 Oct 2024 11:58:15 +0200
|
||||
Subject: [PATCH] Update need_reboot for dnf-automatic
|
||||
|
||||
The need_reboot from dnf-automatic did not match NEED_REBOOT from
|
||||
needs-restarting.
|
||||
---
|
||||
dnf/base.py | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/dnf/base.py b/dnf/base.py
|
||||
index 13222407..168207b5 100644
|
||||
--- a/dnf/base.py
|
||||
+++ b/dnf/base.py
|
||||
@@ -2832,8 +2832,8 @@ class Base(object):
|
||||
|
||||
# List taken from DNF needs-restarting
|
||||
need_reboot = frozenset(('kernel', 'kernel-rt', 'glibc',
|
||||
- 'linux-firmware', 'systemd', 'dbus',
|
||||
- 'dbus-broker', 'dbus-daemon'))
|
||||
+ 'linux-firmware', 'systemd', 'dbus',
|
||||
+ 'dbus-broker', 'dbus-daemon', 'microcode_ctl'))
|
||||
changed_pkgs = self.transaction.install_set | self.transaction.remove_set
|
||||
return any(pkg.name in need_reboot for pkg in changed_pkgs)
|
||||
|
||||
--
|
||||
2.47.1
|
||||
|
@ -0,0 +1,27 @@
|
||||
From c57c26e0cf8a3bba061f5ddf619c40f36a94aab9 Mon Sep 17 00:00:00 2001
|
||||
From: Marek Blaha <mblaha@redhat.com>
|
||||
Date: Thu, 12 Dec 2024 08:09:48 +0100
|
||||
Subject: [PATCH] automatic: Fix incorrect Error class instantiation
|
||||
|
||||
dnf.exceptions.Error class constructor accepts only one argument - error
|
||||
message.
|
||||
---
|
||||
dnf/automatic/main.py | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dnf/automatic/main.py b/dnf/automatic/main.py
|
||||
index 5ca4ad39..51057dfb 100644
|
||||
--- a/dnf/automatic/main.py
|
||||
+++ b/dnf/automatic/main.py
|
||||
@@ -378,7 +378,7 @@ def main(args):
|
||||
(conf.commands.reboot == 'when-needed' and base.reboot_needed())):
|
||||
exit_code = os.waitstatus_to_exitcode(os.system(conf.commands.reboot_command))
|
||||
if exit_code != 0:
|
||||
- raise dnf.exceptions.Error('reboot command returned nonzero exit code: %d', exit_code)
|
||||
+ raise dnf.exceptions.Error('reboot command returned nonzero exit code: %d' % exit_code)
|
||||
except Exception as exc:
|
||||
logger.error(_('Error: %s'), ucd(exc))
|
||||
if conf is not None and conf.emitters.send_error_messages and emitters is not None:
|
||||
--
|
||||
2.47.1
|
||||
|
@ -0,0 +1,39 @@
|
||||
From be3f218b6964116bc2948a9b7b93247322dc979f Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ale=C5=A1=20Mat=C4=9Bj?= <amatej@redhat.com>
|
||||
Date: Thu, 12 Dec 2024 12:59:03 +0100
|
||||
Subject: [PATCH] doc: `--disableexcludepkgs=all` doesn't affect just file
|
||||
configuration
|
||||
|
||||
The option `--disableexcludepkgs=all` disables all configuration
|
||||
includes and excludes including packages specified on the commandline
|
||||
via `--exclude=`, `-x=`, the deprecated `--excludepkgs=` and file
|
||||
configuration via `--excludepkgs=`, `--includepkgs=`.
|
||||
---
|
||||
doc/command_ref.rst | 9 +++++----
|
||||
1 file changed, 5 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/doc/command_ref.rst b/doc/command_ref.rst
|
||||
index 36817c00..75ded68c 100644
|
||||
--- a/doc/command_ref.rst
|
||||
+++ b/doc/command_ref.rst
|
||||
@@ -165,12 +165,13 @@ Options
|
||||
.. _disableexcludes-label:
|
||||
|
||||
``--disableexcludes=[all|main|<repoid>], --disableexcludepkgs=[all|main|<repoid>]``
|
||||
+ Disable ``excludepkgs`` and ``includepkgs`` configuration options. Takes one of the following three options:
|
||||
|
||||
- Disable the configuration file excludes. Takes one of the following three options:
|
||||
+ * ``all``, disables all ``excludepkgs`` and ``includepkgs`` configurations
|
||||
+ * ``main``, disables ``excludepkgs`` and ``includepkgs`` defined in the ``[main]`` section
|
||||
+ * ``repoid``, disables ``excludepkgs`` and ``includepkgs`` defined for the given repository
|
||||
|
||||
- * ``all``, disables all configuration file excludes
|
||||
- * ``main``, disables excludes defined in the ``[main]`` section
|
||||
- * ``repoid``, disables excludes defined for the given repository
|
||||
+ Note that the \-\ :ref:`-exclude <exclude_option-label>` option appends to the ``[main]`` ``excludepkgs`` configuration and therefore is disabled when ``main`` or ``all`` is specified.
|
||||
|
||||
``--disable, --set-disabled``
|
||||
Disable specified repositories (automatically saves). The option has to be used together with the
|
||||
--
|
||||
2.46.2
|
||||
|
181
SOURCES/0039-Add-support-for-transient.patch
Normal file
181
SOURCES/0039-Add-support-for-transient.patch
Normal file
@ -0,0 +1,181 @@
|
||||
From e236290f4aec12ad9b2e5cdfa48ec8e3172bb89c Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Thu, 7 Nov 2024 02:31:25 +0000
|
||||
Subject: [PATCH 39/44] Add support for --transient
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Upstream commit: 6091f3fccea988208ca417a504569947e4c99263
|
||||
|
||||
Adds support for the --transient option on all transactions. Passing
|
||||
--transient on a bootc system will call `bootc usr-overlay` to create a
|
||||
transient writeable /usr and continue the transaction.
|
||||
|
||||
Specifying --transient on a non-bootc system will throw an error; we
|
||||
don't want to mislead users to thinking this feature works on non-bootc
|
||||
systems.
|
||||
|
||||
If --transient is not specified and the bootc system is in a locked
|
||||
state, the operation will be aborted and a message will be printed
|
||||
suggesting to try again with --transient.
|
||||
|
||||
Resolves: https://issues.redhat.com/browse/RHEL-70917
|
||||
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||
---
|
||||
dnf/cli/cli.py | 40 ++++++++++++++++++++++++++++++---------
|
||||
dnf/cli/option_parser.py | 3 +++
|
||||
dnf/conf/config.py | 2 +-
|
||||
dnf/util.py | 41 +++++++++++++++++++++++++++++-----------
|
||||
4 files changed, 65 insertions(+), 21 deletions(-)
|
||||
|
||||
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||
index 3dc08d616..33fe20aab 100644
|
||||
--- a/dnf/cli/cli.py
|
||||
+++ b/dnf/cli/cli.py
|
||||
@@ -205,28 +205,50 @@ class BaseCli(dnf.Base):
|
||||
else:
|
||||
self.output.reportDownloadSize(install_pkgs, install_only)
|
||||
|
||||
+ bootc_unlock_requested = False
|
||||
+
|
||||
if trans or self._moduleContainer.isChanged() or \
|
||||
(self._history and (self._history.group or self._history.env)):
|
||||
# confirm with user
|
||||
if self.conf.downloadonly:
|
||||
logger.info(_("{prog} will only download packages for the transaction.").format(
|
||||
prog=dnf.util.MAIN_PROG_UPPER))
|
||||
+
|
||||
elif 'test' in self.conf.tsflags:
|
||||
logger.info(_("{prog} will only download packages, install gpg keys, and check the "
|
||||
"transaction.").format(prog=dnf.util.MAIN_PROG_UPPER))
|
||||
- if dnf.util._is_bootc_host() and \
|
||||
- os.path.realpath(self.conf.installroot) == "/" and \
|
||||
- not self.conf.downloadonly:
|
||||
- _bootc_host_msg = _("""
|
||||
-*** Error: system is configured to be read-only; for more
|
||||
-*** information run `bootc --help`.
|
||||
-""")
|
||||
- logger.info(_bootc_host_msg)
|
||||
- raise CliError(_("Operation aborted."))
|
||||
+
|
||||
+ is_bootc_transaction = dnf.util._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.
|
||||
+ 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():
|
||||
+ 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 "
|
||||
+ "will reset when the system reboots."))
|
||||
+ raise CliError(_("Operation aborted."))
|
||||
+ assert self.conf.persistence == "transient"
|
||||
+ 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."))
|
||||
|
||||
if self._promptWanted():
|
||||
if self.conf.assumeno or not self.output.userconfirm():
|
||||
raise CliError(_("Operation aborted."))
|
||||
+
|
||||
+ if bootc_unlock_requested:
|
||||
+ dnf.util._bootc_unlock()
|
||||
else:
|
||||
logger.info(_('Nothing to do.'))
|
||||
return
|
||||
diff --git a/dnf/cli/option_parser.py b/dnf/cli/option_parser.py
|
||||
index 41ff16451..66e69cc98 100644
|
||||
--- a/dnf/cli/option_parser.py
|
||||
+++ b/dnf/cli/option_parser.py
|
||||
@@ -320,6 +320,9 @@ class OptionParser(argparse.ArgumentParser):
|
||||
general_grp.add_argument("--downloadonly", dest="downloadonly",
|
||||
action="store_true", default=False,
|
||||
help=_("only download packages"))
|
||||
+ general_grp.add_argument("--transient", dest="persistence",
|
||||
+ action="store_const", const="transient", default=None,
|
||||
+ help=_("Use a transient overlay which will reset on reboot"))
|
||||
general_grp.add_argument("--comment", dest="comment", default=None,
|
||||
help=_("add a comment to transaction"))
|
||||
# Updateinfo options...
|
||||
diff --git a/dnf/conf/config.py b/dnf/conf/config.py
|
||||
index 32516d1a8..ed6daeb2d 100644
|
||||
--- a/dnf/conf/config.py
|
||||
+++ b/dnf/conf/config.py
|
||||
@@ -342,7 +342,7 @@ class MainConf(BaseConfig):
|
||||
'best', 'assumeyes', 'assumeno', 'clean_requirements_on_remove', 'gpgcheck',
|
||||
'showdupesfromrepos', 'plugins', 'ip_resolve',
|
||||
'rpmverbosity', 'disable_excludes', 'color',
|
||||
- 'downloadonly', 'exclude', 'excludepkgs', 'skip_broken',
|
||||
+ 'downloadonly', 'persistence', 'exclude', 'excludepkgs', 'skip_broken',
|
||||
'tsflags', 'arch', 'basearch', 'ignorearch', 'cacheonly', 'comment']
|
||||
|
||||
for name in config_args:
|
||||
diff --git a/dnf/util.py b/dnf/util.py
|
||||
index e8f587a03..f22e3901b 100644
|
||||
--- a/dnf/util.py
|
||||
+++ b/dnf/util.py
|
||||
@@ -38,6 +38,7 @@ import logging
|
||||
import os
|
||||
import pwd
|
||||
import shutil
|
||||
+import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
@@ -634,14 +635,32 @@ def _name_unset_wrapper(input_name):
|
||||
|
||||
|
||||
def _is_bootc_host():
|
||||
- """Returns true is the system is managed as an immutable container,
|
||||
- false otherwise. If msg is True, a warning message is displayed
|
||||
- for the user.
|
||||
- """
|
||||
- ostree_booted = '/run/ostree-booted'
|
||||
- usr = '/usr/'
|
||||
- # Check if usr is writtable and we are in a running ostree system.
|
||||
- # We want this code to return true only when the system is in locked state. If someone ran
|
||||
- # bootc overlay or ostree admin unlock we would want normal DNF path to be ran as it will be
|
||||
- # temporary changes (until reboot).
|
||||
- return os.path.isfile(ostree_booted) and not os.access(usr, os.W_OK)
|
||||
+ """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 _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)
|
||||
+
|
||||
+
|
||||
+def _bootc_unlock():
|
||||
+ """Set up a writeable overlay on bootc systems."""
|
||||
+
|
||||
+ if _is_bootc_unlocked():
|
||||
+ return
|
||||
+
|
||||
+ unlock_command = ["bootc", "usr-overlay"]
|
||||
+
|
||||
+ 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))
|
||||
--
|
||||
2.48.1
|
||||
|
74
SOURCES/0040-bootc-Document-transient-and-persistence.patch
Normal file
74
SOURCES/0040-bootc-Document-transient-and-persistence.patch
Normal file
@ -0,0 +1,74 @@
|
||||
From ec5cbd19adc6f384923e95d42357f23696b3c950 Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Tue, 17 Dec 2024 18:58:32 +0000
|
||||
Subject: [PATCH 40/44] bootc: Document `--transient` and `persistence`
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Upstream commit: 80a62d89ba3c00f4e0bd3fea995d04ccf9ba8098
|
||||
|
||||
Documents the new `--transient` command-line argument and `persistence`
|
||||
configuration option. I tried to use a table for listing the valid
|
||||
options for `persistence`, but RST does not automatically wrap table
|
||||
cells containing long lines, so a list was much easier.
|
||||
|
||||
Resolves: https://issues.redhat.com/browse/RHEL-70917
|
||||
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||
---
|
||||
doc/command_ref.rst | 9 +++++++++
|
||||
doc/conf_ref.rst | 11 +++++++++++
|
||||
2 files changed, 20 insertions(+)
|
||||
|
||||
diff --git a/doc/command_ref.rst b/doc/command_ref.rst
|
||||
index 75ded68cd..8b55d5a76 100644
|
||||
--- a/doc/command_ref.rst
|
||||
+++ b/doc/command_ref.rst
|
||||
@@ -389,6 +389,11 @@ Options
|
||||
``--showduplicates``
|
||||
Show duplicate packages in repositories. Applicable for the list and search commands.
|
||||
|
||||
+.. _transient_option-label:
|
||||
+
|
||||
+``--transient``
|
||||
+ Applicable only on bootc (bootable containers) systems. Perform transactions using a transient overlay which will be lost on the next reboot. See also the :ref:`persistence <persistence-label>` configuration option.
|
||||
+
|
||||
.. _verbose_options-label:
|
||||
|
||||
``-v, --verbose``
|
||||
@@ -708,6 +713,10 @@ transactions and act according to this information (assuming the
|
||||
which specifies a transaction by a package which it manipulated. When no
|
||||
transaction is specified, list all known transactions.
|
||||
|
||||
+ Note that transient transactions (see :ref:`--transient
|
||||
+ <transient_option-label>`) will be listed even though they do not make
|
||||
+ persistent changes to files under ``/usr`` or to the RPM database.
|
||||
+
|
||||
The "Action(s)" column lists each type of action taken in the transaction. The possible values are:
|
||||
|
||||
* Install (I): a new package was installed on the system
|
||||
diff --git a/doc/conf_ref.rst b/doc/conf_ref.rst
|
||||
index 42a9a37e5..a34e355b6 100644
|
||||
--- a/doc/conf_ref.rst
|
||||
+++ b/doc/conf_ref.rst
|
||||
@@ -430,6 +430,17 @@ configuration file by your distribution to override the DNF defaults.
|
||||
|
||||
Directory where DNF stores its persistent data between runs. Default is ``"/var/lib/dnf"``.
|
||||
|
||||
+.. _persistence-label:
|
||||
+
|
||||
+``persistence``
|
||||
+ :ref:`string <string-label>`
|
||||
+
|
||||
+ Whether changes should persist across system reboots. Default is ``auto``. Passing :ref:`--transient <transient_option-label>` will override this setting to ``transient``. Valid values are:
|
||||
+
|
||||
+ * ``auto``: Changes will persist across reboots, unless the target is a running bootc system and the system is already in an unlocked state (i.e. ``/usr`` is writable).
|
||||
+ * ``transient``: Changes will be lost on the next reboot. Only applicable on bootc systems. Beware that changes to ``/etc`` and ``/var`` will persist, depending on the configuration of your bootc system. See also https://containers.github.io/bootc/man/bootc-usr-overlay.html.
|
||||
+ * ``persist``: Changes will persist across reboots.
|
||||
+
|
||||
.. _pluginconfpath-label:
|
||||
|
||||
``pluginconfpath``
|
||||
--
|
||||
2.48.1
|
||||
|
@ -0,0 +1,166 @@
|
||||
From 5a5572b8adc335075cdae321ad1447e1b8bd8760 Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Wed, 15 Jan 2025 21:43:58 +0000
|
||||
Subject: [PATCH 41/44] 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-70917
|
||||
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 33fe20aab..e7ca86ba8 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 f22e3901b..994fddafc 100644
|
||||
--- a/dnf/util.py
|
||||
+++ b/dnf/util.py
|
||||
@@ -634,33 +634,67 @@ def _name_unset_wrapper(input_name):
|
||||
return input_name if input_name else _("<name-unset>")
|
||||
|
||||
|
||||
-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
|
||||
|
@ -0,0 +1,237 @@
|
||||
From 7e0180ad97a677e6701031f13069c20beec1d8ff Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Thu, 16 Jan 2025 14:06:26 -0500
|
||||
Subject: [PATCH 42/44] bootc: "Re-locking": use ostree admin unlock
|
||||
--transient
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Upstream commit: fa47a256ae7add2ce1c99ae8bedce7216001f396
|
||||
|
||||
To keep /usr read-only after DNF is finished with a transient
|
||||
transaction, we call `ostree admin unlock --transient` to mount the /usr
|
||||
overlay as read-only by default. Then, we create a private mount
|
||||
namespace for DNF and its child processes and remount the /usr overlayfs
|
||||
as read/write in the private mountns.
|
||||
|
||||
os.unshare is unfortunately only available in Python >= 3.12, so we have
|
||||
to call libc.unshare via Python ctypes here and hardcode the CLONE_NEWNS
|
||||
flag that we need to pass.
|
||||
|
||||
Resolves: https://issues.redhat.com/browse/RHEL-70917
|
||||
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||
---
|
||||
dnf/cli/cli.py | 33 ++++++++++---------
|
||||
dnf/util.py | 86 ++++++++++++++++++++++++++++++++++++++------------
|
||||
2 files changed, 83 insertions(+), 36 deletions(-)
|
||||
|
||||
diff --git a/dnf/cli/cli.py b/dnf/cli/cli.py
|
||||
index e7ca86ba8..23170a82b 100644
|
||||
--- a/dnf/cli/cli.py
|
||||
+++ b/dnf/cli/cli.py
|
||||
@@ -205,8 +205,6 @@ class BaseCli(dnf.Base):
|
||||
else:
|
||||
self.output.reportDownloadSize(install_pkgs, install_only)
|
||||
|
||||
- bootc_unlock_requested = False
|
||||
-
|
||||
if trans or self._moduleContainer.isChanged() or \
|
||||
(self._history and (self._history.group or self._history.env)):
|
||||
# confirm with user
|
||||
@@ -218,40 +216,45 @@ 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._Bootc.is_bootc_host() and \
|
||||
+ is_bootc_transaction = dnf.util._BootcSystem.is_bootc_system() 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
|
||||
+ bootc_system = 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")
|
||||
|
||||
- bootc = dnf.util._Bootc()
|
||||
+ bootc_system = dnf.util._BootcSystem()
|
||||
|
||||
- if not bootc.is_unlocked():
|
||||
+ if not bootc_system.is_writable():
|
||||
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 "
|
||||
- "will reset when the system reboots."))
|
||||
+ "perform this transaction in a transient overlay which will reset when "
|
||||
+ "the system reboots."))
|
||||
raise CliError(_("Operation aborted."))
|
||||
assert self.conf.persistence == "transient"
|
||||
- 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."))
|
||||
- elif self.conf.persistence == "transient":
|
||||
- raise CliError(_("Transient transactions are only supported on bootc systems."))
|
||||
+ if not bootc_system.is_unlocked_transient():
|
||||
+ # Only tell the user about the transient overlay if
|
||||
+ # it's not already in place
|
||||
+ 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."))
|
||||
+ else:
|
||||
+ # Not a bootc transaction.
|
||||
+ if self.conf.persistence == "transient":
|
||||
+ raise CliError(_("Transient transactions are only supported on bootc systems."))
|
||||
|
||||
if self._promptWanted():
|
||||
if self.conf.assumeno or not self.output.userconfirm():
|
||||
raise CliError(_("Operation aborted."))
|
||||
|
||||
- if bootc:
|
||||
- bootc.unlock_and_prepare()
|
||||
+ if bootc_system:
|
||||
+ bootc_system.make_writable()
|
||||
else:
|
||||
logger.info(_('Nothing to do.'))
|
||||
return
|
||||
diff --git a/dnf/util.py b/dnf/util.py
|
||||
index 994fddafc..0161f80d8 100644
|
||||
--- a/dnf/util.py
|
||||
+++ b/dnf/util.py
|
||||
@@ -25,6 +25,7 @@ from __future__ import unicode_literals
|
||||
from .pycomp import PY3, basestring
|
||||
from dnf.i18n import _, ucd
|
||||
import argparse
|
||||
+import ctypes
|
||||
import dnf
|
||||
import dnf.callback
|
||||
import dnf.const
|
||||
@@ -634,11 +635,12 @@ def _name_unset_wrapper(input_name):
|
||||
return input_name if input_name else _("<name-unset>")
|
||||
|
||||
|
||||
-class _Bootc:
|
||||
+class _BootcSystem:
|
||||
usr = "/usr"
|
||||
+ CLONE_NEWNS = 0x00020000 # defined in linux/include/uapi/linux/sched.h
|
||||
|
||||
def __init__(self):
|
||||
- if not self.is_bootc_host():
|
||||
+ if not self.is_bootc_system():
|
||||
raise RuntimeError(_("Not running on a bootc system."))
|
||||
|
||||
import gi
|
||||
@@ -656,45 +658,87 @@ class _Bootc:
|
||||
assert self._booted_deployment is not None
|
||||
|
||||
@staticmethod
|
||||
- def is_bootc_host():
|
||||
+ def is_bootc_system():
|
||||
"""Returns true is the system is managed as an immutable container, false
|
||||
otherwise."""
|
||||
ostree_booted = "/run/ostree-booted"
|
||||
return os.path.isfile(ostree_booted)
|
||||
|
||||
+ @classmethod
|
||||
+ def is_writable(cls):
|
||||
+ """Returns true if and only if /usr is writable."""
|
||||
+ return os.access(cls.usr, os.W_OK)
|
||||
+
|
||||
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 is_unlocked_transient(self):
|
||||
+ """Returns true if and only if the bootc system is unlocked in a
|
||||
+ transient state, i.e. a overlayfs is mounted as read-only on /usr.
|
||||
+ Changes can be made to the overlayfs by remounting /usr as
|
||||
+ read/write in a private mount namespace."""
|
||||
+ return self._get_unlocked_state() == self._OSTree.DeploymentUnlockedState.TRANSIENT
|
||||
+
|
||||
+ @classmethod
|
||||
+ def _set_up_mountns(cls):
|
||||
+ # os.unshare is only available in Python >= 3.12.
|
||||
+
|
||||
+ # Access symbols in libraries loaded by the Python interpreter,
|
||||
+ # which will include libc. See https://bugs.python.org/issue34592.
|
||||
+ libc = ctypes.CDLL(None)
|
||||
+ if libc.unshare(cls.CLONE_NEWNS) != 0:
|
||||
+ raise OSError("Failed to unshare mount namespace")
|
||||
+
|
||||
+ mount_command = ["mount", "--options-source=disable", "-o", "remount,rw", cls.usr]
|
||||
+ try:
|
||||
+ completed_process = subprocess.run(mount_command, text=True)
|
||||
+ completed_process.check_returncode()
|
||||
+ except FileNotFoundError:
|
||||
+ raise dnf.exceptions.Error(_("%s: command not found.") % mount_command[0])
|
||||
+ except subprocess.CalledProcessError:
|
||||
+ raise dnf.exceptions.Error(_("Failed to mount %s as read/write: %s", cls.usr, completed_process.stderr))
|
||||
|
||||
- def unlock_and_prepare(self):
|
||||
- """Set up a writeable overlay on bootc systems."""
|
||||
+ @staticmethod
|
||||
+ def _unlock():
|
||||
+ unlock_command = ["ostree", "admin", "unlock", "--transient"]
|
||||
+ try:
|
||||
+ completed_process = subprocess.run(unlock_command, text=True)
|
||||
+ completed_process.check_returncode()
|
||||
+ except FileNotFoundError:
|
||||
+ raise dnf.exceptions.Error(_("%s: command not found. Is this a bootc system?") % unlock_command[0])
|
||||
+ except subprocess.CalledProcessError:
|
||||
+ raise dnf.exceptions.Error(_("Failed to unlock system: %s", completed_process.stderr))
|
||||
+
|
||||
+ def make_writable(self):
|
||||
+ """Set up a writable 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,
|
||||
+ 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.
|
||||
+ writable_unlocked_states = (
|
||||
+ self._OSTree.DeploymentUnlockedState.DEVELOPMENT,
|
||||
+ self._OSTree.DeploymentUnlockedState.HOTFIX,
|
||||
+ )
|
||||
+ if bootc_unlocked_state in writable_unlocked_states:
|
||||
+ # System is already unlocked in development mode, and usr is
|
||||
+ # already mounted read/write.
|
||||
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))
|
||||
+ # System is not unlocked. Unlock it in transient mode, then set up
|
||||
+ # a mount namespace for DNF.
|
||||
+ self._unlock()
|
||||
+ self._set_up_mountns()
|
||||
+ elif bootc_unlocked_state == self._OSTree.DeploymentUnlockedState.TRANSIENT:
|
||||
+ # System is unlocked in transient mode, so usr is mounted
|
||||
+ # read-only. Set up a mount namespace for DNF.
|
||||
+ self._set_up_mountns()
|
||||
|
||||
assert os.access(self.usr, os.W_OK)
|
||||
--
|
||||
2.48.1
|
||||
|
56
SOURCES/0043-spec-Add-dnf-bootc-subpackage.patch
Normal file
56
SOURCES/0043-spec-Add-dnf-bootc-subpackage.patch
Normal file
@ -0,0 +1,56 @@
|
||||
From 9c2a1a6a36d748d059140bf6ee9113d5e9641477 Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Tue, 28 Jan 2025 11:27:00 -0500
|
||||
Subject: [PATCH 43/44] spec: Add dnf-bootc subpackage
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Upstream commit: 76a0c339eb172b1b2a4b1aa8b4db8d6a5145916b
|
||||
|
||||
dnf-bootc's only job is to Require python3-gobject-base, ostree,
|
||||
ostree-libs, and util-linux-core, which are needed to interact with
|
||||
bootc systems. We don't want to add these dependencies on `python3-dnf`
|
||||
because we don't want them on non-bootc systems, so we use a subpackage.
|
||||
|
||||
Resolves: https://issues.redhat.com/browse/RHEL-70917
|
||||
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||
---
|
||||
dnf.spec | 14 ++++++++++++++
|
||||
1 file changed, 14 insertions(+)
|
||||
|
||||
diff --git a/dnf.spec b/dnf.spec
|
||||
index e9abd9041..b60f2692b 100644
|
||||
--- a/dnf.spec
|
||||
+++ b/dnf.spec
|
||||
@@ -180,6 +180,17 @@ Requires: %{name} = %{version}-%{release}
|
||||
%description automatic
|
||||
Systemd units that can periodically download package upgrades and apply them.
|
||||
|
||||
+%package bootc
|
||||
+Summary: %{pkg_summary} - additional bootc dependencies
|
||||
+Requires: python3-%{name} = %{version}-%{release}
|
||||
+Requires: ostree
|
||||
+Requires: ostree-libs
|
||||
+Requires: python3-gobject-base
|
||||
+Requires: util-linux-core
|
||||
+
|
||||
+%description bootc
|
||||
+Additional dependencies needed to perform transactions on booted bootc (bootable containers) systems.
|
||||
+
|
||||
|
||||
%prep
|
||||
%autosetup
|
||||
@@ -358,6 +369,9 @@ popd
|
||||
%{_unitdir}/%{name}-automatic-install.timer
|
||||
%{python3_sitelib}/%{name}/automatic/
|
||||
|
||||
+%files bootc
|
||||
+# bootc subpackage does not include any files
|
||||
+
|
||||
%changelog
|
||||
* Fri Sep 09 2022 Jaroslav Rohel <jrohel@redhat.com> - 4.14.0-1
|
||||
- doc: Describe how gpg keys are stored for `repo_ggpcheck` (RhBug:2020678)
|
||||
--
|
||||
2.48.1
|
||||
|
@ -0,0 +1,45 @@
|
||||
From 19754a7c80e1c2232c370f394ad0d36f5713bf1e Mon Sep 17 00:00:00 2001
|
||||
From: Evan Goode <mail@evangoo.de>
|
||||
Date: Wed, 5 Feb 2025 10:35:08 -0500
|
||||
Subject: [PATCH 44/44] Require libdnf >= 0.74.0 with `persistence` option
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Upstream commit: 5a4f6c42e61ed764ff85eea69125947a280d665d
|
||||
|
||||
This backport actually uses RHEL-9 libdnf version.
|
||||
|
||||
Resolves: https://issues.redhat.com/browse/RHEL-70917
|
||||
Signed-off-by: Petr Písař <ppisar@redhat.com>
|
||||
---
|
||||
dnf.spec | 6 +++++-
|
||||
1 file changed, 5 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/dnf.spec b/dnf.spec
|
||||
index b60f2692b..313a3cb2a 100644
|
||||
--- a/dnf.spec
|
||||
+++ b/dnf.spec
|
||||
@@ -2,7 +2,7 @@
|
||||
%define __cmake_in_source_build 1
|
||||
|
||||
# default dependencies
|
||||
-%global hawkey_version 0.66.0
|
||||
+%global hawkey_version 0.74.0
|
||||
%global libcomps_version 0.1.8
|
||||
%global libmodulemd_version 2.9.3
|
||||
%global rpm_version 4.14.0
|
||||
@@ -21,6 +21,10 @@
|
||||
%global rpm_version 4.11.3-25.el7.centos.1
|
||||
%endif
|
||||
|
||||
+%if 0%{?rhel} == 9
|
||||
+ %global hawkey_version 0.69.0-13
|
||||
+%endif
|
||||
+
|
||||
# override dependencies for fedora 26
|
||||
%if 0%{?fedora} == 26
|
||||
%global rpm_version 4.13.0.1-7
|
||||
--
|
||||
2.48.1
|
||||
|
@ -2,7 +2,7 @@
|
||||
%define __cmake_in_source_build 1
|
||||
|
||||
# default dependencies
|
||||
%global hawkey_version 0.66.0
|
||||
%global hawkey_version 0.74.0
|
||||
%global libcomps_version 0.1.8
|
||||
%global libmodulemd_version 2.9.3
|
||||
%global rpm_version 4.14.0
|
||||
@ -21,6 +21,10 @@
|
||||
%global rpm_version 4.11.3-25.el7.centos.1
|
||||
%endif
|
||||
|
||||
%if 0%{?rhel} == 9
|
||||
%global hawkey_version 0.69.0-13
|
||||
%endif
|
||||
|
||||
# override dependencies for fedora 26
|
||||
%if 0%{?fedora} == 26
|
||||
%global rpm_version 4.13.0.1-7
|
||||
@ -69,7 +73,7 @@ It supports RPMs, modules and comps groups & environments.
|
||||
|
||||
Name: dnf
|
||||
Version: 4.14.0
|
||||
Release: 17%{?dist}
|
||||
Release: 25%{?dist}
|
||||
Summary: %{pkg_summary}
|
||||
# For a breakdown of the licensing, see PACKAGE-LICENSING
|
||||
License: GPLv2+
|
||||
@ -101,6 +105,24 @@ Patch23: 0023-Limit-queries-to-nevra-forms-when-provided-by-comman.patch
|
||||
Patch24: 0024-doc-Remove-provide-of-spec-definition-for-repoquery-.patch
|
||||
Patch25: 0025-man-Improve-upgrade-minimal-command-docs-RHEL-6417.patch
|
||||
Patch26: 0026-doc-Makecache-with-timer-tries-only-one-mirror.patch
|
||||
Patch27: 0027-Add-detection-for-ostree-based-systems-and-warn-user.patch
|
||||
Patch28: 0028-Update-ostree-bootc-host-system-check.patch
|
||||
Patch29: 0029-Update-bootc-hosts-message-to-point-to-bootc-help.patch
|
||||
Patch30: 0030-Allow-installroot-on-read-only-bootc-system.patch
|
||||
Patch31: 0031-smtplib-catch-OSError-not-SMTPException.patch
|
||||
Patch32: 0032-Allow-downloadonly-on-read-only-bootc-system.patch
|
||||
Patch33: 0033-automatic-Check-availability-of-config-file.patch
|
||||
Patch34: 0034-automatic-emitters-send-error-messages.patch
|
||||
Patch35: 0035-automatic-Enhance-errors-reporting.patch
|
||||
Patch36: 0036-Update-need_reboot-for-dnf-automatic.patch
|
||||
Patch37: 0037-automatic-Fix-incorrect-Error-class-instantiation.patch
|
||||
PAtch38: 0038-doc-disableexcludepkgs-all-doesn-t-affect-just-file.patch
|
||||
Patch39: 0039-Add-support-for-transient.patch
|
||||
Patch40: 0040-bootc-Document-transient-and-persistence.patch
|
||||
Patch41: 0041-bootc-Use-ostree-GObject-API-to-get-deployment-statu.patch
|
||||
Patch42: 0042-bootc-Re-locking-use-ostree-admin-unlock-transient.patch
|
||||
Patch43: 0043-spec-Add-dnf-bootc-subpackage.patch
|
||||
Patch44: 0044-Require-libdnf-0.74.0-with-persistence-option.patch
|
||||
|
||||
BuildArch: noarch
|
||||
BuildRequires: cmake
|
||||
@ -210,6 +232,17 @@ Requires: %{name} = %{version}-%{release}
|
||||
%description automatic
|
||||
Systemd units that can periodically download package upgrades and apply them.
|
||||
|
||||
%package bootc
|
||||
Summary: %{pkg_summary} - additional bootc dependencies
|
||||
Requires: python3-%{name} = %{version}-%{release}
|
||||
Requires: ostree
|
||||
Requires: ostree-libs
|
||||
Requires: python3-gobject-base
|
||||
Requires: util-linux-core
|
||||
|
||||
%description bootc
|
||||
Additional dependencies needed to perform transactions on booted bootc (bootable containers) systems.
|
||||
|
||||
|
||||
%prep
|
||||
%autosetup -p1
|
||||
@ -388,7 +421,39 @@ popd
|
||||
%{_unitdir}/%{name}-automatic-install.timer
|
||||
%{python3_sitelib}/%{name}/automatic/
|
||||
|
||||
%files bootc
|
||||
# bootc subpackage does not include any files
|
||||
|
||||
%changelog
|
||||
* Tue Feb 04 2025 Petr Pisar <ppisar@redhat.com> - 4.14.0-25
|
||||
- Add support for transient transactions (RHEL-70917)
|
||||
|
||||
* Mon Jan 13 2025 Ales Matej <amatej@redhat.com> - 4.14.0-24
|
||||
- doc: `--disableexcludepkgs=all` doesn't affect just file configuration
|
||||
(RHEL-28779)
|
||||
|
||||
* Thu Dec 12 2024 Marek Blaha <mblaha@redhat.com> - 4.14.0-23
|
||||
- automatic: Update need_reboot to match needs-restarting (RHEL-62830)
|
||||
|
||||
* Wed Dec 11 2024 Marek Blaha <mblaha@redhat.com> - 4.14.0-22
|
||||
- automatic: Added feature to allow emitters to invoke on dnf error
|
||||
(RHEL-45505, RHEL-61882)
|
||||
|
||||
* Mon Oct 21 2024 Marek Blaha <mblaha@redhat.com> - 4.14.0-21
|
||||
- automatic: Check availability of config file (RHEL-49743)
|
||||
|
||||
* Thu Oct 10 2024 Petr Pisar <ppisar@redhat.com> - 4.14.0-20
|
||||
- Allow "dnf install --downloadonly" on locked OSTree and bootc systems
|
||||
(RHEL-61745)
|
||||
|
||||
* Mon Oct 07 2024 Marek Blaha <mblaha@redhat.com> - 4.14.0-19
|
||||
- Catch more specific OSError instead of SMTPException in dnf-automatic email
|
||||
emitter (RHEL-49743)
|
||||
|
||||
* Tue Oct 01 2024 Petr Pisar <ppisar@redhat.com> - 4.14.0-18
|
||||
- More specific error message on a locked OSTree system or a bootc system
|
||||
without a usr-overlay (RHEL-49670)
|
||||
|
||||
* Tue Aug 06 2024 Petr Pisar <ppisar@redhat.com> - 4.14.0-17
|
||||
- Revert more specific error message on a locked OSTree system or a bootc system
|
||||
without a usr-overlay (RHEL-49670)
|
||||
|
Loading…
Reference in New Issue
Block a user