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:
parent
67823beb1f
commit
3d7bff2d41
|
|
@ -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 \
|
||||
|
|
|
|||
|
|
@ -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, ¤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;
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
Loading…
Reference in New Issue