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.
This commit is contained in:
Colin Walters 2013-07-23 09:19:24 -04:00
parent 67823beb1f
commit 3d7bff2d41
7 changed files with 231 additions and 64 deletions

View File

@ -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 \

View File

@ -0,0 +1,120 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright (C) 2012,2013 Colin Walters <walters@verbum.org>
*
* 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 <stdlib.h>
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, &current_bootversion, &current_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;
}

View File

@ -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);

View File

@ -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");

View File

@ -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,

View File

@ -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 },

View File

@ -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"