Skip to content

Commit

Permalink
bootc: First draft of the bootc plugin
Browse files Browse the repository at this point in the history
The only subcommand implemented right now is 'status'.  Most options
on rpm-ostree are available via the plugin.  The notable exception
--advisories since that conflicts with an existing dnf option.

Option groups also don't work, so all of the options are at the plugin
level and I am noting the subcommand(s) they apply to in parentheses
for the description on each option.

After that we just process the options to build an argument vector and
in run() we run it and handle the exit code and log appropriately.
  • Loading branch information
dcantrell authored and evan-goode committed Sep 25, 2024
1 parent f458ab6 commit 78add1e
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 2 deletions.
3 changes: 2 additions & 1 deletion AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ DNF-PLUGINS-CORE CONTRIBUTORS
Wieland Hoffmann <themineo@gmail.com>
Otto Urpelainen <oturpe@iki.fi>
Matija Skrgulja <mskrgulja@gmail.com>
Xiao Liang <github.com/TheHillBright>
Xiao Liang <github.com/TheHillBright>
David Cantrell <dcantrell@redhat.com>
4 changes: 4 additions & 0 deletions dnf-plugins-core.spec
Original file line number Diff line number Diff line change
Expand Up @@ -583,6 +583,7 @@ ln -sf %{yum_utils_subpackage_name}.1.gz %{buildroot}%{_mandir}/man1/repotrack.1
%endif

