From 878a43411eb170149dfeb681f516afe919ed29d4 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Sat, 18 Jan 2014 17:42:24 -0500 Subject: [PATCH] admin/switch: New builtin to switch between trees This is something I want to make easier, as it better showcases the flexibility of OSTree. --- Makefile-ostree.am | 1 + Makefile-tests.am | 1 + src/ostree/ot-admin-builtin-switch.c | 198 +++++++++++++++++++++++++++ src/ostree/ot-admin-builtins.h | 1 + src/ostree/ot-builtin-admin.c | 1 + tests/libtest.sh | 7 + tests/test-admin-deploy-switch.sh | 44 ++++++ 7 files changed, 253 insertions(+) create mode 100644 src/ostree/ot-admin-builtin-switch.c create mode 100644 tests/test-admin-deploy-switch.sh diff --git a/Makefile-ostree.am b/Makefile-ostree.am index bb1ea506..fa120556 100644 --- a/Makefile-ostree.am +++ b/Makefile-ostree.am @@ -58,6 +58,7 @@ ostree_SOURCES += \ src/ostree/ot-admin-builtin-cleanup.c \ src/ostree/ot-admin-builtin-os-init.c \ src/ostree/ot-admin-builtin-status.c \ + src/ostree/ot-admin-builtin-switch.c \ src/ostree/ot-admin-builtin-upgrade.c \ src/ostree/ot-admin-builtins.h \ src/ostree/ot-admin-functions.h \ diff --git a/Makefile-tests.am b/Makefile-tests.am index 3c155ecb..ac63d23a 100644 --- a/Makefile-tests.am +++ b/Makefile-tests.am @@ -35,6 +35,7 @@ testfiles = test-basic \ test-admin-deploy-syslinux \ test-admin-deploy-2 \ test-admin-deploy-karg \ + test-admin-deploy-switch \ test-admin-deploy-etcmerge-cornercases \ test-admin-deploy-uboot \ test-setuid \ diff --git a/src/ostree/ot-admin-builtin-switch.c b/src/ostree/ot-admin-builtin-switch.c new file mode 100644 index 00000000..e524de85 --- /dev/null +++ b/src/ostree/ot-admin-builtin-switch.c @@ -0,0 +1,198 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- + * + * Copyright (C) 2012,2014 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-builtins-common.h" +#include "ostree.h" +#include "otutil.h" +#include "libgsystem.h" + +#include +#include +#include + +static gboolean opt_reboot; +static char *opt_osname; + +static GOptionEntry options[] = { + { "os", 0, 0, G_OPTION_ARG_STRING, &opt_osname, "Specify operating system root to use", NULL }, + { NULL } +}; + +gboolean +ot_admin_builtin_switch (int argc, char **argv, OstreeSysroot *sysroot, GCancellable *cancellable, GError **error) +{ + gboolean ret = FALSE; + GOptionContext *context; + const char *new_ref = NULL; + gs_unref_object OstreeRepo *repo = NULL; + gs_free char *origin_refspec = NULL; + gs_free char *origin_remote = NULL; + gs_free char *origin_ref = NULL; + gs_free char *new_revision = NULL; + gs_unref_object GFile *deployment_path = NULL; + gs_unref_object GFile *deployment_origin_path = NULL; + gs_unref_object OstreeDeployment *merge_deployment = NULL; + gs_unref_object OstreeDeployment *new_deployment = NULL; + GKeyFile *origin; + + context = g_option_context_new ("REF - Construct new tree from current origin and deploy it, if it changed"); + 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, "REF must be specified", error); + goto out; + } + + new_ref = argv[1]; + + if (!ostree_sysroot_load (sysroot, cancellable, error)) + goto out; + + if (!ot_admin_require_booted_deployment_or_osname (sysroot, opt_osname, + cancellable, error)) + goto out; + merge_deployment = ostree_sysroot_get_merge_deployment (sysroot, opt_osname); + if (merge_deployment == NULL) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "No previous deployment for OS '%s'", opt_osname); + goto out; + } + + deployment_path = ostree_sysroot_get_deployment_directory (sysroot, merge_deployment); + deployment_origin_path = ostree_sysroot_get_deployment_origin_path (deployment_path); + + if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error)) + goto out; + + origin = ostree_deployment_get_origin (merge_deployment); + if (!origin) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "No origin known for current deployment"); + goto out; + } + origin_refspec = g_key_file_get_string (origin, "origin", "refspec", NULL); + if (!origin_refspec) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "No origin/refspec in current deployment origin; cannot change via ostree"); + goto out; + } + if (!ostree_parse_refspec (origin_refspec, &origin_remote, &origin_ref, error)) + goto out; + + if (strcmp (origin_ref, new_ref) == 0) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Old and new refs are equal: %s", new_ref); + goto out; + } + + if (origin_remote) + { + OstreeRepoPullFlags pullflags = 0; + char *refs_to_fetch[] = { (char*)new_ref, NULL }; + GSConsole *console; + gs_unref_object OstreeAsyncProgress *progress = NULL; + + console = gs_console_get (); + if (console) + { + gs_console_begin_status_line (console, "", NULL, NULL); + progress = ostree_async_progress_new_and_connect (ot_common_pull_progress, console); + } + + g_print ("Fetching remote %s ref %s\n", origin_remote, new_ref); + + if (!ostree_repo_pull (repo, origin_remote, refs_to_fetch, pullflags, progress, + cancellable, error)) + goto out; + } + + if (!ostree_repo_resolve_rev (repo, new_ref, FALSE, &new_revision, + error)) + goto out; + + if (strcmp (ostree_deployment_get_csum (merge_deployment), new_revision) == 0) + { + g_print ("Refspec %s is unchanged\n", origin_refspec); + } + 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_tree (sysroot, + opt_osname, new_revision, origin, + merge_deployment, + NULL, + &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 (!ostree_repo_prepare_transaction (repo, NULL, cancellable, error)) + goto out; + + g_print ("Deleting ref '%s:%s'\n", origin_remote, origin_ref); + ostree_repo_transaction_set_ref (repo, origin_remote, origin_ref, NULL); + + if (!ostree_repo_commit_transaction (repo, NULL, 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, + cancellable, error, + "systemctl", "reboot", NULL); + } + } + + 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 eaea2af4..e25a520a 100644 --- a/src/ostree/ot-admin-builtins.h +++ b/src/ostree/ot-admin-builtins.h @@ -34,6 +34,7 @@ gboolean ot_admin_builtin_deploy (int argc, char **argv, OstreeSysroot *sysroot, gboolean ot_admin_builtin_cleanup (int argc, char **argv, OstreeSysroot *sysroot, GCancellable *cancellable, GError **error); gboolean ot_admin_builtin_status (int argc, char **argv, OstreeSysroot *sysroot, GCancellable *cancellable, GError **error); gboolean ot_admin_builtin_diff (int argc, char **argv, OstreeSysroot *sysroot, GCancellable *cancellable, GError **error); +gboolean ot_admin_builtin_switch (int argc, char **argv, OstreeSysroot *sysroot, GCancellable *cancellable, GError **error); gboolean ot_admin_builtin_upgrade (int argc, char **argv, OstreeSysroot *sysroot, GCancellable *cancellable, GError **error); G_END_DECLS diff --git a/src/ostree/ot-builtin-admin.c b/src/ostree/ot-builtin-admin.c index 93d2efa7..e4ff7fe2 100644 --- a/src/ostree/ot-builtin-admin.c +++ b/src/ostree/ot-builtin-admin.c @@ -45,6 +45,7 @@ static OstreeAdminCommand admin_subcommands[] = { { "upgrade", ot_admin_builtin_upgrade }, { "cleanup", ot_admin_builtin_cleanup }, { "status", ot_admin_builtin_status }, + { "switch", ot_admin_builtin_switch }, { "config-diff", ot_admin_builtin_diff }, { NULL, NULL } }; diff --git a/tests/libtest.sh b/tests/libtest.sh index 8c5a7841..57365cbb 100644 --- a/tests/libtest.sh +++ b/tests/libtest.sh @@ -226,6 +226,13 @@ EOF echo "a new executable" > usr/bin/sh ostree --repo=${test_tmpdir}/testos-repo commit -b testos/buildmaster/x86_64-runtime -s "Build" + cd ${test_tmpdir} + cp -a osdata osdata-devel + cd osdata-devel + mkdir -p usr/include + echo "a development header" > usr/include/foo.h + ostree --repo=${test_tmpdir}/testos-repo commit -b testos/buildmaster/x86_64-devel -s "Build" + ostree --repo=${test_tmpdir}/testos-repo fsck -q cd ${test_tmpdir} diff --git a/tests/test-admin-deploy-switch.sh b/tests/test-admin-deploy-switch.sh new file mode 100644 index 00000000..815ca69c --- /dev/null +++ b/tests/test-admin-deploy-switch.sh @@ -0,0 +1,44 @@ +#!/bin/bash +# +# Copyright (C) 2011,2014 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. + +set -e + +. $(dirname $0)/libtest.sh + +echo "1..1" + +setup_os_repository "archive-z2" "syslinux" + +echo "ok setup" + +echo "1..2" + +ostree --repo=sysroot/ostree/repo remote add --set=gpg-verify=false testos file://$(pwd)/testos-repo +ostree --repo=sysroot/ostree/repo pull testos testos/buildmaster/x86_64-runtime +ostree admin --sysroot=sysroot deploy --karg=root=LABEL=MOO --karg=quiet --os=testos testos:testos/buildmaster/x86_64-runtime +assert_not_has_file sysroot/ostree/deploy/testos/current/usr/include/foo.h +if ostree admin --sysroot=sysroot switch --os=testos testos/buildmaster/x86_64-runtime; then + assert_not_reached "Switch to same ref unexpectedly succeeded" +fi +echo "ok switch expected error" + +ostree admin --sysroot=sysroot switch --os=testos testos/buildmaster/x86_64-devel +assert_file_has_content sysroot/ostree/deploy/testos/current/usr/include/foo.h 'header' + +echo "ok switch"