Skip to content

Commit

Permalink
Add detection for ostree-based systems and warn users about losing ch…
Browse files Browse the repository at this point in the history
…anges

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 <[email protected]>
  • Loading branch information
dcantrell committed Feb 15, 2024
1 parent 259e4c9 commit 69bc4b8
Show file tree
Hide file tree
Showing 16 changed files with 76 additions and 1 deletion.
11 changes: 10 additions & 1 deletion dnf/cli/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ def configure(self):
_checkEnabledRepo(self.base)

def run(self):
dnf.util.is_container()
self.cli._populate_update_security_filter(self.opts, cmp_type="gte")

found = self.base.check_updates(self.opts.packages, print_=True,
Expand All @@ -303,6 +304,7 @@ def configure(self):

def run_on_repo(self):
"""Execute the command with respect to given arguments *cli_args*."""
dnf.util.is_container()
found = self.base.check_updates(self.opts.pkg_specs,
self.reponame, print_=True)
if found:
Expand Down Expand Up @@ -345,6 +347,7 @@ def configure(self):
demands.root_user = True

def run_on_repo(self):
dnf.util.is_container()
self.cli._populate_update_security_filter(self.opts)
"""Execute the command with respect to given arguments *cli_args*."""
_checkGPGKey(self.base, self.cli)
Expand Down Expand Up @@ -398,6 +401,7 @@ def configure(self):

def run_on_repo(self):
"""Execute the command with respect to given arguments *cli_args*."""
dnf.util.is_container()
_checkGPGKey(self.base, self.cli)

done = False
Expand Down Expand Up @@ -453,6 +457,7 @@ def configure(self):

def run_on_repo(self):
"""Execute the command with respect to given arguments *cli_args*."""
dnf.util.is_container()
_checkGPGKey(self.base, self.cli)

done = False
Expand Down Expand Up @@ -516,6 +521,7 @@ def configure(self):

def run_on_repo(self):
"""Execute the command with respect to given arguments *cli_args*."""
dnf.util.is_container()
_checkGPGKey(self.base, self.cli)
for command in self.wrapped_commands:
try:
Expand Down Expand Up @@ -562,6 +568,7 @@ def _replace(self, pkg_spec, reponame):

def run_on_repo(self):
"""Execute the command with respect to given arguments *cli_args*."""
dnf.util.is_container()
_checkGPGKey(self.base, self.cli)

done = False
Expand Down Expand Up @@ -603,6 +610,7 @@ def configure(self):

def run_on_repo(self):
"""Execute the command with respect to given arguments *cli_args*."""
dnf.util.is_container()
_checkGPGKey(self.base, self.cli)

done = False
Expand Down Expand Up @@ -653,7 +661,7 @@ def configure(self):

def run_on_repo(self):
"""Execute the command with respect to given arguments *cli_args*."""

dnf.util.is_container()
done = False

if not self.opts.pkg_specs:
Expand Down Expand Up @@ -692,6 +700,7 @@ def configure(self):

def run_on_repo(self):
"""Execute the command with respect to given arguments *cli_args*."""
dnf.util.is_container()
_checkGPGKey(self.base, self.cli)

done = False
Expand Down
3 changes: 3 additions & 0 deletions dnf/cli/commands/alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
from dnf.cli import commands
import dnf.conf
import dnf.exceptions
import dnf.util
from dnf.i18n import _

logger = logging.getLogger('dnf')
Expand Down Expand Up @@ -157,6 +158,8 @@ def list_alias(self, cmd):
print(_("Alias %s='%s'") % (cmd, " ".join(args)))

def run(self):
dnf.util.is_container()

if not self.aliases_base.enabled:
logger.warning(_("Aliases resolving is disabled."))

Expand Down
3 changes: 3 additions & 0 deletions dnf/cli/commands/autoremove.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from dnf.i18n import _

import dnf.exceptions
import dnf.util
import hawkey
import logging

Expand Down Expand Up @@ -64,6 +65,8 @@ def configure(self):
demands.fresh_metadata = False

def run(self):
dnf.util.is_container()

if any([self.opts.grp_specs, self.opts.pkg_specs, self.opts.filenames]):
forms = []
if self.opts.command in self.nevra_forms:
Expand Down
2 changes: 2 additions & 0 deletions dnf/cli/commands/distrosync.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from __future__ import absolute_import
from dnf.cli import commands
from dnf.i18n import _
import dnf.util


class DistroSyncCommand(commands.Command):
Expand All @@ -45,4 +46,5 @@ def configure(self):
commands._checkEnabledRepo(self.base, self.opts.package)

def run(self):
dnf.util.is_container()
return self.base.distro_sync_userlist(self.opts.package)
2 changes: 2 additions & 0 deletions dnf/cli/commands/downgrade.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from dnf.cli import commands
from dnf.cli.option_parser import OptionParser
from dnf.i18n import _
import dnf.util


class DowngradeCommand(commands.Command):
Expand Down Expand Up @@ -50,6 +51,7 @@ def configure(self):
commands._checkEnabledRepo(self.base)

def run(self):
dnf.util.is_container()
file_pkgs = self.base.add_remote_rpms(self.opts.filenames, strict=False,
progress=self.base.output.progress)
return self.base.downgradePkgs(
Expand Down
1 change: 1 addition & 0 deletions dnf/cli/commands/group.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,6 +365,7 @@ def configure(self):
commands._checkGPGKey(self.base, self.cli)

def run(self):
dnf.util.is_container()
cmd = self.opts.subcmd
extcmds = self.opts.args

Expand Down
1 change: 1 addition & 0 deletions dnf/cli/commands/history.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ def str2transaction_id(s):
return sorted(tids, reverse=True), merged_tids

def run(self):
dnf.util.is_container()
vcmd = self.opts.transactions_action

if vcmd == 'replay':
Expand Down
2 changes: 2 additions & 0 deletions dnf/cli/commands/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ def configure(self):
commands._checkEnabledRepo(self.base)

def run(self):
dnf.util.is_container()

err_pkgs = []
errs = []
error_module_specs = []
Expand Down
1 change: 1 addition & 0 deletions dnf/cli/commands/makecache.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ def set_argparser(parser):
metavar='timer', help=argparse.SUPPRESS)

def run(self):
dnf.util.is_container()
timer = self.opts.timer is not None or self.opts.timer_opt
msg = _("Making cache files for all metadata files.")
logger.debug(msg)
Expand Down
2 changes: 2 additions & 0 deletions dnf/cli/commands/mark.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from dnf.cli import commands

import dnf
import dnf.util
import functools
import logging

Expand Down Expand Up @@ -67,6 +68,7 @@ def configure(self):
demands.resolving = False

def run(self):
dnf.util.container()
cmd = self.opts.mark[0]
pkgs = self.opts.package

Expand Down
1 change: 1 addition & 0 deletions dnf/cli/commands/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ def configure(self):
self.subcmd.configure()

def run(self):
dnf.util.is_container()
self.check_required_argument()
self.subcmd.run_on_module()

Expand Down
2 changes: 2 additions & 0 deletions dnf/cli/commands/reinstall.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from dnf.i18n import _

import dnf.exceptions
import dnf.util
import logging

logger = logging.getLogger('dnf')
Expand Down Expand Up @@ -59,6 +60,7 @@ def configure(self):
commands._checkEnabledRepo(self.base)

def run(self):
dnf.util.is_container()

# Reinstall files.
done = False
Expand Down
2 changes: 2 additions & 0 deletions dnf/cli/commands/shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ def configure(self):
setattr(self.cli.demands, attr, getattr(default_demands, attr))

def run(self):
dnf.util.is_container()

if self.opts.script:
self._run_script(self.opts.script)
else:
Expand Down
1 change: 1 addition & 0 deletions dnf/cli/commands/swap.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def _perform(self, cmd_str, spec):
cmd.run()

def run(self):
dnf.util.is_container()
# The install part must be performed before the remove one because it can
# operate on local rpm files. Command line packages cannot be added
# to the sack once the goal is created.
Expand Down
1 change: 1 addition & 0 deletions dnf/cli/commands/upgrade.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def configure(self):
self.skipped_grp_specs = None

def run(self):
dnf.util.is_container()
cmp_type = "eq" if self.upgrade_minimal else "gte"
self.cli._populate_update_security_filter(self.opts, cmp_type=cmp_type,
all=self.all_security)
Expand Down
42 changes: 42 additions & 0 deletions dnf/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -639,3 +641,43 @@ def _is_file_pattern_present(specs):
if subj._filename_pattern:
return True
return False


def is_container(msg=True):
"""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_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 not os.path.isfile(bootc) and not os.access(bootc, os.X_OK):
# lack of bootc command -or- inability to execute it means we're
# probably not a container
return False

p = subprocess.Popen([bootc, "status", "--json"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(out, err) = p.communicate()

if p.returncode != 0:
return False

# 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)):
if msg:
logger.info(_ostree_msg)

return True

return False

0 comments on commit 69bc4b8

Please sign in to comment.