rebase: add support for rebasing to a specific rev

Expand the available options in the Rebase() D-Bus method to also have a
"revision" key. Its value has the same semantics as the "revision" key
in the Deploy() method (e.g. the "revision=" and "version=" prefixes are
also supported). Also expand the rebase CLI to allow for specifying the
revision as an additional argument.

This allows users to rebase to a specific version or checksum, rather
than only to the latest. Conceptually, this is the equivalent of doing a
rebase followed by a deploy. I.e. we specify an override-commit in the
origin and expect the same behaviours that apply after a deploy to also
apply here.

Closes: #212

Closes: #555
Approved by: cgwalters
This commit is contained in:
Jonathan Lebon 2016-12-21 10:40:42 -05:00 committed by Atomic Bot
parent c5fa202378
commit e10c97007f
7 changed files with 135 additions and 79 deletions

View File

@ -42,7 +42,7 @@ static GOptionEntry option_entries[] = {
}; };
static GVariant * static GVariant *
get_args_variant (void) get_args_variant (const char *revision)
{ {
GVariantDict dict; GVariantDict dict;
@ -50,6 +50,9 @@ get_args_variant (void)
g_variant_dict_insert (&dict, "skip-purge", "b", opt_skip_purge); g_variant_dict_insert (&dict, "skip-purge", "b", opt_skip_purge);
g_variant_dict_insert (&dict, "reboot", "b", opt_reboot); g_variant_dict_insert (&dict, "reboot", "b", opt_reboot);
if (revision != NULL)
g_variant_dict_insert (&dict, "revision", "s", revision);
return g_variant_dict_end (&dict); return g_variant_dict_end (&dict);
} }
@ -61,11 +64,12 @@ rpmostree_builtin_rebase (int argc,
{ {
int exit_status = EXIT_FAILURE; int exit_status = EXIT_FAILURE;
const char *new_provided_refspec; const char *new_provided_refspec;
const char *revision = NULL;
/* forced blank for now */ /* forced blank for now */
const char *packages[] = { NULL }; const char *packages[] = { NULL };
g_autoptr(GOptionContext) context = g_option_context_new ("REFSPEC - Switch to a different tree"); g_autoptr(GOptionContext) context = g_option_context_new ("REFSPEC [REVISION] - Switch to a different tree");
glnx_unref_object RPMOSTreeOS *os_proxy = NULL; glnx_unref_object RPMOSTreeOS *os_proxy = NULL;
glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL; glnx_unref_object RPMOSTreeSysroot *sysroot_proxy = NULL;
g_autofree char *transaction_address = NULL; g_autofree char *transaction_address = NULL;
@ -79,20 +83,23 @@ rpmostree_builtin_rebase (int argc,
error)) error))
goto out; goto out;
if (argc < 2) if (argc < 2 || argc > 3)
{ {
rpmostree_usage_error (context, "REFSPEC must be specified", error); rpmostree_usage_error (context, "Too few or too many arguments", error);
goto out; goto out;
} }
new_provided_refspec = argv[1]; new_provided_refspec = argv[1];
if (argc == 3)
revision = argv[2];
if (!rpmostree_load_os_proxy (sysroot_proxy, opt_osname, if (!rpmostree_load_os_proxy (sysroot_proxy, opt_osname,
cancellable, &os_proxy, error)) cancellable, &os_proxy, error))
goto out; goto out;
if (!rpmostree_os_call_rebase_sync (os_proxy, if (!rpmostree_os_call_rebase_sync (os_proxy,
get_args_variant (), get_args_variant (revision),
new_provided_refspec, new_provided_refspec,
packages, packages,
&transaction_address, &transaction_address,

View File

@ -144,6 +144,7 @@
<!-- Available options: <!-- Available options:
"skip-purge" (type 'b') "skip-purge" (type 'b')
"reboot" (type 'b') "reboot" (type 'b')
"revision" (type 's')
--> -->
<method name="Rebase"> <method name="Rebase">
<arg type="a{sv}" name="options" direction="in"/> <arg type="a{sv}" name="options" direction="in"/>

View File

@ -649,6 +649,7 @@ os_handle_rebase (RPMOSTreeOS *interface,
GVariantDict options_dict; GVariantDict options_dict;
gboolean opt_skip_purge = FALSE; gboolean opt_skip_purge = FALSE;
const char *osname; const char *osname;
const char *opt_revision = NULL;
gboolean opt_reboot = FALSE; gboolean opt_reboot = FALSE;
GError *local_error = NULL; GError *local_error = NULL;
@ -683,6 +684,9 @@ os_handle_rebase (RPMOSTreeOS *interface,
g_variant_dict_lookup (&options_dict, g_variant_dict_lookup (&options_dict,
"reboot", "b", "reboot", "b",
&opt_reboot); &opt_reboot);
g_variant_dict_lookup (&options_dict,
"revision", "&s",
&opt_revision);
g_variant_dict_clear (&options_dict); g_variant_dict_clear (&options_dict);
@ -690,6 +694,7 @@ os_handle_rebase (RPMOSTreeOS *interface,
ot_sysroot, ot_sysroot,
osname, osname,
arg_refspec, arg_refspec,
opt_revision,
opt_skip_purge, opt_skip_purge,
opt_reboot, opt_reboot,
cancellable, cancellable,

View File

@ -71,6 +71,82 @@ out:
return ret; return ret;
} }
static gboolean
apply_revision_override (RpmostreedTransaction *transaction,
OstreeRepo *repo,
OstreeAsyncProgress *progress,
RpmOstreeSysrootUpgrader *upgrader,
const char *revision,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GKeyFile) origin = NULL;
g_autofree char *checksum = NULL;
g_autofree char *version = NULL;
const char *refspec;
origin = rpmostree_sysroot_upgrader_dup_origin (upgrader);
if (origin == NULL)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Booted deployment has no origin");
return FALSE;
}
if (!rpmostreed_parse_revision (revision,
&checksum,
&version,
error))
return FALSE;
refspec = rpmostree_sysroot_upgrader_get_refspec (upgrader);
if (refspec == NULL)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Could not find refspec for booted deployment");
return FALSE;
}
if (version != NULL)
{
rpmostreed_transaction_emit_message_printf (transaction,
"Resolving version '%s'",
version);
if (!rpmostreed_repo_lookup_version (repo, refspec, version, progress,
cancellable, &checksum, error))
return FALSE;
}
else
{
g_assert (checksum != NULL);
rpmostreed_transaction_emit_message_printf (transaction,
"Validating checksum '%s'",
checksum);
if (!rpmostreed_repo_lookup_checksum (repo, refspec, checksum,
progress, cancellable, error))
return FALSE;
}
g_key_file_set_string (origin, "origin", "override-commit", checksum);
if (version != NULL)
{
g_autofree char *comment = NULL;
/* Add a comment with the version, to be nice. */
comment = g_strdup_printf ("Version %s [%.10s]", version, checksum);
g_key_file_set_comment (origin, "origin", "override-commit", comment, NULL);
}
if (!rpmostree_sysroot_upgrader_set_origin (upgrader, origin, cancellable, error))
return FALSE;
return TRUE;
}
/* ============================= Package Diff ============================= */ /* ============================= Package Diff ============================= */
typedef struct { typedef struct {
@ -682,6 +758,7 @@ typedef struct {
RpmostreedTransaction parent; RpmostreedTransaction parent;
char *osname; char *osname;
char *refspec; char *refspec;
char *revision;
gboolean skip_purge; gboolean skip_purge;
gboolean reboot; gboolean reboot;
} RebaseTransaction; } RebaseTransaction;
@ -739,7 +816,7 @@ rebase_transaction_execute (RpmostreedTransaction *transaction,
flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED; flags |= RPMOSTREE_SYSROOT_UPGRADER_FLAGS_IGNORE_UNCONFIGURED;
upgrader = rpmostree_sysroot_upgrader_new (sysroot, self->osname, flags, upgrader = rpmostree_sysroot_upgrader_new (sysroot, self->osname, flags,
cancellable, error); cancellable, error);
if (upgrader == NULL) if (upgrader == NULL)
goto out; goto out;
@ -755,6 +832,13 @@ rebase_transaction_execute (RpmostreedTransaction *transaction,
rpmostreed_transaction_connect_download_progress (transaction, progress); rpmostreed_transaction_connect_download_progress (transaction, progress);
rpmostreed_transaction_connect_signature_progress (transaction, repo); rpmostreed_transaction_connect_signature_progress (transaction, repo);
if (self->revision)
{
if (!apply_revision_override (transaction, repo, progress, upgrader,
self->revision, cancellable, error))
goto out;
}
if (!rpmostree_sysroot_upgrader_pull (upgrader, NULL, 0, if (!rpmostree_sysroot_upgrader_pull (upgrader, NULL, 0,
progress, &changed, progress, &changed,
cancellable, error)) cancellable, error))
@ -810,6 +894,7 @@ rpmostreed_transaction_new_rebase (GDBusMethodInvocation *invocation,
OstreeSysroot *sysroot, OstreeSysroot *sysroot,
const char *osname, const char *osname,
const char *refspec, const char *refspec,
const char *revision,
gboolean skip_purge, gboolean skip_purge,
gboolean reboot, gboolean reboot,
GCancellable *cancellable, GCancellable *cancellable,
@ -832,6 +917,7 @@ rpmostreed_transaction_new_rebase (GDBusMethodInvocation *invocation,
{ {
self->osname = g_strdup (osname); self->osname = g_strdup (osname);
self->refspec = g_strdup (refspec); self->refspec = g_strdup (refspec);
self->revision = g_strdup (revision);
self->skip_purge = skip_purge; self->skip_purge = skip_purge;
self->reboot = reboot; self->reboot = reboot;
} }
@ -880,11 +966,6 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
glnx_unref_object OstreeRepo *repo = NULL; glnx_unref_object OstreeRepo *repo = NULL;
glnx_unref_object OstreeAsyncProgress *progress = NULL; glnx_unref_object OstreeAsyncProgress *progress = NULL;
g_autoptr(GKeyFile) origin = NULL;
g_autofree char *checksum = NULL;
g_autofree char *version = NULL;
const char *refspec = NULL;
gboolean changed = FALSE; gboolean changed = FALSE;
gboolean ret = FALSE; gboolean ret = FALSE;
@ -903,77 +984,12 @@ deploy_transaction_execute (RpmostreedTransaction *transaction,
if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error)) if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
goto out; goto out;
origin = rpmostree_sysroot_upgrader_dup_origin (upgrader);
if (origin == NULL)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Booted deployment has no origin");
goto out;
}
progress = ostree_async_progress_new (); progress = ostree_async_progress_new ();
rpmostreed_transaction_connect_download_progress (transaction, progress); rpmostreed_transaction_connect_download_progress (transaction, progress);
rpmostreed_transaction_connect_signature_progress (transaction, repo); rpmostreed_transaction_connect_signature_progress (transaction, repo);
if (!rpmostreed_parse_revision (self->revision, if (!apply_revision_override (transaction, repo, progress, upgrader,
&checksum, self->revision, cancellable, error))
&version,
error))
goto out;
refspec = rpmostree_sysroot_upgrader_get_refspec (upgrader);
if (refspec == NULL)
{
g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"Could not find refspec for booted deployment");
goto out;
}
if (version != NULL)
{
rpmostreed_transaction_emit_message_printf (transaction,
"Resolving version '%s'",
version);
if (!rpmostreed_repo_lookup_version (repo,
refspec,
version,
progress,
cancellable,
&checksum,
error))
goto out;
}
else
{
g_assert (checksum != NULL);
rpmostreed_transaction_emit_message_printf (transaction,
"Validating checksum '%s'",
checksum);
if (!rpmostreed_repo_lookup_checksum (repo,
refspec,
checksum,
progress,
cancellable,
error))
goto out;
}
g_key_file_set_string (origin, "origin", "override-commit", checksum);
if (version != NULL)
{
g_autofree char *comment = NULL;
/* Add a comment with the version, to be nice. */
comment = g_strdup_printf ("Version %s [%.10s]", version, checksum);
g_key_file_set_comment (origin, "origin", "override-commit", comment, NULL);
}
if (!rpmostree_sysroot_upgrader_set_origin (upgrader, origin, cancellable, error))
goto out; goto out;
if (!rpmostree_sysroot_upgrader_pull (upgrader, NULL, 0, if (!rpmostree_sysroot_upgrader_pull (upgrader, NULL, 0,

View File

@ -59,6 +59,7 @@ RpmostreedTransaction *
OstreeSysroot *sysroot, OstreeSysroot *sysroot,
const char *osname, const char *osname,
const char *refspec, const char *refspec,
const char *revision,
gboolean skip_purge, gboolean skip_purge,
gboolean reboot, gboolean reboot,
GCancellable *cancellable, GCancellable *cancellable,

View File

@ -23,7 +23,7 @@ set -e
ensure_dbus ensure_dbus
echo "1..10" echo "1..13"
setup_os_repository "archive-z2" "syslinux" setup_os_repository "archive-z2" "syslinux"
@ -109,6 +109,31 @@ fi
assert_file_has_content OUTPUT-err 'Checksum .* not found in .*' assert_file_has_content OUTPUT-err 'Checksum .* not found in .*'
echo "ok error on deploying commit on other branch" echo "ok error on deploying commit on other branch"
# Make sure we're currently on otheros
rpm-ostree status | head --lines 5 | tee OUTPUT-status.txt
assert_file_has_content OUTPUT-status.txt otheros:testos/buildmaster/x86_64-runtime
os_repository_new_commit 2 2
rpm-ostree rebase --os=testos testos:testos/buildmaster/x86_64-runtime $(date "+%Y%m%d.2")
rpm-ostree status | head --lines 5 | tee OUTPUT-status.txt
assert_file_has_content OUTPUT-status.txt testos
assert_file_has_content OUTPUT-status.txt $(date "+%Y%m%d\.2")
echo "ok rebase onto other branch at specific version"
branch=testos/buildmaster/x86_64-runtime
new_csum=$(ostree --repo=${test_tmpdir}/testos-repo commit -b $branch --tree=ref=$branch)
rpm-ostree rebase --os=testos otheros:testos/buildmaster/x86_64-runtime $new_csum
rpm-ostree status | head --lines 5 | tee OUTPUT-status.txt
assert_file_has_content OUTPUT-status.txt otheros
assert_file_has_content OUTPUT-status.txt $new_csum
echo "ok rebase onto other branch at specific checksum"
if rpm-ostree rebase --os=testos testos:testos/buildmaster/x86_64-runtime $other_rev 2>OUTPUT-err; then
assert_not_reached "Rebasing onto out-of-branch commit unexpectedly succeeded."
fi
assert_file_has_content OUTPUT-err 'Checksum .* not found in .*'
echo "ok error on rebasing onto commit on other branch"
# Ensure it returns an error when passing a wrong option. # Ensure it returns an error when passing a wrong option.
rpm-ostree --help | awk '/^$/ {in_commands=0} {if(in_commands==1){print $0}} /^Builtin Commands:/ {in_commands=1}' > commands rpm-ostree --help | awk '/^$/ {in_commands=0} {if(in_commands==1){print $0}} /^Builtin Commands:/ {in_commands=1}' > commands
while read command; do while read command; do

View File

@ -341,6 +341,7 @@ os_repository_new_commit ()
echo "content iteration ${content_iteration}" > usr/bin/content-iteration echo "content iteration ${content_iteration}" > usr/bin/content-iteration
version=$(date "+%Y%m%d.${content_iteration}") version=$(date "+%Y%m%d.${content_iteration}")
echo "version: $version"
ostree --repo=${test_tmpdir}/testos-repo commit --add-metadata-string "version=${version}" -b testos/buildmaster/x86_64-runtime -s "Build" ostree --repo=${test_tmpdir}/testos-repo commit --add-metadata-string "version=${version}" -b testos/buildmaster/x86_64-runtime -s "Build"
cd ${test_tmpdir} cd ${test_tmpdir}