From 4841aa0c5969f6cf201e700bf96325417ef99b88 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Thu, 15 Feb 2024 14:03:32 -0500 Subject: [PATCH] Add detection for ostree-based systems and warn users about losing changes 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 --- 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 1824bd00ed..c14f836398 100644 --- a/dnf/cli/cli.py +++ b/dnf/cli/cli.py @@ -214,6 +214,15 @@ def do_transaction(self, display=()): 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 6cd7ad41fe..1b465bda59 100644 --- a/dnf/util.py +++ b/dnf/util.py @@ -33,11 +33,13 @@ 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 @@ -639,3 +641,32 @@ def _is_file_pattern_present(specs): if subj._filename_pattern: return True return False + + +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