%files
%{_mandir}/man8/dnf*-bootc.*
%{_mandir}/man8/dnf*-builddep.*
%{_mandir}/man8/dnf*-changelog.*
%{_mandir}/man8/dnf*-config-manager.*
Expand Down Expand Up @@ -620,6 +621,7 @@ ln -sf %{yum_utils_subpackage_name}.1.gz %{buildroot}%{_mandir}/man1/repotrack.1
%config(noreplace) %{_sysconfdir}/dnf/plugins/copr.conf
%config(noreplace) %{_sysconfdir}/dnf/plugins/copr.d
%config(noreplace) %{_sysconfdir}/dnf/plugins/debuginfo-install.conf
%{python2_sitelib}/dnf-plugins/bootc.*
%{python2_sitelib}/dnf-plugins/builddep.*
%{python2_sitelib}/dnf-plugins/changelog.*
%{python2_sitelib}/dnf-plugins/config_manager.*
Expand Down Expand Up @@ -649,6 +651,7 @@ ln -sf %{yum_utils_subpackage_name}.1.gz %{buildroot}%{_mandir}/man1/repotrack.1
%config(noreplace) %{_sysconfdir}/dnf/plugins/copr.d
%config(noreplace) %{_sysconfdir}/dnf/plugins/debuginfo-install.conf
%config(noreplace) %{_sysconfdir}/dnf/plugins/expired-pgp-keys.conf
%{python3_sitelib}/dnf-plugins/bootc.py
%{python3_sitelib}/dnf-plugins/builddep.py
%{python3_sitelib}/dnf-plugins/changelog.py
%{python3_sitelib}/dnf-plugins/config_manager.py
Expand All @@ -668,6 +671,7 @@ ln -sf %{yum_utils_subpackage_name}.1.gz %{buildroot}%{_mandir}/man1/repotrack.1
%{python3_sitelib}/dnf-plugins/repomanage.py
%{python3_sitelib}/dnf-plugins/reposync.py
%{python3_sitelib}/dnf-plugins/system_upgrade.py
%{python3_sitelib}/dnf-plugins/__pycache__/bootc.*
%{python3_sitelib}/dnf-plugins/__pycache__/builddep.*
%{python3_sitelib}/dnf-plugins/__pycache__/changelog.*
%{python3_sitelib}/dnf-plugins/__pycache__/config_manager.*
Expand Down
3 changes: 2 additions & 1 deletion doc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ ADD_CUSTOM_TARGET (doc-man
ADD_CUSTOM_TARGET (doc)
ADD_DEPENDENCIES (doc doc-html doc-man)

INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/dnf4-builddep.8
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/dnf4-bootc.8
${CMAKE_CURRENT_BINARY_DIR}/dnf4-builddep.8
${CMAKE_CURRENT_BINARY_DIR}/dnf4-changelog.8
${CMAKE_CURRENT_BINARY_DIR}/dnf4-config-manager.8
${CMAKE_CURRENT_BINARY_DIR}/dnf4-copr.8
Expand Down
39 changes: 39 additions & 0 deletions doc/bootc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
================
DNF bootc Plugin
================

Manipulate image mode based systems using RPMs available from dnf repositories.

--------
Synopsis
--------

``dnf bootc [status] <options>``

-----------------
Arguments (bootc)
-----------------

``bootc status <options>``
The status of the image mode system.

-------
Options
-------

All general DNF options are accepted, see `Options` in :manpage:`dnf(8)` for details.

``--json``
Output JSON (valid on the status subcommand).

``--booted``
Only print the booted deployment (valid on the status subcommand).

``--jsonpath EXPRESSION``
Filter JSONPath expression (valid on the status subcommand).

``--pending-exit-77``
If pending deploymewnt available, exit 77 (valid on the status subcommand).

``--peer``
Force a peer-to-peer connection instead of using the system message bus (valid on the status subcommand).
1 change: 1 addition & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,7 @@ def version_readout():
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('bootc', 'dnf4-bootc', u'DNF bootc Plugin', AUTHORS, 8),
('builddep', 'dnf4-builddep', u'DNF builddep Plugin', AUTHORS, 8),
('changelog', 'dnf4-changelog', u'DNF changelog Plugin', AUTHORS, 8),
('config_manager', 'dnf4-config-manager', u'DNF config-manager Plugin', AUTHORS, 8),
Expand Down
1 change: 1 addition & 0 deletions plugins/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
INSTALL (FILES bootc.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
INSTALL (FILES builddep.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
INSTALL (FILES changelog.py DESTINATION ${PYTHON_INSTALL_DIR}/dnf-plugins)
if (${WITHOUT_DEBUG} STREQUAL "0")
Expand Down
127 changes: 127 additions & 0 deletions plugins/bootc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# bootc.py, implements the 'bootc' command
#
# Copyright David Cantrell <dcantrell@redhat.com>
# SPDX-License-Identifier: GPL-2.0-or-later

from __future__ import absolute_import
from __future__ import unicode_literals

import dnf
import logging
import subprocess

_, P_ = dnf.i18n.translation("dnf-plugin-bootc")
logger = logging.getLogger("dnf.plugin")
rpm_logger = logging.getLogger("dnf.rpm")


@dnf.plugin.register_command
class BootcCommand(dnf.cli.Command):
aliases = ["bootc"]
summary = _("Modify software on a bootc-based system")

_BOOTC_ALIASES = {"update": "upgrade", "erase": "remove"}
_BOOTC_SUBCOMMANDS = ["status", "install"]

_EXT_CMD = "rpm-ostree"

def _canonical(self):
if self.opts.subcmd is None:
self.opts.subcmd = "status"

if self.opts.subcmd in self._BOOTC_ALIASES.keys():
self.opts.subcmd = self._BOOTC_ALIASES[self.opts.subcmd]

def __init__(self, cli):
super().__init__(cli)

@staticmethod
def set_argparser(parser):
# subcommands for the plugin
parser.add_argument(
"subcmd",
nargs="?",
metavar="BOOTC",
help=_("available subcommands: {} (default), {}").format(
BootcCommand._BOOTC_SUBCOMMANDS[0],
", ".join(BootcCommand._BOOTC_SUBCOMMANDS[1:]),
),
)

# these options are for 'status'
parser.add_argument(
"--json", action="store_true", help=_("Output JSON (status)")
)
parser.add_argument(
"--booted",
action="store_true",
help=_("Only print the booted deployment (status)"),
)
parser.add_argument(
"--jsonpath",
metavar="EXPRESSION",
action="store",
help=_("Filter JSONPath expression (status)"),
)
parser.add_argument(
"--pending-exit-77",
action="store_true",
help=_("If pending deployment available, exit 77 (status)"),
)
parser.add_argument(
"--peer",
action="store_true",
help=_(
"Force a peer-to-peer connection instead of using the system message bus (status)"
),
)

def configure(self):
super().configure()

self._canonical()
cmd = self.opts.subcmd

# ensure we have a valid subcommand
if cmd not in self._BOOTC_SUBCOMMANDS:
logger.critical(
_("Invalid bootc sub-command, use: %s."),
", ".join(self._BOOTC_SUBCOMMANDS),
)
raise dnf.cli.CliError

self.extargs = [self._EXT_CMD, cmd]

# process subcommand arguments
if cmd == "status":
if self.opts.quiet:
self.extargs.append("-q")
elif self.opts.verbose:
self.extargs.append("-v")
elif self.opts.json:
self.extargs.append("--json")
elif self.opts.jsonpath:
self.extargs.append("--jsonpath=%s" % self.opts.jsonpath)
elif self.opts.booted:
self.extargs.append("-b")
elif self.opts.pending_exit_77:
self.extargs.append("--pending-exit-77")
elif self.opts.peer:
self.extargs.append("--peer")
elif self.opts.installroot:
self.extargs.append("--sysroot=%s" % self.opts.installroot)

def run(self):
# combine stdout and stderr; capture text output
proc = subprocess.Popen(
self.extargs, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
)

# stdout and stderr will be combined in stdout, err will be None here
(out, err) = proc.communicate()

if proc.returncode != 0:
logger.critical(out)
raise dnf.cli.CliError
else:
logger.info(out)

0 comments on commit 78add1e

Please sign in to comment.