[ostree] Add an OstreeSysrootUpgrader API



commit 7baa600e237b326899de2899a9bc54a6b863943c
Author: Colin Walters <walters verbum org>
Date:   Sun Mar 23 08:54:28 2014 -0400

    Add an OstreeSysrootUpgrader API
    
    This moves some utility code from the ostree tool into the shared
    library, which will make it easier to consume by external tools.

 Makefile-libostree-defines.am           |    1 +
 Makefile-libostree.am                   |    1 +
 src/libostree/ostree-sysroot-upgrader.c |  496 +++++++++++++++++++++++++++++++
 src/libostree/ostree-sysroot-upgrader.h |   71 +++++
 src/libostree/ostree-types.h            |    1 +
 src/libostree/ostree.h                  |    1 +
 src/ostree/ot-admin-builtin-switch.c    |  141 ++++------
 src/ostree/ot-admin-builtin-upgrade.c   |  119 +-------
 src/ostree/ot-admin-functions.c         |   60 ----
 src/ostree/ot-admin-functions.h         |   11 -
 10 files changed, 645 insertions(+), 257 deletions(-)
---
diff --git a/Makefile-libostree-defines.am b/Makefile-libostree-defines.am
index 02037ed..7f776e0 100644
--- a/Makefile-libostree-defines.am
+++ b/Makefile-libostree-defines.am
@@ -29,6 +29,7 @@ libostree_public_headers = \
        src/libostree/ostree-diff.h \
        src/libostree/ostree-sepolicy.h \
        src/libostree/ostree-sysroot.h \
+       src/libostree/ostree-sysroot-upgrader.h \
        src/libostree/ostree-deployment.h \
        src/libostree/ostree-bootconfig-parser.h \
        $(NULL)
diff --git a/Makefile-libostree.am b/Makefile-libostree.am
index 04d03be..988f1cb 100644
--- a/Makefile-libostree.am
+++ b/Makefile-libostree.am
@@ -61,6 +61,7 @@ libostree_1_la_SOURCES = \
        src/libostree/ostree-sysroot.c \
        src/libostree/ostree-sysroot-cleanup.c \
        src/libostree/ostree-sysroot-deploy.c \
+       src/libostree/ostree-sysroot-upgrader.c \
        src/libostree/ostree-bootconfig-parser.c \
        src/libostree/ostree-deployment.c \
        src/libostree/ostree-bootloader.h \
