From 3d7bff2d4110b7e2cd8495c3870a523faa3e7224 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 23 Jul 2013 09:19:24 -0400 Subject: [PATCH] admin: Add an "undeploy" command Otherwise it's really easy to keep accumulating deployments. Also, we may want to run this after rebooting, so we're back down to one operating system. --- Makefile-ostree.am | 1 + src/ostree/ot-admin-builtin-undeploy.c | 120 ++++++++++++++++++++ src/ostree/ot-admin-builtins.h | 1 + src/ostree/ot-admin-deploy.c | 146 ++++++++++++++----------- src/ostree/ot-admin-deploy.h | 7 ++ src/ostree/ot-builtin-admin.c | 1 + tests/test-admin-deploy-1.sh | 19 +++- 7 files changed, 231 insertions(+), 64 deletions(-) create mode 100644 src/ostree/ot-admin-builtin-undeploy.c diff --git a/Makefile-ostree.am b/Makefile-ostree.am index 4c3ade90..949641e6 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -48,6 +48,7 @@ ostree_SOURCES += \ src/ostree/ot-admin-builtin-init-fs.c \ src/ostree/ot-admin-builtin-diff.c \ src/ostree/ot-admin-builtin-deploy.c \ + src/ostree/ot-admin-builtin-undeploy.c \ src/ostree/ot-admin-builtin-cleanup.c \ src/ostree/ot-admin-builtin-os-init.c \ src/ostree/ot-admin-builtin-status.c \ diff --git a/src/ostree/ot-admin-builtin-undeploy.c b/src/ostree/ot-admin-builtin-undeploy.c new file mode 100644 index 00000000..29221890 --- /dev/null +++ b/src/ostree/ot-admin-builtin-undeploy.c @@ -0,0 +1,120 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2012,2013 Colin Walters + * + * This library 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 License, 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 "ot-admin-builtins.h" +#include "ot-admin-functions.h" +#include "ot-admin-deploy.h" +#include "ot-ordered-hash.h" +#include "ostree.h" + +#include + +static GOptionEntry options[] = { + { NULL } +}; + +gboolean +ot_admin_builtin_undeploy (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error) +{ + gboolean ret = FALSE; + __attribute__((unused)) GCancellable *cancellable = NULL; + GFile *sysroot = admin_opts->sysroot; + GOptionContext *context; + const char *deploy_index_str; + int deploy_index; + int current_bootversion; + gs_unref_ptrarray GPtrArray *current_deployments = NULL; + gs_unref_ptrarray GPtrArray *new_deployments = NULL; + gs_free char *revision = NULL; + gs_unref_object OtDeployment *booted_deployment = NULL; + gs_unref_object OtDeployment *target_deployment = NULL; + + context = g_option_context_new ("INDEX - Delete deployment INDEX"); + + g_option_context_add_main_entries (context, options, NULL); + + if (!g_option_context_parse (context, &argc, &argv, error)) + goto out; + + if (argc < 2) + { + ot_util_usage_error (context, "INDEX must be specified", error); + goto out; + } + + deploy_index_str = argv[1]; + deploy_index = atoi (deploy_index_str); + + if (!ot_admin_list_deployments (sysroot, ¤t_bootversion, ¤t_deployments, + cancellable, error)) + { + g_prefix_error (error, "While listing deployments: "); + goto out; + } + + if (!ot_admin_find_booted_deployment (sysroot, current_deployments, &booted_deployment, + cancellable, error)) + goto out; + + if (deploy_index < 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "Invalid index %d", deploy_index); + goto out; + } + if (deploy_index >= current_deployments->len) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "Out of range index %d, expected < %d", deploy_index, current_deployments->len); + goto out; + } + + target_deployment = g_object_ref (current_deployments->pdata[deploy_index]); + if (target_deployment == booted_deployment) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + "Cannot undeploy currently booted deployment %i", deploy_index); + goto out; + } + + g_ptr_array_remove_index (current_deployments, deploy_index); + + if (!ot_admin_write_deployments (sysroot, current_bootversion, + current_bootversion ? 0 : 1, current_deployments, + cancellable, error)) + goto out; + + g_print ("Deleted deployment %s.%d\n", ot_deployment_get_csum (target_deployment), + ot_deployment_get_deployserial (target_deployment)); + + if (!ot_admin_cleanup (sysroot, cancellable, error)) + { + g_prefix_error (error, "Performing final cleanup: "); + goto out; + } + + ret = TRUE; + out: + if (context) + g_option_context_free (context); + return ret; +} diff --git a/src/ostree/ot-admin-builtins.h b/src/ostree/ot-admin-builtins.h index e6dc264d..440ba0f4 100644 --- a/src/ostree/ot-admin-builtins.h +++ b/src/ostree/ot-admin-builtins.h @@ -33,6 +33,7 @@ typedef struct { gboolean ot_admin_builtin_os_init (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error); gboolean ot_admin_builtin_install (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error); gboolean ot_admin_builtin_init_fs (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error); +gboolean ot_admin_builtin_undeploy (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error); gboolean ot_admin_builtin_deploy (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error); gboolean ot_admin_builtin_cleanup (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error); gboolean ot_admin_builtin_status (int argc, char **argv, OtAdminBuiltinOpts *admin_opts, GError **error); diff --git a/src/ostree/ot-admin-deploy.c b/src/ostree/ot-admin-deploy.c index f77a73a7..c7421b06 100644 --- a/src/ostree/ot-admin-deploy.c +++ b/src/ostree/ot-admin-deploy.c @@ -999,6 +999,86 @@ swap_bootloader (GFile *sysroot, return ret; } +gboolean +ot_admin_write_deployments (GFile *sysroot, + int current_bootversion, + int new_bootversion, + GPtrArray *new_deployments, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + guint i; + gs_unref_object OtBootloader *bootloader = NULL; + + bootloader = ot_admin_query_bootloader (sysroot); + if (!bootloader) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "No known bootloader configuration detected"); + goto out; + } + + if (current_bootversion == new_bootversion) + { + if (!full_system_sync (cancellable, error)) + { + g_prefix_error (error, "Full sync: "); + goto out; + } + + if (!swap_bootlinks (sysroot, current_bootversion, + new_deployments, + cancellable, error)) + { + g_prefix_error (error, "Swapping current bootlinks: "); + goto out; + } + } + else + { + for (i = 0; i < new_deployments->len; i++) + { + OtDeployment *deployment = new_deployments->pdata[i]; + if (!install_deployment_kernel (sysroot, new_bootversion, deployment, + cancellable, error)) + { + g_prefix_error (error, "Installing kernel: "); + goto out; + } + } + + /* Swap bootlinks for *new* version */ + if (!swap_bootlinks (sysroot, new_bootversion, new_deployments, + cancellable, error)) + { + g_prefix_error (error, "Generating new bootlinks: "); + goto out; + } + + if (!full_system_sync (cancellable, error)) + { + g_prefix_error (error, "Full sync: "); + goto out; + } + + if (!ot_bootloader_write_config (bootloader, new_bootversion, + cancellable, error)) + goto out; + + if (!swap_bootloader (sysroot, current_bootversion, new_bootversion, + cancellable, error)) + { + g_prefix_error (error, "Final bootloader swap: "); + goto out; + } + } + + ret = TRUE; + out: + return ret; +} + gboolean ot_admin_deploy (GFile *sysroot, int current_bootversion, @@ -1019,7 +1099,6 @@ ot_admin_deploy (GFile *sysroot, gboolean ret = FALSE; OtDeployment *new_deployment; gs_unref_object OtDeployment *merge_deployment = NULL; - gs_unref_object OtBootloader *bootloader = NULL; gs_unref_object GFile *rootfs = NULL; gs_unref_object OstreeRepo *repo = NULL; gs_unref_object GFile *commit_root = NULL; @@ -1071,14 +1150,6 @@ ot_admin_deploy (GFile *sysroot, goto out; } - bootloader = ot_admin_query_bootloader (sysroot); - if (!bootloader) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "No known bootloader configuration detected"); - 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. @@ -1156,60 +1227,9 @@ ot_admin_deploy (GFile *sysroot, ot_config_parser_set (bootconfig, "options", new_options); } - if (current_bootversion == new_bootversion) - { - if (!full_system_sync (cancellable, error)) - { - g_prefix_error (error, "Full sync: "); - goto out; - } - - if (!swap_bootlinks (sysroot, current_bootversion, - new_deployments, - cancellable, error)) - { - g_prefix_error (error, "Swapping current bootlinks: "); - goto out; - } - } - else - { - for (i = 0; i < new_deployments->len; i++) - { - OtDeployment *deployment = new_deployments->pdata[i]; - if (!install_deployment_kernel (sysroot, new_bootversion, deployment, - cancellable, error)) - { - g_prefix_error (error, "Installing kernel: "); - goto out; - } - } - - /* Swap bootlinks for *new* version */ - if (!swap_bootlinks (sysroot, new_bootversion, new_deployments, - cancellable, error)) - { - g_prefix_error (error, "Generating new bootlinks: "); - goto out; - } - - if (!full_system_sync (cancellable, error)) - { - g_prefix_error (error, "Full sync: "); - goto out; - } - - if (!ot_bootloader_write_config (bootloader, new_bootversion, - cancellable, error)) - goto out; - - if (!swap_bootloader (sysroot, current_bootversion, new_bootversion, - cancellable, error)) - { - g_prefix_error (error, "Final bootloader swap: "); - goto out; - } - } + if (!ot_admin_write_deployments (sysroot, current_bootversion, new_bootversion, + new_deployments, cancellable, error)) + goto out; g_print ("Transaction complete, performing cleanup\n"); diff --git a/src/ostree/ot-admin-deploy.h b/src/ostree/ot-admin-deploy.h index d5dfa833..ab9f1f57 100644 --- a/src/ostree/ot-admin-deploy.h +++ b/src/ostree/ot-admin-deploy.h @@ -29,6 +29,13 @@ G_BEGIN_DECLS +gboolean ot_admin_write_deployments (GFile *sysroot, + int current_bootversion, + int new_bootversion, + GPtrArray *new_deployments, + GCancellable *cancellable, + GError **error); + gboolean ot_admin_deploy (GFile *sysroot, int current_bootversion, GPtrArray *current_deployments, diff --git a/src/ostree/ot-builtin-admin.c b/src/ostree/ot-builtin-admin.c index 46656806..c591c015 100644 --- a/src/ostree/ot-builtin-admin.c +++ b/src/ostree/ot-builtin-admin.c @@ -40,6 +40,7 @@ static OstreeAdminCommand admin_subcommands[] = { { "os-init", ot_admin_builtin_os_init }, { "init-fs", ot_admin_builtin_init_fs }, { "deploy", ot_admin_builtin_deploy }, + { "undeploy", ot_admin_builtin_undeploy }, { "upgrade", ot_admin_builtin_upgrade }, { "cleanup", ot_admin_builtin_cleanup }, { "status", ot_admin_builtin_status }, diff --git a/tests/test-admin-deploy-1.sh b/tests/test-admin-deploy-1.sh index 32cca0c4..0119586b 100755 --- a/tests/test-admin-deploy-1.sh +++ b/tests/test-admin-deploy-1.sh @@ -27,7 +27,7 @@ setup_os_repository "archive-z2" echo "ok setup" -echo "1..7" +echo "1..8" ostree --repo=sysroot/ostree/repo pull-local --remote=testos testos-repo testos/buildmaster/x86_64-runtime rev=$(ostree --repo=sysroot/ostree/repo rev-parse testos/buildmaster/x86_64-runtime) @@ -126,9 +126,26 @@ echo "ok upgrade bare" os_repository_new_commit ostree --repo=sysroot/ostree/repo remote add testos file://$(pwd)/testos-repo testos/buildmaster/x86_64-runtime ostree admin --sysroot=sysroot upgrade --os=testos +origrev=${rev} rev=${newrev} newrev=$(ostree --repo=sysroot/ostree/repo rev-parse testos/buildmaster/x86_64-runtime) assert_not_streq ${rev} ${newrev} assert_file_has_content sysroot/ostree/deploy/testos/deploy/${newrev}.0/etc/os-release 'NAME=TestOS' echo "ok upgrade" + +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 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_file_has_content sysroot/ostree/deploy/testos/deploy/${origrev}.3/etc/os-release 'NAME=TestOS' +ostree admin --sysroot=sysroot undeploy 3 +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 + +echo "ok undeploy"