Backport patch for local repo remote rebases

Backport https://github.com/projectatomic/rpm-ostree/pull/1732/.
Not planning to do a Fedora respin for this, just want it to get picked
up by CAHC.
This commit is contained in:
Jonathan Lebon 2019-02-06 15:13:03 -05:00
parent fa4bb14da2
commit 2d9b7d5e90
2 changed files with 426 additions and 0 deletions

View File

@ -0,0 +1,424 @@
From 7cceb35219e5192b0cb37d8f46f7dfbd7401381e Mon Sep 17 00:00:00 2001
From: Jonathan Lebon <jonathan@jlebon.com>
Date: Mon, 4 Feb 2019 11:51:18 -0500
Subject: [PATCH] app/rebase: Support local repo remotes
Teach rpm-ostree to interpret rebases where the remote component is a
path to a local repo, e.g.:
rpm-ostree rebase /mnt/ostree/repo:my/target/ref
Essentially, the local remote in this case is considered "ephemeral".
It's kind of the equivalent of, on traditional systems:
dnf install --repofrompath repo,/path/to/repodata ...
The use case for this is in OpenShift v4, in which upgrades are done
from containers containing the OSTree commit. There, we want to point
RPM-OSTree directly at the repo in the mounted container and rebase to
the checksum.
For now, the option is marked experimental. One major reason for this is
that the way we pass the repo differs on RHEL7 vs other platforms. (See
comment block in `rpmostree-dbus-helpers.c` for details).
Related: https://github.com/openshift/machine-config-operator/issues/314
Co-authored-by: Colin Walters <walters@verbum.org>
Closes: #1732
Approved by: cgwalters
---
configure.ac | 9 +++
src/app/rpmostree-builtin-deploy.c | 1 +
src/app/rpmostree-builtin-rebase.c | 30 ++++++++-
src/app/rpmostree-builtin-reset.c | 2 +-
src/app/rpmostree-builtin-upgrade.c | 1 +
src/app/rpmostree-dbus-helpers.c | 24 +++++++
src/app/rpmostree-dbus-helpers.h | 1 +
src/app/rpmostree-override-builtins.c | 1 +
src/app/rpmostree-pkg-builtins.c | 1 +
src/daemon/rpmostreed-transaction-types.c | 80 ++++++++++++++++++++++-
tests/vmcheck/test-misc-2.sh | 12 +++-
11 files changed, 155 insertions(+), 7 deletions(-)
diff --git a/configure.ac b/configure.ac
index d60430c0..e398eed1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -135,6 +135,14 @@ AS_IF([pkg-config --atleast-version=4.14.2 rpm], [
])
librpm_version="$(pkg-config rpm --modversion)"
+AC_ARG_ENABLE(dfd_over_dbus,
+ AS_HELP_STRING([--enable-dfd-over-dbus],
+ [Enable passing dfd over D-Bus (default: yes)]),,
+ [enable_dfd_over_dbus=yes])
+AS_IF([test x$enable_dfd_over_dbus = xyes], [
+ AC_DEFINE(HAVE_DFD_OVER_DBUS, 1, [Define if we can pass dfd over D-Bus])
+])
+
AC_PATH_PROG([XSLTPROC], [xsltproc])
GLIB_TESTS
@@ -303,4 +311,5 @@ echo "
gtk-doc: $enable_gtk_doc
rust: $rust_debug_release (lto: ${enable_lto:-no})
cbindgen: ${cbindgen:-internal}
+ use dfd over D-Bus: $enable_dfd_over_dbus
"
diff --git a/src/app/rpmostree-builtin-deploy.c b/src/app/rpmostree-builtin-deploy.c
index 9cda3d6a..c2bd29a5 100644
--- a/src/app/rpmostree-builtin-deploy.c
+++ b/src/app/rpmostree-builtin-deploy.c
@@ -130,6 +130,7 @@ rpmostree_builtin_deploy (int argc,
NULL, /* override replace */
NULL, /* override remove */
NULL, /* override reset */
+ NULL, /* local_repo_remote */
options,
&transaction_address,
cancellable,
diff --git a/src/app/rpmostree-builtin-rebase.c b/src/app/rpmostree-builtin-rebase.c
index e8deec50..9f38915a 100644
--- a/src/app/rpmostree-builtin-rebase.c
+++ b/src/app/rpmostree-builtin-rebase.c
@@ -127,12 +127,37 @@ rpmostree_builtin_rebase (int argc,
new_provided_refspec = opt_branch;
}
+ const char *remainder = NULL;
RpmOstreeRefspecType refspectype;
- if (!rpmostree_refspec_classify (new_provided_refspec, &refspectype, NULL, error))
+ if (!rpmostree_refspec_classify (new_provided_refspec, &refspectype, &remainder, error))
return FALSE;
if (!opt_experimental && refspectype == RPMOSTREE_REFSPEC_TYPE_ROJIG)
return glnx_throw (error, "rojig:// refspec requires --experimental");
+ /* catch "ostree://" or "rojig://"; we'd error out much later in the daemon otherwise */
+ if (strlen (remainder) == 0)
+ return glnx_throw (error, "Refspec is empty");
+
+ /* Check if remote refers to a local repo */
+ g_autofree char *local_repo_remote = NULL;
+ if (G_IN_SET (refspectype, RPMOSTREE_REFSPEC_TYPE_OSTREE,
+ RPMOSTREE_REFSPEC_TYPE_CHECKSUM))
+ {
+ if (*remainder == '/')
+ {
+ if (!opt_experimental)
+ return glnx_throw (error, "Local repo rebase requires --experimental");
+ const char *ref = strrchr (remainder, ':');
+ if (!ref)
+ return glnx_throw (error, "Missing ':' in LOCALPATH:REF rebase");
+ local_repo_remote = g_strndup (remainder, ref - remainder);
+ new_provided_refspec = ref + 1;
+ /* just don't support "/path/to/repo:" for now */
+ if (strlen (new_provided_refspec) == 0)
+ return glnx_throw (error, "Missing REF in LOCALPATH:REF rebase");
+ }
+ }
+
g_autoptr(GVariant) previous_deployment = rpmostree_os_dup_default_deployment (os_proxy);
GVariantDict dict;
@@ -153,7 +178,7 @@ rpmostree_builtin_rebase (int argc,
g_autoptr(GVariant) options = g_variant_ref_sink (g_variant_dict_end (&dict));
/* Use newer D-Bus API only if we have to. */
- if (install_pkgs || uninstall_pkgs)
+ if (install_pkgs || uninstall_pkgs || local_repo_remote)
{
if (!rpmostree_update_deployment (os_proxy,
new_provided_refspec,
@@ -163,6 +188,7 @@ rpmostree_builtin_rebase (int argc,
NULL, /* override replace */
NULL, /* override remove */
NULL, /* override reset */
+ local_repo_remote,
options,
&transaction_address,
cancellable,
diff --git a/src/app/rpmostree-builtin-reset.c b/src/app/rpmostree-builtin-reset.c
index 4b19db63..18b13f12 100644
--- a/src/app/rpmostree-builtin-reset.c
+++ b/src/app/rpmostree-builtin-reset.c
@@ -103,7 +103,7 @@ rpmostree_builtin_reset (int argc,
g_autoptr(GVariant) options = g_variant_ref_sink (g_variant_dict_end (&dict));
if (!rpmostree_update_deployment (os_proxy, NULL, NULL, install_pkgs, uninstall_pkgs,
- NULL, NULL, NULL, options, &transaction_address,
+ NULL, NULL, NULL, NULL, options, &transaction_address,
cancellable, error))
return FALSE;
diff --git a/src/app/rpmostree-builtin-upgrade.c b/src/app/rpmostree-builtin-upgrade.c
index c52ff96f..cc56ae6d 100644
--- a/src/app/rpmostree-builtin-upgrade.c
+++ b/src/app/rpmostree-builtin-upgrade.c
@@ -175,6 +175,7 @@ rpmostree_builtin_upgrade (int argc,
NULL, /* override replace */
NULL, /* override remove */
NULL, /* override reset */
+ NULL, /* local_repo_remote */
options,
&transaction_address,
cancellable,
diff --git a/src/app/rpmostree-dbus-helpers.c b/src/app/rpmostree-dbus-helpers.c
index 0748b887..42b28914 100644
--- a/src/app/rpmostree-dbus-helpers.c
+++ b/src/app/rpmostree-dbus-helpers.c
@@ -1123,6 +1123,7 @@ get_modifiers_variant (const char *set_refspec,
const char *const* override_replace_pkgs,
const char *const* override_remove_pkgs,
const char *const* override_reset_pkgs,
+ const char *local_repo_remote,
GVariant **out_modifiers,
GUnixFDList **out_fd_list,
GError **error)
@@ -1154,6 +1155,27 @@ get_modifiers_variant (const char *set_refspec,
vardict_insert_strv (&dict, "override-remove-packages", override_remove_pkgs);
vardict_insert_strv (&dict, "override-reset-packages", override_reset_pkgs);
+ if (local_repo_remote)
+ {
+ /* Unfortunately, we can't pass an fd to a dir through D-Bus on el7 right now. So
+ * there, we just pass the path. Once that's fixed (or we no longer care about
+ * supporting this feature on el7), we can drop this buildopt. See:
+ * https://bugzilla.redhat.com/show_bug.cgi?id=1672404 */
+#ifdef HAVE_DFD_OVER_DBUS
+ glnx_fd_close int repo_dfd = -1;
+ if (!glnx_opendirat (AT_FDCWD, local_repo_remote, TRUE, &repo_dfd, error))
+ return FALSE;
+
+ int idx = g_unix_fd_list_append (fd_list, repo_dfd, error);
+ if (idx < 0)
+ return FALSE;
+
+ g_variant_dict_insert (&dict, "ex-local-repo-remote", "h", idx);
+#else
+ g_variant_dict_insert (&dict, "ex-local-repo-remote", "s", local_repo_remote);
+#endif
+ }
+
*out_fd_list = g_steal_pointer (&fd_list);
*out_modifiers = g_variant_ref_sink (g_variant_dict_end (&dict));
return TRUE;
@@ -1168,6 +1190,7 @@ rpmostree_update_deployment (RPMOSTreeOS *os_proxy,
const char *const* override_replace_pkgs,
const char *const* override_remove_pkgs,
const char *const* override_reset_pkgs,
+ const char *local_repo_remote,
GVariant *options,
char **out_transaction_address,
GCancellable *cancellable,
@@ -1180,6 +1203,7 @@ rpmostree_update_deployment (RPMOSTreeOS *os_proxy,
override_replace_pkgs,
override_remove_pkgs,
override_reset_pkgs,
+ local_repo_remote,
&modifiers, &fd_list, error))
return FALSE;
diff --git a/src/app/rpmostree-dbus-helpers.h b/src/app/rpmostree-dbus-helpers.h
index 89e6b729..539589c6 100644
--- a/src/app/rpmostree-dbus-helpers.h
+++ b/src/app/rpmostree-dbus-helpers.h
@@ -108,6 +108,7 @@ rpmostree_update_deployment (RPMOSTreeOS *os_proxy,
const char *const* override_replace_pkgs,
const char *const* override_remove_pkgs,
const char *const* override_reset_pkgs,
+ const char *local_repo_remote,
GVariant *options,
char **out_transaction_address,
GCancellable *cancellable,
diff --git a/src/app/rpmostree-override-builtins.c b/src/app/rpmostree-override-builtins.c
index 7cd4eb9b..9e2d217c 100644
--- a/src/app/rpmostree-override-builtins.c
+++ b/src/app/rpmostree-override-builtins.c
@@ -94,6 +94,7 @@ handle_override (RPMOSTreeSysroot *sysroot_proxy,
override_replace,
override_remove,
override_reset,
+ NULL,
options,
&transaction_address,
cancellable,
diff --git a/src/app/rpmostree-pkg-builtins.c b/src/app/rpmostree-pkg-builtins.c
index 469a9c94..2e1274f6 100644
--- a/src/app/rpmostree-pkg-builtins.c
+++ b/src/app/rpmostree-pkg-builtins.c
@@ -114,6 +114,7 @@ pkg_change (RpmOstreeCommandInvocation *invocation,
NULL, /* override replace */
NULL, /* override remove */
NULL, /* override reset */
+ NULL, /* local_repo_remote */
options,
&transaction_address,
cancellable,
diff --git a/src/daemon/rpmostreed-transaction-types.c b/src/daemon/rpmostreed-transaction-types.c
index a26ebdd9..e817d25d 100644
--- a/src/daemon/rpmostreed-transaction-types.c
+++ b/src/daemon/rpmostreed-transaction-types.c
@@ -595,6 +595,7 @@ typedef struct {
GUnixFDList *override_replace_local_pkgs;
char **override_remove_pkgs; /* strv but strings owned by modifiers */
char **override_reset_pkgs; /* strv but strings owned by modifiers */
+ int local_repo_remote_dfd;
} DeployTransaction;
typedef RpmostreedTransactionClass DeployTransactionClass;
@@ -851,6 +852,47 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
g_autoptr(RpmOstreeOrigin) origin =
rpmostree_sysroot_upgrader_dup_origin (upgrader);
+ /* Handle local repo remotes immediately; the idea is that the remote is "transient"
+ * (otherwise, one should set up a proper file:/// remote), so we only support rebasing to
+ * a checksum. We don't want to import a ref. */
+ if (self->local_repo_remote_dfd != -1)
+ {
+ /* self->refspec is the rev in the other local repo we'll rebase to */
+ g_assert (self->refspec);
+
+ RpmOstreeRefspecType refspectype;
+ const char *ref;
+ if (!rpmostree_refspec_classify (self->refspec, &refspectype, &ref, error))
+ return FALSE;
+
+ if (refspectype == RPMOSTREE_REFSPEC_TYPE_ROJIG)
+ return glnx_throw (error, "Local repo remotes not supported for rojig://");
+
+ g_autoptr(OstreeRepo) local_repo_remote =
+ ostree_repo_open_at (self->local_repo_remote_dfd, ".", cancellable, error);
+ if (!local_repo_remote)
+ return glnx_prefix_error (error, "Failed to open local repo");
+ g_autofree char *rev = NULL;
+ if (!ostree_repo_resolve_rev (local_repo_remote, ref, FALSE, &rev, error))
+ return FALSE;
+
+ g_autoptr(OstreeAsyncProgress) progress = ostree_async_progress_new ();
+ rpmostreed_transaction_connect_download_progress (transaction, progress);
+
+ /* pull-local into the system repo */
+ const char *refs_to_fetch[] = { rev, NULL };
+ g_autofree char *local_repo_uri =
+ g_strdup_printf ("file:///proc/self/fd/%d", self->local_repo_remote_dfd);
+ if (!ostree_repo_pull (repo, local_repo_uri, (char**)refs_to_fetch,
+ OSTREE_REPO_PULL_FLAGS_NONE, progress, cancellable, error))
+ return FALSE;
+ rpmostree_transaction_emit_progress_end (RPMOSTREE_TRANSACTION (transaction));
+
+ /* as far as the rest of the code is concerned, we're rebasing to :SHA256 now */
+ g_clear_pointer (&self->refspec, g_free);
+ self->refspec = g_strdup_printf (":%s", rev);
+ }
+
g_autofree gchar *new_refspec = NULL;
g_autofree gchar *old_refspec = NULL;
if (self->refspec)
@@ -1357,6 +1399,7 @@ deploy_transaction_class_init (DeployTransactionClass *class)
static void
deploy_transaction_init (DeployTransaction *self)
{
+ self->local_repo_remote_dfd = -1;
}
static char **
@@ -1517,16 +1560,36 @@ rpmostreed_transaction_new_deploy (GDBusMethodInvocation *invocation,
g_autoptr(GVariant) override_replace_local_pkgs_idxs =
g_variant_dict_lookup_value (self->modifiers, "override-replace-local-packages",
G_VARIANT_TYPE("ah"));
+ int local_repo_remote_idx = -1;
+ /* See related blurb in get_modifiers_variant() */
+#ifdef HAVE_DFD_OVER_DBUS
+ g_variant_dict_lookup (self->modifiers, "ex-local-repo-remote", "h", &local_repo_remote_idx);
+#else
+ const char *local_repo_remote =
+ vardict_lookup_ptr (self->modifiers, "ex-local-repo-remote", "&s");
+ if (local_repo_remote)
+ {
+ if (!glnx_opendirat (AT_FDCWD, local_repo_remote, TRUE,
+ &self->local_repo_remote_dfd, error))
+ return FALSE;
+ }
+#endif
- /* We only use the fd list right now to transfer local RPM fds, which are relevant in the
+ /* First in the fd list is local RPM fds, which are relevant in the
* `install foo.rpm` case and the `override replace foo.rpm` case. Let's make sure that
- * the actual number of fds passed is what we expect. */
-
+ * the actual number of fds passed is what we expect.
+ *
+ * A more recent addition is the local-repo-remote fd.
+ *
+ * Here we validate the number of fds provided against the arguments.
+ */
guint expected_fdn = 0;
if (install_local_pkgs_idxs)
expected_fdn += g_variant_n_children (install_local_pkgs_idxs);
if (override_replace_local_pkgs_idxs)
expected_fdn += g_variant_n_children (override_replace_local_pkgs_idxs);
+ if (local_repo_remote_idx != -1)
+ expected_fdn += 1;
guint actual_fdn = 0;
if (fd_list)
@@ -1555,6 +1618,13 @@ rpmostreed_transaction_new_deploy (GDBusMethodInvocation *invocation,
get_fd_array_from_sparse (fds, nfds, override_replace_local_pkgs_idxs);
self->override_replace_local_pkgs = g_unix_fd_list_new_from_array (new_fds, -1);
}
+
+ if (local_repo_remote_idx != -1)
+ {
+ g_assert_cmpint (local_repo_remote_idx, >=, 0);
+ g_assert_cmpint (local_repo_remote_idx, <, nfds);
+ self->local_repo_remote_dfd = fds[local_repo_remote_idx];
+ }
}
/* Also check for conflicting options -- this is after all a public API. */
@@ -1580,6 +1650,10 @@ rpmostreed_transaction_new_deploy (GDBusMethodInvocation *invocation,
self->override_replace_pkgs || override_replace_local_pkgs_idxs))
return glnx_null_throw (error, "Can't specify no-overrides if setting "
"override modifiers");
+ if (!self->refspec && self->local_repo_remote_dfd != -1)
+ return glnx_null_throw (error, "Missing ref for transient local rebases");
+ if (self->revision && self->local_repo_remote_dfd != -1)
+ return glnx_null_throw (error, "Revision overrides for transient local rebases not implemented yet");
return (RpmostreedTransaction *) g_steal_pointer (&self);
}
diff --git a/tests/vmcheck/test-misc-2.sh b/tests/vmcheck/test-misc-2.sh
index 99c0aef4..f111cac4 100755
--- a/tests/vmcheck/test-misc-2.sh
+++ b/tests/vmcheck/test-misc-2.sh
@@ -26,7 +26,9 @@ set -x
# More miscellaneous tests
-# Custom origin https://github.com/projectatomic/rpm-ostree/pull/1406
+# Custom origin and local repo rebases. This is essentially the RHCOS workflow.
+# https://github.com/projectatomic/rpm-ostree/pull/1406
+# https://github.com/projectatomic/rpm-ostree/pull/1732
booted_csum=$(vm_get_booted_csum)
oscontainer_source="oscontainer://quay.io/exampleos@sha256:98ea6e4f216f2fb4b69fff9b3a44842c38686ca685f3f55dc48c5d3fb1107be4"
if vm_rpmostree rebase --skip-purge --custom-origin-url "${oscontainer_source}" \
@@ -43,6 +45,14 @@ assert_file_has_content_literal status.txt "${oscontainer_source}"
vm_rpmostree upgrade >out.txt
assert_file_has_content_literal out.txt 'Pinned to commit by custom origin: Updated via pivot'
vm_rpmostree cleanup -p
+echo "ok rebase with custom origin"
+
+# Try again but making it think it's pulling from another local repo
+vm_rpmostree rebase --skip-purge /sysroot/ostree/repo:${booted_csum} --experimental
+vm_rpmostree upgrade >out.txt
+assert_file_has_content_literal out.txt 'Pinned to commit; no upgrade available'
+vm_rpmostree cleanup -p
+echo "ok rebase from local repo remote"
# Add metadata string containing EnfOfLife attribtue
META_ENDOFLIFE_MESSAGE="this is a test for metadata message"
--
2.20.1

View File

@ -9,6 +9,8 @@ Source0: rpm-ostree-%{version}.tar.xz
License: LGPLv2+
URL: https://github.com/projectatomic/rpm-ostree
Patch0: 0001-app-rebase-Support-local-repo-remotes.patch
%if !%{defined rust_arches}
# It's not defined yet in the base CentOS7 root
%define rust_arches x86_64 i686 armv7hl aarch64 ppc64 ppc64le s390x