Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bootc: First draft of the bootc plugin #555

Merged
merged 1 commit into from
Sep 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Loading