From 83a267409d633a1f24c0b92300c9c88d54512911 Mon Sep 17 00:00:00 2001 From: Jonathan Lebon Date: Thu, 18 Apr 2019 17:27:09 -0400 Subject: [PATCH] Add new D-Bus APIs for deployment finalization Teach `UpdateDeployment` to make use of libostree's staging lock and then add a `FinalizeDeployment` API to perform the final unlock & reboot. I also added a hidden CLI to make testing this easier, but also because it's likely the FCOS-agent-yet-to-be-named will just end up using the CLI to keep it simple. Closes: #1748 Closes: #1814 Approved by: lucab --- Makefile-rpm-ostree.am | 1 + src/app/main.c | 2 + .../rpmostree-builtin-finalize-deployment.c | 102 ++++++++++++++ src/app/rpmostree-builtin-upgrade.c | 3 + src/app/rpmostree-builtins.h | 1 + .../org.projectatomic.rpmostree1.policy | 11 ++ src/daemon/org.projectatomic.rpmostree1.xml | 18 +++ src/daemon/rpmostree-sysroot-core.h | 4 + src/daemon/rpmostree-sysroot-upgrader.c | 18 +++ src/daemon/rpmostree-sysroot-upgrader.h | 2 + src/daemon/rpmostreed-deployment-utils.c | 8 +- src/daemon/rpmostreed-os.c | 66 +++++++++ src/daemon/rpmostreed-transaction-types.c | 125 ++++++++++++++++++ src/daemon/rpmostreed-transaction-types.h | 8 ++ tests/vmcheck/test-misc-2.sh | 24 ++++ 15 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 src/app/rpmostree-builtin-finalize-deployment.c diff --git a/Makefile-rpm-ostree.am b/Makefile-rpm-ostree.am index d64754006d..d979f339d0 100644 --- a/Makefile-rpm-ostree.am +++ b/Makefile-rpm-ostree.am @@ -44,6 +44,7 @@ rpm_ostree_SOURCES = src/app/main.c \ src/app/rpmostree-ex-builtin-rojig2commit.c \ src/app/rpmostree-builtin-db.c \ src/app/rpmostree-builtin-start-daemon.c \ + src/app/rpmostree-builtin-finalize-deployment.c \ src/app/rpmostree-db-builtin-diff.c \ src/app/rpmostree-db-builtin-list.c \ src/app/rpmostree-db-builtin-version.c \ diff --git a/src/app/main.c b/src/app/main.c index 5cbcd6ddd2..be9fd8bcdd 100644 --- a/src/app/main.c +++ b/src/app/main.c @@ -118,6 +118,8 @@ static RpmOstreeCommand commands[] = { RPM_OSTREE_BUILTIN_FLAG_REQUIRES_ROOT | RPM_OSTREE_BUILTIN_FLAG_HIDDEN, NULL, rpmostree_builtin_start_daemon }, + { "finalize-deployment", RPM_OSTREE_BUILTIN_FLAG_HIDDEN, + NULL, rpmostree_builtin_finalize_deployment }, { NULL } }; diff --git a/src/app/rpmostree-builtin-finalize-deployment.c b/src/app/rpmostree-builtin-finalize-deployment.c new file mode 100644 index 0000000000..aa7c34bfc1 --- /dev/null +++ b/src/app/rpmostree-builtin-finalize-deployment.c @@ -0,0 +1,102 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2019 Red Hat, Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation; either version 2 of the licence or (at + * your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General + * Public License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place, Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "config.h" + +#include "rpmostree-builtins.h" +#include "rpmostree-libbuiltin.h" + +static char *opt_osname; +static gboolean opt_allow_unlocked; +static gboolean opt_allow_missing; + +static GOptionEntry option_entries[] = { + /* though there can only be one staged deployment at a time, this could still + * be useful to assert a specific osname */ + { "os", 0, 0, G_OPTION_ARG_STRING, &opt_osname, "Operate on provided OSNAME", "OSNAME" }, + { "allow-missing-checksum", 0, 0, G_OPTION_ARG_NONE, &opt_allow_missing, "Don't error out if no expected checksum is provided", NULL }, + { "allow-unlocked", 0, 0, G_OPTION_ARG_NONE, &opt_allow_unlocked, "Don't error out if staged deployment wasn't locked", NULL }, + { NULL } +}; + +gboolean +rpmostree_builtin_finalize_deployment (int argc, + char **argv, + RpmOstreeCommandInvocation *invocation, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(GOptionContext) context = g_option_context_new ("CHECKSUM"); + + _cleanup_peer_ GPid peer_pid = 0; + glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL; + if (!rpmostree_option_context_parse (context, option_entries, &argc, &argv, + invocation, cancellable, NULL, NULL, + &sysroot_proxy, &peer_pid, NULL, error)) + return FALSE; + + const char *checksum = NULL; + if (argc > 2) + { + rpmostree_usage_error (context, "Too many arguments passed", error); + return FALSE; + } + else if (argc < 2 && !opt_allow_missing) + { + rpmostree_usage_error (context, "Must provide expected CHECKSUM or --allow-missing-checksum", error); + return FALSE; + } + else if (argc == 2 && opt_allow_missing) + { + rpmostree_usage_error (context, "Cannot specify both CHECKSUM and --allow-missing-checksum", error); + return FALSE; + } + else if (!opt_allow_missing) + checksum = argv[1]; + + glnx_unref_object RPMOSTreeOS *os_proxy = NULL; + if (!rpmostree_load_os_proxy (sysroot_proxy, opt_osname, + cancellable, &os_proxy, error)) + return FALSE; + + GVariantDict dict; + g_variant_dict_init (&dict, NULL); + if (!opt_allow_missing) + g_variant_dict_insert (&dict, "checksum", "s", checksum); + g_variant_dict_insert (&dict, "allow-missing-checksum", "b", opt_allow_missing); + g_variant_dict_insert (&dict, "allow-unlocked", "b", opt_allow_unlocked); + g_autoptr(GVariant) options = g_variant_ref_sink (g_variant_dict_end (&dict)); + + g_autofree char *transaction_address = NULL; + if (!rpmostree_os_call_finalize_deployment_sync (os_proxy, + options, + &transaction_address, + cancellable, + error)) + return FALSE; + + if (!rpmostree_transaction_get_response_sync (sysroot_proxy, + transaction_address, + cancellable, + error)) + return FALSE; + + return TRUE; +} diff --git a/src/app/rpmostree-builtin-upgrade.c b/src/app/rpmostree-builtin-upgrade.c index 994ea20ec8..730db62d4e 100644 --- a/src/app/rpmostree-builtin-upgrade.c +++ b/src/app/rpmostree-builtin-upgrade.c @@ -40,6 +40,7 @@ static gboolean opt_upgrade_unchanged_exit_77; static gboolean opt_cache_only; static gboolean opt_download_only; static char *opt_automatic; +static gboolean opt_lock_finalization; /* "check-diff" is deprecated, replaced by "preview" */ static GOptionEntry option_entries[] = { @@ -53,6 +54,7 @@ static GOptionEntry option_entries[] = { { "download-only", 0, 0, G_OPTION_ARG_NONE, &opt_download_only, "Just download latest ostree and RPM data, don't deploy", NULL }, { "upgrade-unchanged-exit-77", 0, 0, G_OPTION_ARG_NONE, &opt_upgrade_unchanged_exit_77, "If no upgrade is available, exit 77", NULL }, { "trigger-automatic-update-policy", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &opt_automatic, "For automated use only; triggered by automatic timer", NULL }, + { "lock-finalization", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &opt_lock_finalization, "Prevent automatic deployment finalization on shutdown", NULL }, { NULL } }; @@ -162,6 +164,7 @@ rpmostree_builtin_upgrade (int argc, g_variant_dict_insert (&dict, "allow-downgrade", "b", opt_allow_downgrade); g_variant_dict_insert (&dict, "cache-only", "b", opt_cache_only); g_variant_dict_insert (&dict, "download-only", "b", opt_download_only); + g_variant_dict_insert (&dict, "lock-finalization", "b", opt_lock_finalization); g_autoptr(GVariant) options = g_variant_ref_sink (g_variant_dict_end (&dict)); /* Use newer D-Bus API only if we have to. */ diff --git a/src/app/rpmostree-builtins.h b/src/app/rpmostree-builtins.h index 7122e0bf5c..bb6da7d52c 100644 --- a/src/app/rpmostree-builtins.h +++ b/src/app/rpmostree-builtins.h @@ -52,6 +52,7 @@ BUILTINPROTO(kargs); BUILTINPROTO(reset); BUILTINPROTO(start_daemon); BUILTINPROTO(ex); +BUILTINPROTO(finalize_deployment); #undef BUILTINPROTO diff --git a/src/daemon/org.projectatomic.rpmostree1.policy b/src/daemon/org.projectatomic.rpmostree1.policy index 64775321bf..1e5748a1d8 100644 --- a/src/daemon/org.projectatomic.rpmostree1.policy +++ b/src/daemon/org.projectatomic.rpmostree1.policy @@ -159,4 +159,15 @@ yes + + + Finalize staged deployment + Authentication is required to finalize staged deployment + package-x-generic + + auth_admin + auth_admin + auth_admin_keep + + diff --git a/src/daemon/org.projectatomic.rpmostree1.xml b/src/daemon/org.projectatomic.rpmostree1.xml index 6ae04a4590..2d45737e82 100644 --- a/src/daemon/org.projectatomic.rpmostree1.xml +++ b/src/daemon/org.projectatomic.rpmostree1.xml @@ -339,6 +339,10 @@ "idempotent-layering" (type 'b') Don't error out on requests in install-* or uninstall-* modifiers that are already satisfied. + "lock-finalization" (type 'b') + Prevent automatic deployment finalization on shutdown. + Clients must manually call FinalizeDeployment() when ready + to apply the update and reboot. --> @@ -347,6 +351,20 @@ + + + + + diff --git a/src/daemon/rpmostree-sysroot-core.h b/src/daemon/rpmostree-sysroot-core.h index c136175f8e..585a1b1934 100644 --- a/src/daemon/rpmostree-sysroot-core.h +++ b/src/daemon/rpmostree-sysroot-core.h @@ -31,6 +31,10 @@ /* The legacy dir, which we will just delete if we find it */ #define RPMOSTREE_OLD_TMP_ROOTFS_DIR "extensions/rpmostree/commit" +/* Really, this is an OSTree API, but let's consider it hidden for now like the + * /run/ostree/staged-deployment path and company. */ +#define _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED "/run/ostree/staged-deployment-locked" + gboolean rpmostree_syscore_cleanup (OstreeSysroot *sysroot, OstreeRepo *repo, diff --git a/src/daemon/rpmostree-sysroot-upgrader.c b/src/daemon/rpmostree-sysroot-upgrader.c index 27551a10ef..26b9516f03 100644 --- a/src/daemon/rpmostree-sysroot-upgrader.c +++ b/src/daemon/rpmostree-sysroot-upgrader.c @@ -1299,6 +1299,21 @@ rpmostree_sysroot_upgrader_deploy (RpmOstreeSysrootUpgrader *self, if (use_staging) { + /* touch file *before* we stage to avoid races */ + if (self->flags & RPMOSTREE_SYSROOT_UPGRADER_FLAGS_LOCK_FINALIZATION) + { + if (!glnx_shutil_mkdir_p_at (AT_FDCWD, + dirname (strdupa (_OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED)), + 0755, cancellable, error)) + return FALSE; + + glnx_autofd int fd = open (_OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED, + O_CREAT | O_WRONLY | O_NOCTTY | O_CLOEXEC, 0640); + if (fd == -1) + return glnx_throw_errno_prefix (error, "touch(%s)", + _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED); + } + g_auto(RpmOstreeProgress) task = { 0, }; rpmostree_output_task_begin (&task, "Staging deployment"); if (!ostree_sysroot_stage_tree (self->sysroot, self->osname, @@ -1395,6 +1410,9 @@ rpmostree_sysroot_upgrader_flags_get_type (void) { RPMOSTREE_SYSROOT_UPGRADER_FLAGS_SYNTHETIC_PULL, "RPMOSTREE_SYSROOT_UPGRADER_FLAGS_SYNTHETIC_PULL", "synthetic-pull" }, + { RPMOSTREE_SYSROOT_UPGRADER_FLAGS_LOCK_FINALIZATION, + "RPMOSTREE_SYSROOT_UPGRADER_FLAGS_LOCK_FINALIZATION", + "lock-finalization" }, }; GType g_define_type_id = g_flags_register_static (g_intern_static_string ("RpmOstreeSysrootUpgraderFlags"), values); diff --git a/src/daemon/rpmostree-sysroot-upgrader.h b/src/daemon/rpmostree-sysroot-upgrader.h index 8cb9f83df5..c7ccca841e 100644 --- a/src/daemon/rpmostree-sysroot-upgrader.h +++ b/src/daemon/rpmostree-sysroot-upgrader.h @@ -43,6 +43,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (RpmOstreeSysrootUpgrader, g_object_unref) * @RPMOSTREE_SYSROOT_UPGRADER_FLAGS_DRY_RUN: Don't deploy new base. If layering packages, only print the transaction * @RPMOSTREE_SYSROOT_UPGRADER_FLAGS_PKGCACHE_ONLY: Don't try to update cached packages. * @RPMOSTREE_SYSROOT_UPGRADER_FLAGS_SYNTHETIC_PULL: Don't actually pull, just resolve ref and timestamp check + * @RPMOSTREE_SYSROOT_UPGRADER_FLAGS_LOCK_FINALIZATION: Prevent deployment finalization on shutdown * * Flags controlling operation of an #RpmOstreeSysrootUpgrader. */ @@ -54,6 +55,7 @@ typedef enum { RPMOSTREE_SYSROOT_UPGRADER_FLAGS_DRY_RUN = (1 << 3), RPMOSTREE_SYSROOT_UPGRADER_FLAGS_PKGCACHE_ONLY = (1 << 4), RPMOSTREE_SYSROOT_UPGRADER_FLAGS_SYNTHETIC_PULL = (1 << 5), + RPMOSTREE_SYSROOT_UPGRADER_FLAGS_LOCK_FINALIZATION = (1 << 6), } RpmOstreeSysrootUpgraderFlags; /* _NONE means we're doing pure ostree, no client-side computation. diff --git a/src/daemon/rpmostreed-deployment-utils.c b/src/daemon/rpmostreed-deployment-utils.c index 3e86cbafef..f7563dcb2f 100644 --- a/src/daemon/rpmostreed-deployment-utils.c +++ b/src/daemon/rpmostreed-deployment-utils.c @@ -373,7 +373,13 @@ rpmostreed_deployment_generate_variant (OstreeSysroot *sysroot, g_variant_dict_insert (&dict, "live-replaced", "s", live_replaced); if (ostree_deployment_is_staged (deployment)) - g_variant_dict_insert (&dict, "staged", "b", TRUE); + { + g_variant_dict_insert (&dict, "staged", "b", TRUE); + if (!glnx_fstatat_allow_noent (AT_FDCWD, _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED, + NULL, 0, error)) + return FALSE; + g_variant_dict_insert (&dict, "finalization-locked", "b", errno == 0); + } if (refspec) g_variant_dict_insert (&dict, "origin", "s", refspec); diff --git a/src/daemon/rpmostreed-os.c b/src/daemon/rpmostreed-os.c index e84e41d980..0b61b30930 100644 --- a/src/daemon/rpmostreed-os.c +++ b/src/daemon/rpmostreed-os.c @@ -210,6 +210,10 @@ os_authorize_method (GDBusInterfaceSkeleton *interface, no_overrides) g_ptr_array_add (actions, "org.projectatomic.rpmostree1.override"); } + else if (g_strcmp0 (method_name, "FinalizeDeployment") == 0) + { + g_ptr_array_add (actions, "org.projectatomic.rpmostree1.finalize-deployment"); + } else { authorized = FALSE; @@ -974,6 +978,67 @@ os_handle_modify_yum_repo (RPMOSTreeOS *interface, return TRUE; } +static void +on_finalize_done (RpmostreedTransaction *transaction, RpmostreedOS *self) +{ + g_autoptr(GError) local_error = NULL; + if (!rpmostreed_os_load_internals (self, &local_error)) + { + sd_journal_print (LOG_WARNING, "Failed to reload internals: %s", + local_error->message); + } +} + +static gboolean +os_handle_finalize_deployment (RPMOSTreeOS *interface, + GDBusMethodInvocation *invocation, + GVariant *arg_options) +{ + RpmostreedOS *self = RPMOSTREED_OS (interface); + g_autoptr(GCancellable) cancellable = g_cancellable_new (); + glnx_unref_object RpmostreedTransaction *transaction = NULL; + g_autoptr(OstreeSysroot) sysroot = NULL; + const char *osname; + GError *local_error = NULL; + + /* try to merge with an existing transaction, otherwise start a new one */ + RpmostreedSysroot *rsysroot = rpmostreed_sysroot_get (); + + if (!rpmostreed_sysroot_prep_for_txn (rsysroot, invocation, &transaction, &local_error)) + goto out; + if (transaction) + goto out; + + if (!rpmostreed_sysroot_load_state (rsysroot, cancellable, &sysroot, NULL, &local_error)) + goto out; + + osname = rpmostree_os_get_name (interface); + + transaction = rpmostreed_transaction_new_finalize_deployment (invocation, sysroot, osname, + arg_options, cancellable, + &local_error); + if (transaction == NULL) + goto out; + + rpmostreed_sysroot_set_txn (rsysroot, transaction); + + /* Really, we just want to refresh `DefaultDeployment`, but meh... */ + g_signal_connect (transaction, "closed", G_CALLBACK (on_finalize_done), self); + +out: + if (local_error != NULL) + { + g_dbus_method_invocation_take_error (invocation, local_error); + } + else + { + const char *client_address = rpmostreed_transaction_get_client_address (transaction); + rpmostree_os_complete_finalize_deployment (interface, invocation, client_address); + } + + return TRUE; +} + /* This is an older variant of Cleanup, kept for backcompat */ static gboolean os_handle_clear_rollback_target (RPMOSTreeOS *interface, @@ -1713,6 +1778,7 @@ rpmostreed_os_iface_init (RPMOSTreeOSIface *iface) iface->handle_rollback = os_handle_rollback; iface->handle_set_initramfs_state = os_handle_set_initramfs_state; iface->handle_update_deployment = os_handle_update_deployment; + iface->handle_finalize_deployment = os_handle_finalize_deployment; /* legacy cleanup API; superseded by Cleanup() */ iface->handle_clear_rollback_target = os_handle_clear_rollback_target; /* legacy deployment change API; superseded by UpdateDeployment() */ diff --git a/src/daemon/rpmostreed-transaction-types.c b/src/daemon/rpmostreed-transaction-types.c index 943b73c0bd..d1c3b08e0a 100644 --- a/src/daemon/rpmostreed-transaction-types.c +++ b/src/daemon/rpmostreed-transaction-types.c @@ -817,6 +817,8 @@ deploy_transaction_execute (RpmostreedTransaction *transaction, upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_ALLOW_OLDER; if (dry_run) upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_DRY_RUN; + if (deploy_has_bool_option (self, "lock-finalization")) + upgrader_flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_LOCK_FINALIZATION; /* DOWNLOAD_METADATA_ONLY isn't directly exposed at the D-Bus API level, so we shouldn't * ever run into these conflicting options */ @@ -2209,6 +2211,129 @@ rpmostreed_transaction_new_modify_yum_repo (GDBusMethodInvocation *invocation, return (RpmostreedTransaction *) self; } +/* ================================ FinalizeDeployment ================================ */ + +typedef struct { + RpmostreedTransaction parent; + char *osname; + GVariantDict *options; +} FinalizeDeploymentTransaction; + +typedef RpmostreedTransactionClass FinalizeDeploymentTransactionClass; + +GType finalize_deployment_transaction_get_type (void); + +G_DEFINE_TYPE (FinalizeDeploymentTransaction, + finalize_deployment_transaction, + RPMOSTREED_TYPE_TRANSACTION) + +static void +finalize_deployment_transaction_finalize (GObject *object) +{ + FinalizeDeploymentTransaction *self; + + self = (FinalizeDeploymentTransaction *) object; + g_free (self->osname); + g_clear_pointer (&self->options, g_variant_dict_unref); + + G_OBJECT_CLASS (finalize_deployment_transaction_parent_class)->finalize (object); +} + +static gboolean +finalize_deployment_transaction_execute (RpmostreedTransaction *transaction, + GCancellable *cancellable, + GError **error) +{ + FinalizeDeploymentTransaction *self = (FinalizeDeploymentTransaction *) transaction; + OstreeSysroot *sysroot = rpmostreed_transaction_get_sysroot (transaction); + OstreeRepo *repo = ostree_sysroot_repo (sysroot); + + g_autoptr(GPtrArray) deployments = ostree_sysroot_get_deployments (sysroot); + if (deployments->len == 0) + return glnx_throw (error, "No deployments found"); + + OstreeDeployment *default_deployment = deployments->pdata[0]; + if (!ostree_deployment_is_staged (default_deployment)) + return glnx_throw (error, "No pending staged deployment found"); + if (!g_str_equal (ostree_deployment_get_osname (default_deployment), self->osname)) + return glnx_throw (error, "Staged deployment is not for osname '%s'", self->osname); + + gboolean is_layered = FALSE; + g_autofree char *base_checksum = NULL; + if (!rpmostree_deployment_get_layered_info (repo, default_deployment, &is_layered, NULL, + &base_checksum, NULL, NULL, NULL, error)) + return FALSE; + const char *checksum = base_checksum ?: ostree_deployment_get_csum (default_deployment); + + const char *expected_checksum = + vardict_lookup_ptr (self->options, "checksum", "&s"); + const gboolean allow_missing_checksum = + vardict_lookup_bool (self->options, "allow-missing-checksum", FALSE); + if (!expected_checksum && !allow_missing_checksum) + return glnx_throw (error, "Missing expected checksum"); + if (expected_checksum && !g_str_equal (checksum, expected_checksum)) + return glnx_throw (error, "Expected staged base checksum %s, but found %s", + expected_checksum, checksum); + + if (unlink (_OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED) < 0) + { + if (errno != ENOENT) + return glnx_throw_errno_prefix (error, "unlink(%s)", + _OSTREE_SYSROOT_RUNSTATE_STAGED_LOCKED); + if (!vardict_lookup_bool (self->options, "allow-unlocked", FALSE)) + return glnx_throw (error, "Staged deployment already unlocked"); + } + + sd_journal_print (LOG_INFO, "Finalized deployment; rebooting into %s", checksum); + rpmostreed_reboot (cancellable, error); + return TRUE; +} + +static void +finalize_deployment_transaction_class_init (FinalizeDeploymentTransactionClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + object_class->finalize = finalize_deployment_transaction_finalize; + + class->execute = finalize_deployment_transaction_execute; +} + +static void +finalize_deployment_transaction_init (FinalizeDeploymentTransaction *self) +{ +} + +RpmostreedTransaction * +rpmostreed_transaction_new_finalize_deployment (GDBusMethodInvocation *invocation, + OstreeSysroot *sysroot, + const char *osname, + GVariant *options, + GCancellable *cancellable, + GError **error) +{ + g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), NULL); + g_return_val_if_fail (OSTREE_IS_SYSROOT (sysroot), NULL); + + g_autoptr(GVariantDict) options_dict = g_variant_dict_new (options); + + FinalizeDeploymentTransaction *self = + g_initable_new (finalize_deployment_transaction_get_type (), + cancellable, error, + "invocation", invocation, + "sysroot-path", gs_file_get_path_cached (ostree_sysroot_get_path (sysroot)), + NULL); + + if (self != NULL) + { + self->osname = g_strdup (osname); + self->options = g_variant_dict_ref (options_dict); + } + + return (RpmostreedTransaction *) self; +} + /* ================================KernelArg================================ */ typedef struct { diff --git a/src/daemon/rpmostreed-transaction-types.h b/src/daemon/rpmostreed-transaction-types.h index adc31677eb..ebc85feed6 100644 --- a/src/daemon/rpmostreed-transaction-types.h +++ b/src/daemon/rpmostreed-transaction-types.h @@ -69,6 +69,14 @@ rpmostreed_transaction_new_deploy (GDBusMethodInvocation *invocation, GCancellable *cancellable, GError **error); +RpmostreedTransaction * +rpmostreed_transaction_new_finalize_deployment (GDBusMethodInvocation *invocation, + OstreeSysroot *sysroot, + const char *osname, + GVariant *options, + GCancellable *cancellable, + GError **error); + RpmostreedTransaction * rpmostreed_transaction_new_initramfs_state (GDBusMethodInvocation *invocation, OstreeSysroot *sysroot, diff --git a/tests/vmcheck/test-misc-2.sh b/tests/vmcheck/test-misc-2.sh index eeb9e65971..663c126337 100755 --- a/tests/vmcheck/test-misc-2.sh +++ b/tests/vmcheck/test-misc-2.sh @@ -26,6 +26,30 @@ set -x # More miscellaneous tests +# Locked finalization +booted_csum=$(vm_get_booted_csum) +commit=$(vm_cmd ostree commit -b vmcheck --tree=ref=vmcheck) +vm_rpmostree upgrade --lock-finalization +vm_cmd test -f /run/ostree/staged-deployment-locked +cursor=$(vm_get_journal_cursor) +vm_reboot +assert_streq "$(vm_get_booted_csum)" "${booted_csum}" +vm_assert_journal_has_content $cursor 'Not finalizing; found /run/ostree/staged-deployment-locked' +echo "ok locked staging" + +vm_rpmostree upgrade --lock-finalization +vm_cmd test -f /run/ostree/staged-deployment-locked +if vm_rpmostree finalize-deployment; then + assert_not_reached "finalized without expected checksum" +elif vm_rpmostree finalize-deployment WRONG_CHECKSUM; then + assert_not_reached "finalized with wrong checksum" +fi +cursor=$(vm_get_journal_cursor) +vm_reboot_cmd rpm-ostree finalize-deployment "${commit}" +assert_streq "$(vm_get_booted_csum)" "${commit}" +vm_assert_journal_has_content $cursor "Finalized deployment; rebooting into ${commit}" +echo "ok finalize-deployment" + # 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