[ostree] admin/switch: New builtin to switch between trees



commit 878a43411eb170149dfeb681f516afe919ed29d4
Author: Colin Walters <walters verbum org>
Date:   Sat Jan 18 17:42:24 2014 -0500

    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(+), 0 deletions(-)
---
diff --git a/Makefile-ostree.am b/Makefile-ostree.am
index bb1ea50..fa12055 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 3c155ec..ac63d23 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 0000000..e524de8
--- /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 <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-builtins-common.h"
+#include "ostree.h"
+#include "otutil.h"
+#include "libgsystem.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+
+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 eaea2af..e25a520 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 93d2efa..e4ff7fe 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 8c5a784..57365cb 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 0000000..815ca69
--- /dev/null
+++ b/tests/test-admin-deploy-switch.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+#
+# Copyright (C) 2011,2014 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.
+
+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"


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]