diff --git a/src/libostree/ostree-sysroot-upgrader.c b/src/libostree/ostree-sysroot-upgrader.c
new file mode 100644
index 0000000..c565434
--- /dev/null
+++ b/src/libostree/ostree-sysroot-upgrader.c
@@ -0,0 +1,496 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 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 "otutil.h"
+#include "libgsystem.h"
+
+#include "ostree-sysroot-upgrader.h"
+
+/**
+ * SECTION:libostree-sysroot-upgrader
+ * @title: Simple upgrade class
+ * @short_description: Upgrade OSTree systems
+ *
+ * The #OstreeSysrootUpgrader class allows performing simple upgrade
+ * operations.
+ */
+typedef struct {
+  GObjectClass parent_class;
+} OstreeSysrootUpgraderClass;
+
+struct OstreeSysrootUpgrader {
+  GObject parent;
+
+  OstreeSysroot *sysroot;
+  char *osname;
+
+  OstreeDeployment *merge_deployment;
+  GKeyFile *origin;
+  char *origin_remote;
+  char *origin_ref;
+
+  char *new_revision;
+}; 
+
+enum {
+  PROP_0,
+
+  PROP_SYSROOT,
+  PROP_OSNAME
+};
+
+static void ostree_sysroot_upgrader_initable_iface_init (GInitableIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (OstreeSysrootUpgrader, ostree_sysroot_upgrader, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, 
ostree_sysroot_upgrader_initable_iface_init))
+
+static gboolean
+parse_refspec (OstreeSysrootUpgrader  *self,
+               GCancellable           *cancellable,
+               GError                **error)
+{
+  gboolean ret = FALSE;
+  gs_free char *origin_refspec = NULL;
+
+  origin_refspec = g_key_file_get_string (self->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 upgrade via ostree");
+      goto out;
+    }
+  g_clear_pointer (&self->origin_remote, g_free);
+  g_clear_pointer (&self->origin_ref, g_free);
+  if (!ostree_parse_refspec (origin_refspec,
+                             &self->origin_remote, 
+                             &self->origin_ref,
+                             error))
+    goto out;
+  
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+ostree_sysroot_upgrader_initable_init (GInitable        *initable,
+                                       GCancellable     *cancellable,
+                                       GError          **error)
+{
+  gboolean ret = FALSE;
+  OstreeSysrootUpgrader *self = (OstreeSysrootUpgrader*)initable;
+  OstreeDeployment *booted_deployment =
+    ostree_sysroot_get_booted_deployment (self->sysroot);
+  gs_unref_object GFile *deployment_path = NULL;
+  gs_unref_object GFile *deployment_origin_path = NULL;
+
+  if (booted_deployment == NULL && self->osname == NULL)
+    {
+      g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                           "Not currently booted into an OSTree system and no OS specified");
+      goto out;
+    }
+
+  if (self->osname == NULL)
+    {
+      g_assert (booted_deployment);
+      self->osname = g_strdup (ostree_deployment_get_osname (booted_deployment));
+    }
+
+  self->merge_deployment = ostree_sysroot_get_merge_deployment (self->sysroot, self->osname); 
+  if (self->merge_deployment == NULL)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "No previous deployment for OS '%s'", self->osname);
+      goto out;
+    }
+
+  deployment_path = ostree_sysroot_get_deployment_directory (self->sysroot, self->merge_deployment);
+  deployment_origin_path = ostree_sysroot_get_deployment_origin_path (deployment_path);
+
+  self->origin = ostree_deployment_get_origin (self->merge_deployment);
+  if (!self->origin)
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "No origin known for deployment %s.%d",
+                   ostree_deployment_get_csum (self->merge_deployment),
+                   ostree_deployment_get_deployserial (self->merge_deployment));
+      goto out;
+    }
+
+  if (!parse_refspec (self, cancellable, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static void
+ostree_sysroot_upgrader_initable_iface_init (GInitableIface *iface)
+{
+  iface->init = ostree_sysroot_upgrader_initable_init;
+}
+
+static void
+ostree_sysroot_upgrader_finalize (GObject *object)
+{
+  OstreeSysrootUpgrader *self = OSTREE_SYSROOT_UPGRADER (object);
+
+  g_clear_object (&self->sysroot);
+  g_free (self->osname);
+
+  g_clear_object (&self->merge_deployment);
+  if (self->origin)
+    g_key_file_unref (self->origin);
+  g_free (self->origin_remote);
+  g_free (self->origin_ref);
+
+  G_OBJECT_CLASS (ostree_sysroot_upgrader_parent_class)->finalize (object);
+}
+
+static void
+ostree_sysroot_upgrader_set_property (GObject         *object,
+                                      guint            prop_id,
+                                      const GValue    *value,
+                                      GParamSpec      *pspec)
+{
+  OstreeSysrootUpgrader *self = OSTREE_SYSROOT_UPGRADER (object);
+
+  switch (prop_id)
+    {
+    case PROP_SYSROOT:
+      self->sysroot = g_value_dup_object (value);
+      break;
+    case PROP_OSNAME:
+      self->osname = g_value_dup_string (value);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+ostree_sysroot_upgrader_get_property (GObject         *object,
+                                      guint            prop_id,
+                                      GValue          *value,
+                                      GParamSpec      *pspec)
+{
+  OstreeSysrootUpgrader *self = OSTREE_SYSROOT_UPGRADER (object);
+
+  switch (prop_id)
+    {
+    case PROP_SYSROOT:
+      g_value_set_object (value, self->sysroot);
+      break;
+    case PROP_OSNAME:
+      g_value_set_string (value, self->osname);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+ostree_sysroot_upgrader_constructed (GObject *object)
+{
+  OstreeSysrootUpgrader *self = OSTREE_SYSROOT_UPGRADER (object);
+
+  g_assert (self->sysroot != NULL);
+
+  G_OBJECT_CLASS (ostree_sysroot_upgrader_parent_class)->constructed (object);
+}
+
+static void
+ostree_sysroot_upgrader_class_init (OstreeSysrootUpgraderClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->constructed = ostree_sysroot_upgrader_constructed;
+  object_class->get_property = ostree_sysroot_upgrader_get_property;
+  object_class->set_property = ostree_sysroot_upgrader_set_property;
+  object_class->finalize = ostree_sysroot_upgrader_finalize;
+
+  g_object_class_install_property (object_class,
+                                   PROP_SYSROOT,
+                                   g_param_spec_object ("sysroot", "", "",
+                                                        OSTREE_TYPE_SYSROOT,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+  g_object_class_install_property (object_class,
+                                   PROP_OSNAME,
+                                   g_param_spec_string ("osname", "", "", NULL,
+                                                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
+
+static void
+ostree_sysroot_upgrader_init (OstreeSysrootUpgrader *self)
+{
+}
+
+/**
+ * ostree_sysroot_upgrader_new:
+ * @sysroot: An #OstreeSysroot
+ *
+ * Returns: (transfer full): An upgrader
+ */
+OstreeSysrootUpgrader*
+ostree_sysroot_upgrader_new (OstreeSysroot *sysroot,
+                             GCancellable  *cancellable,
+                             GError       **error)
+{
+  return g_initable_new (OSTREE_TYPE_SYSROOT_UPGRADER, cancellable, error,
+                         "sysroot", sysroot, NULL);
+}
+
+/**
+ * ostree_sysroot_upgrader_new_for_os:
+ * @sysroot: An #OstreeSysroot
+ * @osname: (allow-none): Operating system name
+ *
+ * Returns: (transfer full): An upgrader
+ */
+OstreeSysrootUpgrader*
+ostree_sysroot_upgrader_new_for_os (OstreeSysroot *sysroot,
+                                    const char    *osname,
+                                    GCancellable  *cancellable,
+                                    GError       **error)
+{
+  return g_initable_new (OSTREE_TYPE_SYSROOT_UPGRADER, cancellable, error,
+                       "sysroot", sysroot, "osname", osname, NULL);
+}
+
+/**
+ * ostree_sysroot_upgrader_get_origin:
+ * @self: Sysroot
+ *
+ * Returns: (transfer none): The origin file, or %NULL if unknown
+ */
+GKeyFile *
+ostree_sysroot_upgrader_get_origin (OstreeSysrootUpgrader *self)
+{
+  return self->origin;
+}
+
+/**
+ * ostree_sysroot_upgrader_set_origin:
+ * @self: Sysroot
+ * @origin: (allow-none): The new origin
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Replace the origin with @origin.
+ */
+gboolean
+ostree_sysroot_upgrader_set_origin (OstreeSysrootUpgrader *self,
+                                    GKeyFile              *origin,
+                                    GCancellable          *cancellable,
+                                    GError               **error)
+{
+  gboolean ret = FALSE;
+
+  g_clear_pointer (&self->origin, g_key_file_unref);
+  if (origin)
+    {
+      self->origin = g_key_file_ref (origin);
+      if (!parse_refspec (self, cancellable, error))
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+/**
+ * ostree_sysroot_upgrader_check_timestamps:
+ * @repo: Repo
+ * @from_rev: From revision
+ * @to_rev: To revision
+ * @error: Error
+ *
+ * Check that the timestamp on @to_rev is equal to or newer than
+ * @from_rev.  This protects systems against man-in-the-middle
+ * attackers which provide a client with an older commit.
+ */
+gboolean
+ostree_sysroot_upgrader_check_timestamps (OstreeRepo     *repo,
+                                          const char     *from_rev,
+                                          const char     *to_rev,
+                                          GError        **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_variant GVariant *old_commit = NULL;
+  gs_unref_variant GVariant *new_commit = NULL;
+
+  if (!ostree_repo_load_variant (repo,
+                                 OSTREE_OBJECT_TYPE_COMMIT,
+                                 from_rev,
+                                 &old_commit,
+                                 error))
+    goto out;
+  
+  if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
+                                 to_rev, &new_commit,
+                                 error))
+    goto out;
+
+  if (ostree_commit_get_timestamp (old_commit) > ostree_commit_get_timestamp (new_commit))
+    {
+      GDateTime *old_ts = g_date_time_new_from_unix_utc (ostree_commit_get_timestamp (old_commit));
+      GDateTime *new_ts = g_date_time_new_from_unix_utc (ostree_commit_get_timestamp (new_commit));
+      gs_free char *old_ts_str = NULL;
+      gs_free char *new_ts_str = NULL;
+
+      g_assert (old_ts);
+      g_assert (new_ts);
+      old_ts_str = g_date_time_format (old_ts, "%c");
+      new_ts_str = g_date_time_format (new_ts, "%c");
+      g_date_time_unref (old_ts);
+      g_date_time_unref (new_ts);
+
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Upgrade target revision '%s' with timestamp '%s' is chronologically older than current 
revision '%s' with timestamp '%s'; use --allow-downgrade to permit",
+                   to_rev, new_ts_str, from_rev, old_ts_str);
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+
+/**
+ * ostree_sysroot_upgrader_pull:
+ * @self: Upgrader
+ * @flags: Flags controlling pull behavior
+ * @upgrader_flags: Flags controlling upgrader behavior
+ * @progress: (allow-none): Progress
+ * @out_changed: (out): Whether or not the origin changed
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Perform a pull from the origin.  First check if the ref has
+ * changed, if so download the linked objects, and store the updated
+ * ref locally.  Then @out_changed will be %TRUE.
+ *
+ * If the origin remote is unchanged, @out_changed will be set to
+ * %FALSE.
+ */
+gboolean
+ostree_sysroot_upgrader_pull (OstreeSysrootUpgrader  *self,
+                              OstreeRepoPullFlags     flags,
+                              OstreeSysrootUpgraderPullFlags     upgrader_flags,
+                              OstreeAsyncProgress    *progress,
+                              gboolean               *out_changed,
+                              GCancellable           *cancellable,
+                              GError                **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_object OstreeRepo *repo = NULL;
+  char *refs_to_fetch[] = { self->origin_ref, NULL };
+  gs_free char *from_revision = NULL;
+  gs_free char *new_revision = NULL;
+  gs_free char *origin_refspec = NULL;
+
+  if (!ostree_sysroot_get_repo (self->sysroot, &repo, cancellable, error))
+    goto out;
+
+  if (self->origin_remote)
+    origin_refspec = g_strconcat (self->origin_remote, ":", self->origin_ref, NULL);
+  else
+    origin_refspec = g_strdup (self->origin_ref);
+
+  if (!ostree_repo_resolve_rev (repo, origin_refspec, TRUE, &from_revision,
+                                error))
+    goto out;
+
+  if (!ostree_repo_pull (repo, self->origin_remote, refs_to_fetch,
+                         flags, progress,
+                         cancellable, error))
+    goto out;
+
+  if (!ostree_repo_resolve_rev (repo, origin_refspec, FALSE, &self->new_revision,
+                                error))
+    goto out;
+
+  if (g_strcmp0 (from_revision, self->new_revision) == 0)
+    {
+      *out_changed = FALSE;
+    }
+  else
+    {
+      *out_changed = TRUE;
+      if (from_revision)
+        {
+          if (!ostree_sysroot_upgrader_check_timestamps (repo, from_revision,
+                                                         self->new_revision,
+                                                         error))
+            goto out;
+        }
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+/**
+ * ostree_sysroot_upgrader_deploy:
+ * @self: Self
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Write the new deployment to disk, perform a configuration merge
+ * with /etc, and update the bootloader configuration.
+ */
+gboolean
+ostree_sysroot_upgrader_deploy (OstreeSysrootUpgrader  *self,
+                                GCancellable           *cancellable,
+                                GError                **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_object OstreeDeployment *new_deployment = NULL;
+
+  if (!ostree_sysroot_deploy_tree (self->sysroot, self->osname,
+                                   self->new_revision,
+                                   self->origin,
+                                   self->merge_deployment,
+                                   NULL,
+                                   &new_deployment,
+                                   cancellable, error))
+    goto out;
+
+  if (!ostree_sysroot_simple_write_deployment (self->sysroot, self->osname,
+                                               new_deployment,
+                                               self->merge_deployment,
+                                               0,
+                                               cancellable, error))
+    goto out;
+
+  ret = TRUE;
+ out:
+  return ret;
+}
diff --git a/src/libostree/ostree-sysroot-upgrader.h b/src/libostree/ostree-sysroot-upgrader.h
new file mode 100644
index 0000000..79f3635
--- /dev/null
+++ b/src/libostree/ostree-sysroot-upgrader.h
@@ -0,0 +1,71 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
+ *
+ * Copyright (C) 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.
+ */
+
+#pragma once
+
+#include "ostree-sysroot.h"
+
+G_BEGIN_DECLS
+
+#define OSTREE_TYPE_SYSROOT_UPGRADER ostree_sysroot_upgrader_get_type()
+#define OSTREE_SYSROOT_UPGRADER(obj) \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj), OSTREE_TYPE_SYSROOT_UPGRADER, OstreeSysrootUpgrader))
+#define OSTREE_IS_SYSROOT_UPGRADER(obj) \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj), OSTREE_TYPE_SYSROOT_UPGRADER))
+
+GType ostree_sysroot_upgrader_get_type (void);
+
+OstreeSysrootUpgrader *ostree_sysroot_upgrader_new (OstreeSysroot *sysroot,
+                                                    GCancellable  *cancellable,
+                                                    GError       **error);
+
+OstreeSysrootUpgrader *ostree_sysroot_upgrader_new_for_os (OstreeSysroot *sysroot,
+                                                           const char    *osname,
+                                                           GCancellable  *cancellable,
+                                                           GError       **error);
+
+GKeyFile *ostree_sysroot_upgrader_get_origin (OstreeSysrootUpgrader *self);
+gboolean ostree_sysroot_upgrader_set_origin (OstreeSysrootUpgrader *self, GKeyFile *origin,
+                                             GCancellable *cancellable, GError **error);
+
+gboolean ostree_sysroot_upgrader_check_timestamps (OstreeRepo     *repo,
+                                                   const char     *from_rev,
+                                                   const char     *to_rev,
+                                                   GError        **error);
+
+typedef enum {
+  OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_NONE = 0,
+  OSTREE_SYSROOT_UPGRADER_PULL_FLAGS_ALLOW_OLDER = (1 << 0)
+} OstreeSysrootUpgraderPullFlags;
+
+gboolean ostree_sysroot_upgrader_pull (OstreeSysrootUpgrader  *self,
+                                       OstreeRepoPullFlags     flags,
+                                       OstreeSysrootUpgraderPullFlags     upgrader_flags,
+                                       OstreeAsyncProgress    *progress,
+                                       gboolean               *out_changed,
+                                       GCancellable           *cancellable,
+                                       GError                **error);
+
+gboolean ostree_sysroot_upgrader_deploy (OstreeSysrootUpgrader  *self,
+                                         GCancellable           *cancellable,
+                                         GError                **error);
+
+G_END_DECLS
+
diff --git a/src/libostree/ostree-types.h b/src/libostree/ostree-types.h
index 8be1258..533d3b1 100644
--- a/src/libostree/ostree-types.h
+++ b/src/libostree/ostree-types.h
@@ -29,6 +29,7 @@ G_BEGIN_DECLS
 typedef struct OstreeRepo OstreeRepo;
 typedef struct OstreeSePolicy OstreeSePolicy;
 typedef struct OstreeSysroot OstreeSysroot;
+typedef struct OstreeSysrootUpgrader OstreeSysrootUpgrader;
 typedef struct OstreeMutableTree OstreeMutableTree;
 typedef struct OstreeRepoFile OstreeRepoFile;
 
diff --git a/src/libostree/ostree.h b/src/libostree/ostree.h
index f1e7450..57b2ffd 100644
--- a/src/libostree/ostree.h
+++ b/src/libostree/ostree.h
@@ -26,6 +26,7 @@
 #include <ostree-mutable-tree.h>
 #include <ostree-repo-file.h>
 #include <ostree-sysroot.h>
+#include <ostree-sysroot-upgrader.h>
 #include <ostree-deployment.h>
 #include <ostree-bootconfig-parser.h>
 #include <ostree-diff.h>
diff --git a/src/ostree/ot-admin-builtin-switch.c b/src/ostree/ot-admin-builtin-switch.c
index 1edd7f5..d9d6ee0 100644
--- a/src/ostree/ot-admin-builtin-switch.c
+++ b/src/ostree/ot-admin-builtin-switch.c
@@ -46,14 +46,21 @@ ot_admin_builtin_switch (int argc, char **argv, OstreeSysroot *sysroot, GCancell
   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_refspec = 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;
+  gs_unref_object OstreeSysrootUpgrader *upgrader = NULL;
+  gs_unref_object OstreeAsyncProgress *progress = NULL;
+  gboolean changed;
+  GSConsole *console;
+  GKeyFile *old_origin;
+  GKeyFile *new_origin = NULL;
 
   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);
@@ -72,108 +79,74 @@ ot_admin_builtin_switch (int argc, char **argv, OstreeSysroot *sysroot, GCancell
   if (!ostree_sysroot_load (sysroot, cancellable, error))
     goto out;
 
-  if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
+  upgrader = ostree_sysroot_upgrader_new_for_os (sysroot, opt_osname,
+                                                 cancellable, error);
+  if (!upgrader)
     goto out;
 
-  if (!ot_admin_deploy_prepare (sysroot, opt_osname, &merge_deployment,
-                                &origin_remote, &origin_ref,
-                                &origin,
-                                cancellable, error))
+  old_origin = ostree_sysroot_upgrader_get_origin (upgrader);
+  origin_refspec = g_key_file_get_string (old_origin, "origin", "refspec", NULL);
+  
+  if (!ostree_parse_refspec (origin_refspec, &origin_remote, &origin_ref, error))
     goto out;
+  
+  if (origin_remote)
+    new_refspec = g_strconcat (origin_remote, ":", new_ref, NULL);
+  else
+    new_refspec = g_strdup (new_ref);
 
-  if (strcmp (origin_ref, new_ref) == 0)
+  if (strcmp (origin_refspec, new_refspec) == 0)
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "Old and new refs are equal: %s", new_ref);
+                   "Old and new refs are equal: %s", new_refspec);
       goto out;
     }
 
-  {
-    gs_free char *new_refspec = NULL;
-    if (origin_remote)
-      new_refspec = g_strconcat (origin_remote, ":", new_ref, NULL);
-    else
-      new_refspec = g_strdup (new_ref);
-    g_key_file_unref (origin);
-    origin = ostree_sysroot_origin_new_from_refspec (sysroot, new_refspec);
-  }
+  new_origin = ostree_sysroot_origin_new_from_refspec (sysroot, new_refspec);
+  if (!ostree_sysroot_upgrader_set_origin (upgrader, new_origin, cancellable, error))
+    goto out;
 
-  if (origin_remote)
+  console = gs_console_get ();
+  if (console)
     {
-      OstreeRepoPullFlags pullflags = 0;
-      char *refs_to_fetch[] = { (char*)new_ref, NULL };
-      GSConsole *console;
-      gs_unref_object OstreeAsyncProgress *progress = NULL;
-
-      g_print ("Fetching remote %s ref %s\n", origin_remote, new_ref);
-
-      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);
-        }
-
-      if (!ostree_repo_pull (repo, origin_remote, refs_to_fetch, pullflags, progress,
-                             cancellable, error))
-        goto out;
+      gs_console_begin_status_line (console, "", NULL, NULL);
+      progress = ostree_async_progress_new_and_connect (ot_common_pull_progress, console);
     }
 
-  if (!ostree_repo_resolve_rev (repo, new_ref, FALSE, &new_revision,
-                                error))
+  if (!ostree_sysroot_upgrader_pull (upgrader, 0, 0, progress, &changed,
+                                     cancellable, error))
     goto out;
 
-  if (TRUE)
-    {
-      gs_unref_object GFile *real_sysroot = g_file_new_for_path ("/");
+  if (!ostree_sysroot_upgrader_deploy (upgrader, cancellable, error))
+    goto out;
+
+  if (!ostree_sysroot_get_repo (sysroot, &repo, 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;
+  
+  {
+    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 (!ostree_sysroot_simple_write_deployment (sysroot, opt_osname,
-                                                   new_deployment,
-                                                   merge_deployment,
-                                                   0,
-                                                   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);
-        }
-    }
+    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 (new_origin)
+    g_key_file_unref (new_origin);
   if (context)
     g_option_context_free (context);
   return ret;
diff --git a/src/ostree/ot-admin-builtin-upgrade.c b/src/ostree/ot-admin-builtin-upgrade.c
index badaf2e..f852711 100644
--- a/src/ostree/ot-admin-builtin-upgrade.c
+++ b/src/ostree/ot-admin-builtin-upgrade.c
@@ -49,7 +49,7 @@ ot_admin_builtin_upgrade (int argc, char **argv, OstreeSysroot *sysroot, GCancel
 {
   gboolean ret = FALSE;
   GOptionContext *context;
-  gs_unref_object OstreeRepo *repo = NULL;
+  gs_unref_object OstreeSysrootUpgrader *upgrader = NULL;
   gs_free char *origin_remote = NULL;
   gs_free char *origin_ref = NULL;
   gs_free char *origin_refspec = NULL;
@@ -58,7 +58,9 @@ ot_admin_builtin_upgrade (int argc, char **argv, OstreeSysroot *sysroot, GCancel
   gs_unref_object GFile *deployment_origin_path = NULL;
   gs_unref_object OstreeDeployment *merge_deployment = NULL;
   gs_unref_object OstreeDeployment *new_deployment = NULL;
-  GKeyFile *origin;
+  GSConsole *console;
+  gs_unref_object OstreeAsyncProgress *progress = NULL;
+  gboolean changed;
 
   context = g_option_context_new ("Construct new tree from current origin and deploy it, if it changed");
   g_option_context_add_main_entries (context, options, NULL);
@@ -69,118 +71,31 @@ ot_admin_builtin_upgrade (int argc, char **argv, OstreeSysroot *sysroot, GCancel
   if (!ostree_sysroot_load (sysroot, cancellable, error))
     goto out;
 
-
-  if (!ostree_sysroot_get_repo (sysroot, &repo, cancellable, error))
-    goto out;
-
-  if (!ot_admin_deploy_prepare (sysroot, opt_osname, &merge_deployment,
-                                &origin_remote, &origin_ref,
-                                &origin,
-                                cancellable, error))
+  upgrader = ostree_sysroot_upgrader_new_for_os (sysroot, opt_osname,
+                                                 cancellable, error);
+  if (!upgrader)
     goto out;
 
-  if (origin_remote)
+  console = gs_console_get ();
+  if (console)
     {
-      OstreeRepoPullFlags pullflags = 0;
-      char *refs_to_fetch[] = { origin_ref, NULL };
-      GSConsole *console;
-      gs_unref_object OstreeAsyncProgress *progress = NULL;
-
-      g_print ("Fetching remote %s ref %s\n", origin_remote, origin_ref);
-
-      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);
-        }
-
-      if (!ostree_repo_pull (repo, origin_remote, refs_to_fetch, pullflags, progress,
-                             cancellable, error))
-        goto out;
-      
-      origin_refspec = g_strconcat (origin_remote, ":", origin_ref, NULL);
+      gs_console_begin_status_line (console, "", NULL, NULL);
+      progress = ostree_async_progress_new_and_connect (ot_common_pull_progress, console);
     }
-  else
-    origin_refspec = g_strdup (origin_ref);
 
-
-  if (!ostree_repo_resolve_rev (repo, origin_refspec, FALSE, &new_revision,
-                                error))
+  if (!ostree_sysroot_upgrader_pull (upgrader, 0, 0, progress, &changed,
+                                     cancellable, error))
     goto out;
-  
-  if (strcmp (ostree_deployment_get_csum (merge_deployment), new_revision) == 0)
+
+  if (!changed)
     {
-      g_print ("Refspec %s is unchanged\n", origin_refspec);
+      g_print ("No update available.\n");
     }
   else
     {
       gs_unref_object GFile *real_sysroot = g_file_new_for_path ("/");
 
-      if (!opt_allow_downgrade)
-        {
-          const char *old_revision = ostree_deployment_get_csum (merge_deployment);
-          gs_unref_variant GVariant *old_commit = NULL;
-          gs_unref_variant GVariant *new_commit = NULL;
-
-          if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
-                                         old_revision,
-                                         &old_commit,
-                                         error))
-            goto out;
-          
-          if (!ostree_repo_load_variant (repo, OSTREE_OBJECT_TYPE_COMMIT,
-                                         new_revision, &new_commit,
-                                         error))
-            goto out;
-
-          if (ostree_commit_get_timestamp (old_commit) > ostree_commit_get_timestamp (new_commit))
-            {
-              GDateTime *old_ts = g_date_time_new_from_unix_utc (ostree_commit_get_timestamp (old_commit));
-              GDateTime *new_ts = g_date_time_new_from_unix_utc (ostree_commit_get_timestamp (new_commit));
-              gs_free char *old_ts_str = NULL;
-              gs_free char *new_ts_str = NULL;
-
-              g_assert (old_ts);
-              g_assert (new_ts);
-              old_ts_str = g_date_time_format (old_ts, "%c");
-              new_ts_str = g_date_time_format (new_ts, "%c");
-              g_date_time_unref (old_ts);
-              g_date_time_unref (new_ts);
-
-              g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                           "Upgrade target revision '%s' with timestamp '%s' is chronologically older than 
current revision '%s' with timestamp '%s'; use --allow-downgrade to permit",
-                           new_revision, new_ts_str, old_revision, old_ts_str);
-              goto out;
-            }
-        }
-      
-      /* 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 (!ostree_sysroot_simple_write_deployment (sysroot, opt_osname,
-                                                   new_deployment,
-                                                   merge_deployment,
-                                                   0,
-                                                   cancellable, error))
+      if (!ostree_sysroot_upgrader_deploy (upgrader, cancellable, error))
         goto out;
 
       if (opt_reboot && g_file_equal (ostree_sysroot_get_path (sysroot), real_sysroot))
diff --git a/src/ostree/ot-admin-functions.c b/src/ostree/ot-admin-functions.c
index 198f3e4..34e5c6b 100644
--- a/src/ostree/ot-admin-functions.c
+++ b/src/ostree/ot-admin-functions.c
@@ -48,63 +48,3 @@ ot_admin_require_booted_deployment_or_osname (OstreeSysroot       *sysroot,
  out:
   return ret;
 }
-
-gboolean
-ot_admin_deploy_prepare (OstreeSysroot      *sysroot,
-                         const char         *osname,
-                         OstreeDeployment  **out_merge_deployment,
-                         char              **out_origin_remote,
-                         char              **out_origin_ref,
-                         GKeyFile          **out_origin,
-                         GCancellable        *cancellable,
-                         GError             **error)
-{
-  gboolean ret = FALSE;
-  gs_free char *origin_refspec = NULL;
-  gs_free char *origin_remote = NULL;
-  gs_free char *origin_ref = NULL;
-  gs_unref_object GFile *deployment_path = NULL;
-  gs_unref_object GFile *deployment_origin_path = NULL;
-  gs_unref_object OstreeDeployment *merge_deployment = NULL;
-  GKeyFile *origin;
-
-  if (!ot_admin_require_booted_deployment_or_osname (sysroot, osname,
-                                                     cancellable, error))
-    goto out;
-  merge_deployment = ostree_sysroot_get_merge_deployment (sysroot, osname); 
-  if (merge_deployment == NULL)
-    {
-      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
-                   "No previous deployment for OS '%s'", osname);
-      goto out;
-    }
-
-  deployment_path = ostree_sysroot_get_deployment_directory (sysroot, merge_deployment);
-  deployment_origin_path = ostree_sysroot_get_deployment_origin_path (deployment_path);
-
-  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 upgrade via ostree");
-      goto out;
-    }
-  if (!ostree_parse_refspec (origin_refspec, &origin_remote, &origin_ref, error))
-    goto out;
-
-  ret = TRUE;
-  gs_transfer_out_value (out_merge_deployment, &merge_deployment);
-  gs_transfer_out_value (out_origin_remote, &origin_remote);
-  gs_transfer_out_value (out_origin_ref, &origin_ref);
-  gs_transfer_out_value (out_origin, &origin);
- out:
-  g_clear_pointer (&origin, g_key_file_unref);
-  return ret;
-}
diff --git a/src/ostree/ot-admin-functions.h b/src/ostree/ot-admin-functions.h
index 8d90371..ab83036 100644
--- a/src/ostree/ot-admin-functions.h
+++ b/src/ostree/ot-admin-functions.h
@@ -32,16 +32,5 @@ ot_admin_require_booted_deployment_or_osname (OstreeSysroot       *sysroot,
                                               const char          *osname,
                                               GCancellable        *cancellable,
                                               GError             **error);
-
-gboolean
-ot_admin_deploy_prepare (OstreeSysroot      *sysroot,
-                         const char         *osname,
-                         OstreeDeployment  **merge_deployment,
-                         char              **origin_remote,
-                         char              **origin_ref,
-                         GKeyFile          **out_origin,
-                         GCancellable        *cancellable,
-                         GError             **error);
-
 G_END_DECLS
 



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