From 07904c245715f3ae379463b44f28bd5533880170 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 16 Sep 2013 22:37:00 -0400 Subject: [PATCH] sysroot: Support more arbitrary deployment changes This commit changes the sysroot API so that one can create arbitrary new deployment checkouts, then commit them as one step. This is to enable things like an automatic bisection tool which say create 50 deployments at once, then when done clean them up. This also moves some printfs from the library into src/ostree. --- src/libostree/ostree-sysroot-cleanup.c | 15 +- src/libostree/ostree-sysroot-deploy.c | 417 ++++++++----------------- src/libostree/ostree-sysroot-private.h | 6 + src/libostree/ostree-sysroot.c | 4 + src/libostree/ostree-sysroot.h | 1 - src/ostree/ot-admin-builtin-deploy.c | 24 +- src/ostree/ot-admin-builtin-upgrade.c | 21 +- src/ostree/ot-admin-functions.c | 54 ++++ src/ostree/ot-admin-functions.h | 9 + tests/test-admin-deploy-1.sh | 11 +- 10 files changed, 258 insertions(+), 304 deletions(-) diff --git a/src/libostree/ostree-sysroot-cleanup.c b/src/libostree/ostree-sysroot-cleanup.c index 186c6cb2..884597b2 100644 --- a/src/libostree/ostree-sysroot-cleanup.c +++ b/src/libostree/ostree-sysroot-cleanup.c @@ -25,11 +25,11 @@ #include "ostree-sysroot-private.h" -static gboolean -list_deployment_dirs_for_os (GFile *osdir, - GPtrArray *inout_deployments, - GCancellable *cancellable, - GError **error) +gboolean +_ostree_sysroot_list_deployment_dirs_for_os (GFile *osdir, + GPtrArray *inout_deployments, + GCancellable *cancellable, + GError **error) { gboolean ret = FALSE; const char *osname = gs_file_get_basename_cached (osdir); @@ -136,7 +136,8 @@ list_all_deployment_directories (OstreeSysroot *self, if (g_file_info_get_file_type (file_info) != G_FILE_TYPE_DIRECTORY) continue; - if (!list_deployment_dirs_for_os (child, ret_deployments, cancellable, error)) + if (!_ostree_sysroot_list_deployment_dirs_for_os (child, ret_deployments, + cancellable, error)) goto out; } @@ -340,7 +341,6 @@ cleanup_old_deployments (OstreeSysroot *self, if (device == root_device && inode == root_inode) continue; - g_print ("ostadmin: Deleting deployment %s\n", gs_file_get_path_cached (deployment_path)); if (!gs_shutil_rm_rf (deployment_path, cancellable, error)) goto out; if (!gs_shutil_rm_rf (origin_path, cancellable, error)) @@ -365,7 +365,6 @@ cleanup_old_deployments (OstreeSysroot *self, if (g_hash_table_lookup (active_boot_checksums, bootcsum)) continue; - g_print ("ostadmin: Deleting bootdir %s\n", gs_file_get_path_cached (bootdir)); if (!gs_shutil_rm_rf (bootdir, cancellable, error)) goto out; } diff --git a/src/libostree/ostree-sysroot-deploy.c b/src/libostree/ostree-sysroot-deploy.c index 14136d24..229bf151 100644 --- a/src/libostree/ostree-sysroot-deploy.c +++ b/src/libostree/ostree-sysroot-deploy.c @@ -459,224 +459,6 @@ checksum_from_kernel_src (GFile *src, return TRUE; } -static int -sort_by_bootserial (gconstpointer ap, gconstpointer bp) -{ - OstreeDeployment **a_loc = (OstreeDeployment**)ap; - OstreeDeployment *a = *a_loc; - OstreeDeployment **b_loc = (OstreeDeployment**)bp; - OstreeDeployment *b = *b_loc; - - if (ostree_deployment_get_bootserial (a) == ostree_deployment_get_bootserial (b)) - return 0; - else if (ostree_deployment_get_bootserial (a) < ostree_deployment_get_bootserial (b)) - return -1; - return 1; -} - -static GPtrArray * -filter_deployments_by_bootcsum (GPtrArray *deployments, - const char *osname, - const char *bootcsum) -{ - GPtrArray *ret = g_ptr_array_new (); - guint i; - - for (i = 0; i < deployments->len; i++) - { - OstreeDeployment *deployment = deployments->pdata[i]; - - if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0) - continue; - if (strcmp (ostree_deployment_get_bootcsum (deployment), bootcsum) != 0) - continue; - - g_ptr_array_add (ret, deployment); - } - g_ptr_array_sort (ret, sort_by_bootserial); - - return ret; -} - -static void -compute_new_deployment_list (int current_bootversion, - GPtrArray *current_deployments, - const char *osname, - OstreeDeployment *booted_deployment, - OstreeDeployment *merge_deployment, - gboolean retain, - const char *revision, - const char *bootcsum, - GPtrArray **out_new_deployments) -{ - guint i; - int new_index; - guint new_deployserial = 0; - int new_bootserial = 0; - gs_unref_object OstreeDeployment *new_deployment = NULL; - gs_unref_ptrarray GPtrArray *matching_deployments_by_bootserial = NULL; - OstreeDeployment *deployment_to_delete = NULL; - gs_unref_ptrarray GPtrArray *ret_new_deployments = NULL; - - /* First, compute the serial for this deployment; we look - * for other ones in this os with the same checksum. - */ - for (i = 0; i < current_deployments->len; i++) - { - OstreeDeployment *deployment = current_deployments->pdata[i]; - - if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0) - continue; - if (strcmp (ostree_deployment_get_csum (deployment), revision) != 0) - continue; - - new_deployserial = MAX(new_deployserial, ostree_deployment_get_deployserial (deployment)+1); - } - - /* We retain by default (well, hardcoded now) one previous - * deployment for this OS, plus the booted deployment. Usually, we - * have one previous, one into which we're booted, and we're - * deploying a new one. So the old previous will get swapped out, - * and booted becomes previous. - * - * But if the user then upgrades again, we will end up pruning the - * front of the deployment list. We never delete the running - * deployment. - */ - if (!retain) - { - for (i = 0; i < current_deployments->len; i++) - { - OstreeDeployment *deployment = current_deployments->pdata[i]; - - if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0) - continue; - - // Keep both the booted and merge deployments - if (ostree_deployment_equal (deployment, booted_deployment) || - ostree_deployment_equal (deployment, merge_deployment)) - continue; - - deployment_to_delete = deployment; - } - } - - ret_new_deployments = g_ptr_array_new_with_free_func ((GDestroyNotify)g_object_unref); - - new_deployment = ostree_deployment_new (0, osname, revision, new_deployserial, - bootcsum, new_bootserial); - g_ptr_array_add (ret_new_deployments, g_object_ref (new_deployment)); - new_index = 1; - for (i = 0; i < current_deployments->len; i++) - { - OstreeDeployment *orig_deployment = current_deployments->pdata[i]; - gs_unref_object OstreeDeployment *deployment_clone = NULL; - - if (orig_deployment == deployment_to_delete) - continue; - - deployment_clone = ostree_deployment_clone (orig_deployment); - ostree_deployment_set_index (deployment_clone, new_index); - new_index++; - g_ptr_array_add (ret_new_deployments, g_object_ref (deployment_clone)); - } - - /* Just renumber the deployments for the OS we're adding; we don't - * handle anything else at the moment. - */ - matching_deployments_by_bootserial = filter_deployments_by_bootcsum (ret_new_deployments, - osname, bootcsum); - for (i = 0; i < matching_deployments_by_bootserial->len; i++) - { - OstreeDeployment *deployment = matching_deployments_by_bootserial->pdata[i]; - ostree_deployment_set_bootserial (deployment, i); - } - - *out_new_deployments = ret_new_deployments; - ret_new_deployments = NULL; -} - -static GHashTable * -object_array_to_set (GPtrArray *objlist, - GHashFunc hashfunc, - GEqualFunc equalfunc) -{ - GHashTable *ret = g_hash_table_new_full (hashfunc, equalfunc, g_object_unref, NULL); - guint i; - - for (i = 0; i < objlist->len; i++) - { - GObject *obj = g_object_ref (objlist->pdata[i]); - g_hash_table_insert (ret, obj, obj); - } - - return ret; -} - -static GHashTable * -object_set_subtract (GHashTable *a, GHashTable *b) -{ - GHashTable *ret = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL); - GHashTableIter hashiter; - gpointer hashkey, hashvalue; - - g_hash_table_iter_init (&hashiter, a); - while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue)) - { - if (!g_hash_table_contains (b, hashkey)) - { - GObject *o = g_object_ref (hashkey); - g_hash_table_insert (ret, o, o); - } - } - - return ret; -} - -static void -print_deployment_set (gboolean for_removal, - GHashTable *set) -{ - GHashTableIter hashiter; - gpointer hashkey, hashvalue; - - if (g_hash_table_size (set) == 0) - return; - - g_print ("%s\n", for_removal ? "removed:" : "added: "); - - g_hash_table_iter_init (&hashiter, set); - while (g_hash_table_iter_next (&hashiter, &hashkey, &hashvalue)) - { - OstreeDeployment *deployment = hashkey; - - g_print (" %c %s %s.%d", - for_removal ? '-' : '+', ostree_deployment_get_osname (deployment), - ostree_deployment_get_csum (deployment), - ostree_deployment_get_deployserial (deployment)); - - if (!for_removal) - g_print (" index=%d", ostree_deployment_get_index (deployment)); - g_print ("\n"); - } -} - -static void -print_deployment_diff (GPtrArray *current_deployments, - GPtrArray *new_deployments) -{ - gs_unref_hashtable GHashTable *curset = object_array_to_set (current_deployments, ostree_deployment_hash, ostree_deployment_equal); - gs_unref_hashtable GHashTable *newset = object_array_to_set (new_deployments, ostree_deployment_hash, ostree_deployment_equal); - gs_unref_hashtable GHashTable *removed = NULL; - gs_unref_hashtable GHashTable *added = NULL; - - removed = object_set_subtract (curset, newset); - added = object_set_subtract (newset, curset); - - print_deployment_set (TRUE, removed); - print_deployment_set (FALSE, added); -} - /* FIXME: We should really do individual fdatasync() on files/dirs, * since this causes us to block on unrelated I/O. However, it's just * safer for now. @@ -970,7 +752,8 @@ swap_bootloader (OstreeSysroot *sysroot, } static GHashTable * -bootcsum_counts_for_deployment_list (GPtrArray *deployments) +bootcsum_counts_for_deployment_list (GPtrArray *deployments, + gboolean set_bootserial) { guint i; GHashTable *ret = @@ -984,10 +767,55 @@ bootcsum_counts_for_deployment_list (GPtrArray *deployments) count = GPOINTER_TO_UINT (g_hash_table_lookup (ret, bootcsum)); g_hash_table_replace (ret, (char*)bootcsum, GUINT_TO_POINTER (count + 1)); + + if (set_bootserial) + ostree_deployment_set_bootserial (deployment, count); } return ret; } +/* TEMPORARY HACK: Add a "current" symbolic link that's easy to + * follow inside the gnome-ostree build scripts. This isn't atomic, + * but that doesn't matter because it's only used by deployments + * done from the host. + */ +static gboolean +create_current_symlinks (OstreeSysroot *self, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + guint i; + gs_unref_hashtable GHashTable *created_current_for_osname = + g_hash_table_new (g_str_hash, g_str_equal); + + for (i = 0; i < self->deployments->len; i++) + { + OstreeDeployment *deployment = self->deployments->pdata[i]; + const char *osname = ostree_deployment_get_osname (deployment); + + if (!g_hash_table_lookup (created_current_for_osname, osname)) + { + gs_unref_object GFile *osdir = ot_gfile_resolve_path_printf (self->path, "ostree/deploy/%s", osname); + gs_unref_object GFile *os_current_path = g_file_get_child (osdir, "current"); + gs_unref_object GFile *deployment_path = ostree_sysroot_get_deployment_directory (self, deployment); + gs_free char *target = g_file_get_relative_path (osdir, deployment_path); + + g_assert (target != NULL); + + if (!ot_gfile_atomic_symlink_swap (os_current_path, target, + cancellable, error)) + goto out; + + g_hash_table_insert (created_current_for_osname, (char*)osname, GUINT_TO_POINTER (1)); + } + } + + ret = TRUE; + out: + return ret; +} + /** * ostree_sysroot_write_deployments: * @self: Sysroot @@ -1007,9 +835,16 @@ ostree_sysroot_write_deployments (OstreeSysroot *self, gboolean ret = FALSE; guint i; gboolean requires_new_bootversion = FALSE; + gboolean found_booted_deployment = FALSE; + gs_unref_hashtable GHashTable *new_bootcsum_to_count = NULL; g_assert (self->loaded); + /* Calculate the total number of deployments per bootcsums; while we + * are doing this, assign a bootserial to each new deployment. + */ + new_bootcsum_to_count = bootcsum_counts_for_deployment_list (new_deployments, TRUE); + /* Determine whether or not we need to touch the bootloader * configuration. If we have an equal number of deployments and * more strongly an equal number of deployments per bootcsum, then @@ -1022,9 +857,7 @@ ostree_sysroot_write_deployments (OstreeSysroot *self, GHashTableIter hashiter; gpointer hkey, hvalue; gs_unref_hashtable GHashTable *orig_bootcsum_to_count - = bootcsum_counts_for_deployment_list (self->deployments); - gs_unref_hashtable GHashTable *new_bootcsum_to_count - = bootcsum_counts_for_deployment_list (new_deployments); + = bootcsum_counts_for_deployment_list (self->deployments, FALSE); g_hash_table_iter_init (&hashiter, orig_bootcsum_to_count); while (g_hash_table_iter_next (&hashiter, &hkey, &hvalue)) @@ -1041,6 +874,22 @@ ostree_sysroot_write_deployments (OstreeSysroot *self, } } + for (i = 0; i < new_deployments->len; i++) + { + OstreeDeployment *deployment = new_deployments->pdata[i]; + + if (deployment == self->booted_deployment) + found_booted_deployment = TRUE; + + ostree_deployment_set_index (deployment, i); + } + + if (self->booted_deployment && !found_booted_deployment) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Attempting to remove booted deployment"); + goto out; + } + if (!requires_new_bootversion) { if (!full_system_sync (cancellable, error)) @@ -1108,6 +957,8 @@ ostree_sysroot_write_deployments (OstreeSysroot *self, } } + g_print ("Transaction complete, performing cleanup\n"); + /* Now reload from disk */ if (!ostree_sysroot_load (self, cancellable, error)) { @@ -1115,10 +966,60 @@ ostree_sysroot_write_deployments (OstreeSysroot *self, goto out; } + if (!create_current_symlinks (self, cancellable, error)) + goto out; + + /* And finally, cleanup of any leftover data. + */ + if (!ostree_sysroot_cleanup (self, cancellable, error)) + { + g_prefix_error (error, "Performing final cleanup: "); + goto out; + } + ret = TRUE; out: return ret; } + +static gboolean +allocate_deployserial (OstreeSysroot *self, + const char *osname, + const char *revision, + int *out_deployserial, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + guint i; + int new_deployserial = 0; + gs_unref_object GFile *osdir = NULL; + gs_unref_ptrarray GPtrArray *tmp_current_deployments = + g_ptr_array_new_with_free_func (g_object_unref); + + osdir = ot_gfile_get_child_build_path (self->path, "ostree/deploy", osname, NULL); + + if (!_ostree_sysroot_list_deployment_dirs_for_os (osdir, tmp_current_deployments, + cancellable, error)) + goto out; + + for (i = 0; i < tmp_current_deployments->len; i++) + { + OstreeDeployment *deployment = tmp_current_deployments->pdata[i]; + + if (strcmp (ostree_deployment_get_osname (deployment), osname) != 0) + continue; + if (strcmp (ostree_deployment_get_csum (deployment), revision) != 0) + continue; + + new_deployserial = MAX(new_deployserial, ostree_deployment_get_deployserial (deployment)+1); + } + + ret = TRUE; + *out_deployserial = new_deployserial; + out: + return ret; +} /** * ostree_sysroot_deploy_one_tree: @@ -1127,14 +1028,13 @@ ostree_sysroot_write_deployments (OstreeSysroot *self, * @revision: Checksum to add * @origin: (allow-none): Origin to use for upgrades * @add_kernel_argv: (allow-none): Append these arguments to kernel configuration - * @retain: If %TRUE, then do not delete earlier deployment * @provided_merge_deployment: (allow-none): Use this deployment for merge path * @out_new_deployment: (out): The new deployment path * @cancellable: Cancellable * @error: Error * - * Add a new deployment with revision @revision; if @retain is %FALSE, - * then an earlier deployment will be garbage collected. + * Check out deployment tree with revision @revision, performing a 3 + * way merge with @provided_merge_deployment for configuration. */ gboolean ostree_sysroot_deploy_one_tree (OstreeSysroot *self, @@ -1142,14 +1042,14 @@ ostree_sysroot_deploy_one_tree (OstreeSysroot *self, const char *revision, GKeyFile *origin, char **add_kernel_argv, - gboolean retain, OstreeDeployment *provided_merge_deployment, OstreeDeployment **out_new_deployment, GCancellable *cancellable, GError **error) { gboolean ret = FALSE; - OstreeDeployment *new_deployment; + gint new_deployserial; + gs_unref_object OstreeDeployment *new_deployment = NULL; gs_unref_object OstreeDeployment *merge_deployment = NULL; gs_unref_object OstreeRepo *repo = NULL; gs_unref_object GFile *commit_root = NULL; @@ -1158,7 +1058,6 @@ ostree_sysroot_deploy_one_tree (OstreeSysroot *self, gs_unref_object GFile *new_deployment_path = NULL; gs_free char *new_bootcsum = NULL; gs_unref_object OstreeBootconfigParser *bootconfig = NULL; - gs_unref_ptrarray GPtrArray *new_deployments = NULL; g_return_val_if_fail (osname != NULL || self->booted_deployment != NULL, FALSE); @@ -1168,19 +1067,6 @@ ostree_sysroot_deploy_one_tree (OstreeSysroot *self, if (!ostree_sysroot_get_repo (self, &repo, cancellable, error)) goto out; - /* Here we perform cleanup of any leftover data from previous - * partial failures. This avoids having to call gs_shutil_rm_rf() - * at random points throughout the process. - * - * TODO: Add /ostree/transaction file, and only do this cleanup if - * we find it. - */ - if (!ostree_sysroot_cleanup (self, cancellable, error)) - { - g_prefix_error (error, "Performing initial cleanup: "); - goto out; - } - if (!ostree_repo_read_commit (repo, revision, &commit_root, NULL, cancellable, error)) goto out; @@ -1199,26 +1085,17 @@ ostree_sysroot_deploy_one_tree (OstreeSysroot *self, goto out; } - /* If we're booted into the OS into which we're deploying, then - * merge the currently *booted* configuration, rather than the most - * recently deployed. - */ if (provided_merge_deployment != NULL) merge_deployment = g_object_ref (provided_merge_deployment); - else - merge_deployment = ostree_sysroot_get_merge_deployment (self, osname); - compute_new_deployment_list (self->bootversion, - self->deployments, osname, - self->booted_deployment, merge_deployment, - retain, - revision, new_bootcsum, - &new_deployments); - new_deployment = g_object_ref (new_deployments->pdata[0]); + if (!allocate_deployserial (self, osname, revision, &new_deployserial, + cancellable, error)) + goto out; + + new_deployment = ostree_deployment_new (0, osname, revision, new_deployserial, + new_bootcsum, -1); ostree_deployment_set_origin (new_deployment, origin); - print_deployment_diff (self->deployments, new_deployments); - /* Check out the userspace tree onto the filesystem */ if (!checkout_deployment_tree (self, repo, new_deployment, &new_deployment_path, cancellable, error)) @@ -1274,34 +1151,6 @@ ostree_sysroot_deploy_one_tree (OstreeSysroot *self, ostree_bootconfig_parser_set (bootconfig, "options", new_options); } - if (!ostree_sysroot_write_deployments (self, new_deployments, cancellable, error)) - goto out; - - g_print ("Transaction complete, performing cleanup\n"); - - /* TEMPORARY HACK: Add a "current" symbolic link that's easy to - * follow inside the gnome-ostree build scripts. This isn't atomic, - * but that doesn't matter because it's only used by deployments - * done from the host. - */ - { - gs_unref_object GFile *osdir = ot_gfile_resolve_path_printf (self->path, "ostree/deploy/%s", ostree_deployment_get_osname (new_deployment)); - gs_unref_object GFile *os_current_path = g_file_get_child (osdir, "current"); - gs_free char *target = g_file_get_relative_path (osdir, new_deployment_path); - g_assert (target != NULL); - if (!ot_gfile_atomic_symlink_swap (os_current_path, target, - cancellable, error)) - goto out; - } - - /* And finally, cleanup of any leftover data. - */ - if (!ostree_sysroot_cleanup (self, cancellable, error)) - { - g_prefix_error (error, "Performing final cleanup: "); - goto out; - } - ret = TRUE; ot_transfer_out_value (out_new_deployment, &new_deployment); out: diff --git a/src/libostree/ostree-sysroot-private.h b/src/libostree/ostree-sysroot-private.h index 864d3cd8..ce79fb8e 100644 --- a/src/libostree/ostree-sysroot-private.h +++ b/src/libostree/ostree-sysroot-private.h @@ -60,6 +60,12 @@ _ostree_sysroot_parse_deploy_path_name (const char *name, int *out_serial, GError **error); +gboolean +_ostree_sysroot_list_deployment_dirs_for_os (GFile *osdir, + GPtrArray *inout_deployments, + GCancellable *cancellable, + GError **error); + gboolean _ostree_sysroot_get_devino (GFile *path, guint32 *out_device, diff --git a/src/libostree/ostree-sysroot.c b/src/libostree/ostree-sysroot.c index bf064d5a..92d7b035 100644 --- a/src/libostree/ostree-sysroot.c +++ b/src/libostree/ostree-sysroot.c @@ -1070,6 +1070,10 @@ ostree_sysroot_get_merge_deployment (OstreeSysroot *self, if (osname == NULL) osname = ostree_deployment_get_osname (self->booted_deployment); + /* If we're booted into the OS into which we're deploying, then + * merge the currently *booted* configuration, rather than the most + * recently deployed. + */ if (self->booted_deployment && g_strcmp0 (ostree_deployment_get_osname (self->booted_deployment), osname) == 0) { diff --git a/src/libostree/ostree-sysroot.h b/src/libostree/ostree-sysroot.h index d8959c4c..a61f491d 100644 --- a/src/libostree/ostree-sysroot.h +++ b/src/libostree/ostree-sysroot.h @@ -76,7 +76,6 @@ gboolean ostree_sysroot_deploy_one_tree (OstreeSysroot *self, const char *revision, GKeyFile *origin, char **add_kernel_argv, - gboolean retain, OstreeDeployment *provided_merge_deployment, OstreeDeployment **out_new_deployment, GCancellable *cancellable, diff --git a/src/ostree/ot-admin-builtin-deploy.c b/src/ostree/ot-admin-builtin-deploy.c index 9761f864..5e94e909 100644 --- a/src/ostree/ot-admin-builtin-deploy.c +++ b/src/ostree/ot-admin-builtin-deploy.c @@ -54,6 +54,7 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeSysroot *sysroot, GCancell gs_unref_object OstreeRepo *repo = NULL; gs_unref_ptrarray GPtrArray *new_deployments = NULL; gs_unref_object OstreeDeployment *new_deployment = NULL; + gs_unref_object OstreeDeployment *merge_deployment = NULL; gs_free char *revision = NULL; context = g_option_context_new ("REFSPEC - Checkout revision REFSPEC as the new default deployment"); @@ -102,14 +103,33 @@ ot_admin_builtin_deploy (int argc, char **argv, OstreeSysroot *sysroot, GCancell if (!ostree_repo_resolve_rev (repo, refspec, FALSE, &revision, error)) goto out; + merge_deployment = ostree_sysroot_get_merge_deployment (sysroot, opt_osname); + + /* Here we perform cleanup of any leftover data from previous + * partial failures. This avoids having to call gs_shutil_rm_rf() + * at random points throughout the process. + * + * TODO: Add /ostree/transaction file, and only do this cleanup if + * we find it. + */ + if (!ostree_sysroot_cleanup (sysroot, cancellable, error)) + { + g_prefix_error (error, "Performing initial cleanup: "); + goto out; + } + if (!ostree_sysroot_deploy_one_tree (sysroot, opt_osname, revision, origin, - opt_kernel_argv, opt_retain, - NULL, + opt_kernel_argv, merge_deployment, &new_deployment, cancellable, error)) goto out; + if (!ot_admin_complete_deploy_one (sysroot, opt_osname, + new_deployment, merge_deployment, opt_retain, + cancellable, error)) + goto out; + ret = TRUE; out: if (origin) diff --git a/src/ostree/ot-admin-builtin-upgrade.c b/src/ostree/ot-admin-builtin-upgrade.c index 73b1bf57..3beb7c92 100644 --- a/src/ostree/ot-admin-builtin-upgrade.c +++ b/src/ostree/ot-admin-builtin-upgrade.c @@ -125,14 +125,33 @@ ot_admin_builtin_upgrade (int argc, char **argv, OstreeSysroot *sysroot, GCancel else { gs_unref_object GFile *real_sysroot = g_file_new_for_path ("/"); + + /* Here we perform cleanup of any leftover data from previous + * partial failures. This avoids having to call gs_shutil_rm_rf() + * at random points throughout the process. + * + * TODO: Add /ostree/transaction file, and only do this cleanup if + * we find it. + */ + if (!ostree_sysroot_cleanup (sysroot, cancellable, error)) + { + g_prefix_error (error, "Performing initial cleanup: "); + goto out; + } + if (!ostree_sysroot_deploy_one_tree (sysroot, opt_osname, new_revision, origin, - NULL, FALSE, + NULL, merge_deployment, &new_deployment, cancellable, error)) goto out; + if (!ot_admin_complete_deploy_one (sysroot, opt_osname, + new_deployment, merge_deployment, FALSE, + cancellable, error)) + goto out; + if (opt_reboot && g_file_equal (ostree_sysroot_get_path (sysroot), real_sysroot)) { gs_subprocess_simple_run_sync (NULL, GS_SUBPROCESS_STREAM_DISPOSITION_INHERIT, diff --git a/src/ostree/ot-admin-functions.c b/src/ostree/ot-admin-functions.c index 90f3a4ec..3ca7508c 100644 --- a/src/ostree/ot-admin-functions.c +++ b/src/ostree/ot-admin-functions.c @@ -56,3 +56,57 @@ ot_admin_require_booted_deployment_or_osname (OstreeSysroot *sysroot, out: return ret; } + +gboolean +ot_admin_complete_deploy_one (OstreeSysroot *sysroot, + const char *osname, + OstreeDeployment *new_deployment, + OstreeDeployment *merge_deployment, + gboolean opt_retain, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + guint i; + OstreeDeployment *booted_deployment = NULL; + gs_unref_ptrarray GPtrArray *deployments = NULL; + gs_unref_ptrarray GPtrArray *new_deployments = g_ptr_array_new_with_free_func (g_object_unref); + + deployments = ostree_sysroot_get_deployments (sysroot); + booted_deployment = ostree_sysroot_get_booted_deployment (sysroot); + + g_ptr_array_add (new_deployments, g_object_ref (new_deployment)); + + for (i = 0; i < deployments->len; i++) + { + OstreeDeployment *deployment = deployments->pdata[i]; + + /* Keep deployments with different osnames, as well as the + * booted and merge deployments + */ + if (opt_retain || + strcmp (ostree_deployment_get_osname (deployment), osname) != 0 || + ostree_deployment_equal (deployment, booted_deployment) || + ostree_deployment_equal (deployment, merge_deployment)) + { + g_ptr_array_add (new_deployments, g_object_ref (deployment)); + } + else + { + g_print ("ostadmin: Will delete deployment osname=%s %s.%u\n", + ostree_deployment_get_osname (deployment), + ostree_deployment_get_csum (deployment), + ostree_deployment_get_deployserial (deployment)); + } + } + + if (!ostree_sysroot_write_deployments (sysroot, new_deployments, cancellable, error)) + goto out; + + if (!ostree_sysroot_cleanup (sysroot, cancellable, error)) + goto out; + + ret = TRUE; + out: + return ret; +} diff --git a/src/ostree/ot-admin-functions.h b/src/ostree/ot-admin-functions.h index 8b9758d6..e0a824d2 100644 --- a/src/ostree/ot-admin-functions.h +++ b/src/ostree/ot-admin-functions.h @@ -35,5 +35,14 @@ ot_admin_require_booted_deployment_or_osname (OstreeSysroot *sysroot, GCancellable *cancellable, GError **error); +gboolean +ot_admin_complete_deploy_one (OstreeSysroot *sysroot, + const char *osname, + OstreeDeployment *new_deployment, + OstreeDeployment *merge_deployment, + gboolean opt_retain, + GCancellable *cancellable, + GError **error); + G_END_DECLS diff --git a/tests/test-admin-deploy-1.sh b/tests/test-admin-deploy-1.sh index 66139d54..02f5b208 100755 --- a/tests/test-admin-deploy-1.sh +++ b/tests/test-admin-deploy-1.sh @@ -143,17 +143,12 @@ ostree admin --sysroot=sysroot status echo "ok upgrade" +assert_file_has_content sysroot/ostree/deploy/testos/deploy/${rev}.0/etc/os-release 'NAME=TestOS' assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/etc/os-release 'NAME=TestOS' -assert_file_has_content sysroot/ostree/deploy/testos/deploy/${origrev}.4/etc/os-release 'NAME=TestOS' -ostree admin --sysroot=sysroot undeploy 2 +ostree admin --sysroot=sysroot undeploy 1 assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/etc/os-release 'NAME=TestOS' -assert_not_has_dir sysroot/ostree/deploy/testos/deploy/${origrev}.4 +assert_not_has_dir sysroot/ostree/deploy/testos/deploy/${rev}.0 -assert_file_has_content sysroot/ostree/deploy/testos/deploy/${origrev}.3/etc/os-release 'NAME=TestOS' -ostree admin --sysroot=sysroot undeploy 2 -assert_not_has_dir sysroot/ostree/deploy/testos/deploy/${origrev}.3 - -assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/etc/os-release 'NAME=TestOS' ostree admin --sysroot=sysroot undeploy 0 assert_not_has_dir sysroot/ostree/deploy/testos/deploy/${newrev}.0 ostree admin --sysroot=sysroot status