[ostree/wip/metalinks] A bit more metalink work



commit 0882a36e56f80dd5bc1e30c99292834432e87769
Author: Colin Walters <walters verbum org>
Date:   Mon Aug 18 17:29:33 2014 -0400

    A bit more metalink work

 Makefile-ostree.am                |    1 +
 src/libostree/ostree-metalink.c   |  219 ++++++++++++++++++++++++++++++-------
 src/libostree/ostree-metalink.h   |   10 +-
 src/libostree/ostree-repo-pull.c  |    2 +-
 src/libostree/ostree-repo.c       |    9 +-
 src/libotutil/ot-checksum-utils.c |   26 +++++
 src/libotutil/ot-checksum-utils.h |    5 +
 src/ostree/main.c                 |    1 +
 src/ostree/ot-builtin-summary.c   |   64 +++++++++++
 src/ostree/ot-builtins.h          |    1 +
 tests/test-pull-metalink.sh       |   68 ++++++++++++
 11 files changed, 358 insertions(+), 48 deletions(-)
---
diff --git a/Makefile-ostree.am b/Makefile-ostree.am
index f1381ac..76df368 100644
--- a/Makefile-ostree.am
+++ b/Makefile-ostree.am
@@ -40,6 +40,7 @@ ostree_SOURCES = src/ostree/main.c \
        src/ostree/ot-builtin-remote.c \
        src/ostree/ot-builtin-reset.c \
        src/ostree/ot-builtin-rev-parse.c \
+       src/ostree/ot-builtin-summary.c \
        src/ostree/ot-builtin-show.c \
        src/ostree/ot-builtin-static-delta.c \
        src/ostree/ot-main.h \
diff --git a/src/libostree/ostree-metalink.c b/src/libostree/ostree-metalink.c
index d84a577..2c0541e 100644
--- a/src/libostree/ostree-metalink.c
+++ b/src/libostree/ostree-metalink.c
@@ -36,25 +36,29 @@ typedef enum {
   OSTREE_METALINK_STATE_RESOURCES,
   OSTREE_METALINK_STATE_URL,
 
-  OSTREE_METALINK_STATE_PASSTHROUGH, /* Ignoring unknown elements */
-  OSTREE_METALINK_STATE_ERROR
+  OSTREE_METALINK_STATE_PASSTHROUGH /* Ignoring unknown elements */
 } OstreeMetalinkState;
 
 struct OstreeMetalink
 {
   GObject parent_instance;
 
-  OstreeMetalink *fetcher;
+  SoupURI *uri;
+
+  OstreeFetcher *fetcher;
   char *requested_file;
   guint64 max_size;
 };
 
 G_DEFINE_TYPE (OstreeMetalink, _ostree_metalink, G_TYPE_OBJECT)
 
-struct OstreeMetalinkRequest
+typedef struct
 {
   OstreeMetalink *metalink;
 
+  GTask *task;
+  GMarkupParseContext *parser;
+
   guint passthrough_depth;
   OstreeMetalinkState passthrough_previous;
   
@@ -62,14 +66,20 @@ struct OstreeMetalinkRequest
   guint found_our_file_element : 1;
   guint verification_known : 1;
 
+  GChecksumType in_verification_type;
+
   guint64 size;
   char *verification_sha256;
   char *verification_sha512;
 
+  GFile *result;
+
+  char *last_metalink_error;
+  guint current_url_index;
   GPtrArray *urls;
 
   OstreeMetalinkState state;
-}
+} OstreeMetalinkRequest;
 
 static void
 state_transition (OstreeMetalinkRequest  *self,
@@ -116,7 +126,7 @@ metalink_parser_start (GMarkupParseContext  *context,
       /* If we've already processed a <file> element we're OK with, just
        * ignore the others.
        */
-      if (self->urls->length > 0)
+      if (self->urls->len > 0)
         {
           state_transition (self, OSTREE_METALINK_STATE_PASSTHROUGH);
         }
@@ -124,7 +134,6 @@ metalink_parser_start (GMarkupParseContext  *context,
         {
           const char *file_name;
 
-          g_clear_pointer (&self->file_name, g_free);
           if (!g_markup_collect_attributes (element_name,
                                             attribute_names,
                                             attribute_values,
@@ -137,7 +146,7 @@ metalink_parser_start (GMarkupParseContext  *context,
 
           self->found_a_file_element = TRUE;
 
-          if (strcmp (file_name, self->requested_file) != 0)
+          if (strcmp (file_name, self->metalink->requested_file) != 0)
             {
               state_transition (self, OSTREE_METALINK_STATE_PASSTHROUGH);
               g_assert (self->passthrough_depth == 0);
@@ -183,9 +192,9 @@ metalink_parser_start (GMarkupParseContext  *context,
           /* Only accept sha256/sha512 */
           self->verification_known = TRUE;
           if (strcmp (verification_type_str, "sha256") == 0)
-            self->verification_type = G_CHECKSUM_SHA256;
+            self->in_verification_type = G_CHECKSUM_SHA256;
           else if (strcmp (verification_type_str, "sha512") == 0)
-            self->verification_type = G_CHECKSUM_SHA512;
+            self->in_verification_type = G_CHECKSUM_SHA512;
           else
             self->verification_known = FALSE;
         }
@@ -231,7 +240,6 @@ metalink_parser_start (GMarkupParseContext  *context,
         }
       else
         unknown_element (self, element_name, error);
-      }
       break;
     case OSTREE_METALINK_STATE_URL:
       unknown_element (self, element_name, error);
@@ -251,7 +259,6 @@ metalink_parser_end (GMarkupParseContext  *context,
                      gpointer              user_data,
                      GError              **error)
 {
-  OstreeMetalinkRequest *self = user_data;
 }
 
 static void
@@ -280,9 +287,11 @@ metalink_parser_text (GMarkupParseContext *context,
       }
       break;
     case OSTREE_METALINK_STATE_VERIFICATION:
+      break;
+    case OSTREE_METALINK_STATE_HASH:
       if (self->verification_known)
         {
-          switch (self->verification_type)
+          switch (self->in_verification_type)
             {
             case G_CHECKSUM_SHA256:
               self->verification_sha256 = g_strndup (text, text_len);
@@ -295,12 +304,6 @@ metalink_parser_text (GMarkupParseContext *context,
             }
         }
       break;
-    case OSTREE_METALINK_STATE_HASH:
-      {
-        g_clear_pointer (&self->verification_value, g_free);
-        self->verification_value = g_strndup (text, text_len);
-      }
-      break;
     case OSTREE_METALINK_STATE_RESOURCES:
       break;
     case OSTREE_METALINK_STATE_URL:
@@ -314,8 +317,7 @@ metalink_parser_text (GMarkupParseContext *context,
     case OSTREE_METALINK_STATE_PASSTHROUGH:
       break;
     }
- out:
-  return;
+
 }
 
 static void
@@ -325,6 +327,9 @@ _ostree_metalink_finalize (GObject *object)
 
   self = OSTREE_METALINK (object);
 
+  g_free (self->requested_file);
+  g_clear_object (&self->uri);
+
   G_OBJECT_CLASS (_ostree_metalink_parent_class)->finalize (object);
 }
 
@@ -351,10 +356,14 @@ _ostree_metalink_new (OstreeFetcher  *fetcher,
 
   self->requested_file = g_strdup (requested_file);
   self->max_size = max_size;
+  self->uri = g_object_ref (uri);
  
   return self;
 }
 
+static void
+try_next_url (OstreeMetalinkRequest          *self);
+
 static gboolean
 valid_hex_checksum (const char *s, gsize expected_len)
 {
@@ -363,6 +372,108 @@ valid_hex_checksum (const char *s, gsize expected_len)
   return len == expected_len && s[len] == '\0';
 }
 
+static void
+on_fetched_url (GObject              *src,
+                GAsyncResult         *res,
+                gpointer              user_data)
+{
+  GTask *task = user_data;
+  OstreeMetalinkRequest *self = g_task_get_task_data (task);
+  GError *local_error = NULL;
+  gs_unref_object GFile *result = NULL;
+  gs_unref_object GFileInfo *finfo = NULL;
+
+  result = _ostree_fetcher_request_uri_with_partial_finish ((OstreeFetcher*)src, res, &local_error);
+  if (!result)
+    goto out;
+  
+  finfo = g_file_query_info (result, OSTREE_GIO_FAST_QUERYINFO, 0,
+                             g_task_get_cancellable (task), &local_error);
+  if (!finfo)
+    goto out;
+
+  if (g_file_info_get_size (finfo) != self->size)
+    {
+      g_set_error (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "Expected size is %" G_GUINT64_FORMAT " bytes but content is %" G_GUINT64_FORMAT " bytes",
+                   self->size, g_file_info_get_size (finfo));
+      goto out;
+    }
+  
+  if (self->verification_sha512)
+    {
+      gs_free char *actual = ot_checksum_file (result, G_CHECKSUM_SHA512,
+                                               g_task_get_cancellable (task),
+                                               &local_error);
+      
+      if (!actual)
+        goto out;
+
+      if (strcmp (self->verification_sha512, actual) != 0)
+        {
+          g_set_error (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Expected checksum is %s but actual is %s",
+                       self->verification_sha512, actual);
+          goto out;
+        }
+    }
+
+  if (self->verification_sha256)
+    {
+      gs_free char *actual = ot_checksum_file (result, G_CHECKSUM_SHA256,
+                                               g_task_get_cancellable (task),
+                                               &local_error);
+      
+      if (!actual)
+        goto out;
+
+      if (strcmp (self->verification_sha256, actual) != 0)
+        {
+          g_set_error (&local_error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Expected checksum is %s but actual is %s",
+                       self->verification_sha256, actual);
+          goto out;
+        }
+    }
+
+ out:
+  if (local_error)
+    {
+      g_free (self->last_metalink_error);
+      self->last_metalink_error = g_strdup (local_error->message);
+      g_clear_error (&local_error);
+
+      /* And here we iterate on the next one if we hit an error */
+      self->current_url_index++;
+      try_next_url (self);
+    }
+  else
+    {
+      self->result = g_object_ref (result);
+      g_task_return_boolean (self->task, TRUE);
+    }
+}
+
+static void
+try_next_url (OstreeMetalinkRequest          *self)
+{
+  if (self->current_url_index >= self->urls->len)
+    {
+      g_task_return_new_error (self->task, G_IO_ERROR, G_IO_ERROR_FAILED,
+                               "Exhausted %u metalink targets, last error: %s",
+                               self->urls->len, self->last_metalink_error);
+    }
+  else
+    {
+      SoupURI *next = self->urls->pdata[self->current_url_index];
+      
+      _ostree_fetcher_request_uri_with_partial_async (self->metalink->fetcher, next,
+                                                      self->metalink->max_size,
+                                                      g_task_get_cancellable (self->task),
+                                                      on_fetched_url, self->task);
+    }
+}
+
 static gboolean
 start_target_request_phase (OstreeMetalinkRequest      *self,
                             GError                    **error)
@@ -404,13 +515,14 @@ start_target_request_phase (OstreeMetalinkRequest      *self,
       goto out;
     }
 
-  if (self->urls->length == 0)
+  if (self->urls->len == 0)
     {
       g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
                    "No <url method='http'> elements found");
       goto out;
     }
 
+  try_next_url (self);
   
   ret = TRUE;
  out:
@@ -430,20 +542,22 @@ on_metalink_bytes_read (GObject           *src,
   const guint8 *data;
 
   bytes = g_input_stream_read_bytes_finish ((GInputStream*)src,
-                                            result, error);
+                                            result, &local_error);
   if (!bytes)
     goto out;
   
-  data = g_bytes_get_data (bytes, 
+  data = g_bytes_get_data (bytes, &len);
 
-  if (g_bytes_get_size (bytes) == 0)
+  if (len == 0)
     {
       if (!start_target_request_phase (self, &local_error))
         goto out;
     }
   else
     {
-      g_markup_parse_context_parse (self->parser, g_
+      if (!g_markup_parse_context_parse (self->parser, (const char*)data, len, &local_error))
+        goto out;
+
       g_input_stream_read_bytes_async ((GInputStream*)src, 8192, G_PRIORITY_DEFAULT,
                                        g_task_get_cancellable (task),
                                        on_metalink_bytes_read, task);
@@ -463,7 +577,7 @@ on_retrieved_metalink (GObject           *src,
   GTask *task = user_data;
   gs_unref_object GInputStream *metalink_stream = NULL;
 
-  metalink_stream = ostree_fetcher_stream_uri_finish ((OstreeFetcher*)src, result, &local_error);
+  metalink_stream = _ostree_fetcher_stream_uri_finish ((OstreeFetcher*)src, result, &local_error);
   if (!metalink_stream)
     goto out;
 
@@ -481,10 +595,19 @@ ostree_metalink_request_unref (gpointer data)
 {
   OstreeMetalinkRequest  *request = data;
   g_object_unref (request->metalink);
+  g_clear_object (&request->result);
+  g_free (request->last_metalink_error);
   g_ptr_array_unref (request->urls);
   g_free (request);
 }
                                
+static const GMarkupParser metalink_parser = {
+  metalink_parser_start,
+    metalink_parser_end,
+  metalink_parser_text,
+  NULL,
+  NULL
+};
 
 void
 _ostree_metalink_request_async (OstreeMetalink         *self,
@@ -494,8 +617,13 @@ _ostree_metalink_request_async (OstreeMetalink         *self,
 {
   GTask *task = g_task_new (self, cancellable, callback, user_data);
   OstreeMetalinkRequest *request = g_new0 (OstreeMetalinkRequest, 1);
+
   request->metalink = g_object_ref (self);
   request->urls = g_ptr_array_new_with_free_func (g_free);
+  request->task = task; /* Unowned */
+
+  request->parser = g_markup_parse_context_new (&metalink_parser, G_MARKUP_PREFIX_ERROR_POSITION, task, 
NULL);
+  
   g_task_set_task_data (task, request, ostree_metalink_request_unref);
   _ostree_fetcher_stream_uri_async (self->fetcher, self->uri,
                                     self->max_size, cancellable,
@@ -503,22 +631,36 @@ _ostree_metalink_request_async (OstreeMetalink         *self,
 }
 
 gboolean
-ostree_metalink_request_finish (OstreeMetalink         *self,
-                                GAsyncResult           *result,
-                                SoupURI               **out_target_uri,
-                                GFile                 **out_data,
-                                GError                **error)
+_ostree_metalink_request_finish (OstreeMetalink         *self,
+                                 GAsyncResult           *result,
+                                 SoupURI               **out_target_uri,
+                                 GFile                 **out_data,
+                                 GError                **error)
 {
+  OstreeMetalinkRequest *request;
+
+  g_return_val_if_fail (g_task_is_valid (result, self), FALSE);
+
+  request = g_task_get_task_data ((GTask*)result);
+
+  if (g_task_propagate_boolean ((GTask*)result, error))
+    {
+      *out_target_uri = request->urls->pdata[request->current_url_index];
+      *out_data = g_object_ref (request->result);
+      return TRUE;
+    }
+  else
+    return FALSE;
 }
 
-struct MetalinkSyncCallState
+typedef struct
 {
   gboolean                running;
   gboolean                success;
   SoupURI               **out_target_uri;
   GFile                 **out_data;
   GError                **error;
-}
+} MetalinkSyncCallState;
 
 static void
 on_async_result (GObject          *src,
@@ -527,17 +669,17 @@ on_async_result (GObject          *src,
 {
   MetalinkSyncCallState *state = user_data;
 
-  state->success = ostree_metalink_request_finish ((OstreeMetalink*)src, result,
-                                                   state->out_target_uri, state->out_data,
-                                                   state->error);
+  state->success = _ostree_metalink_request_finish ((OstreeMetalink*)src, result,
+                                                    state->out_target_uri, state->out_data,
+                                                    state->error);
   state->running = FALSE;
 }
 
 gboolean
 _ostree_metalink_request_sync (OstreeMetalink         *self,
-                               GCancellable           *cancellable,
                                SoupURI               **out_target_uri,
                                GFile                 **out_data,
+                               GCancellable           *cancellable,
                                GError                **error)
 {
   gboolean ret = FALSE;
@@ -550,7 +692,6 @@ _ostree_metalink_request_sync (OstreeMetalink         *self,
     g_main_context_iteration (sync_context, TRUE);
 
   ret = state.success;
- out:
   if (sync_context)
     g_main_context_unref (sync_context);
   return ret;
diff --git a/src/libostree/ostree-metalink.h b/src/libostree/ostree-metalink.h
index 72bdfb5..09065b6 100644
--- a/src/libostree/ostree-metalink.h
+++ b/src/libostree/ostree-metalink.h
@@ -60,11 +60,11 @@ void _ostree_metalink_request_async (OstreeMetalink         *self,
                                      GAsyncReadyCallback    callback,
                                      gpointer               user_data);
 
-gboolean_ostree_metalink_request_finish (OstreeMetalink         *self,
-                                         GAsyncResult           *result,
-                                         SoupURI               **out_target_uri,
-                                         GFile                 **out_data,
-                                         GError                **error);
+gboolean _ostree_metalink_request_finish (OstreeMetalink         *self,
+                                          GAsyncResult           *result,
+                                          SoupURI               **out_target_uri,
+                                          GFile                 **out_data,
+                                          GError                **error);
 
 G_END_DECLS
 
diff --git a/src/libostree/ostree-repo-pull.c b/src/libostree/ostree-repo-pull.c
index 7a0917b..000a225 100644
--- a/src/libostree/ostree-repo-pull.c
+++ b/src/libostree/ostree-repo-pull.c
@@ -26,7 +26,7 @@
 #include "ostree-core-private.h"
 #include "ostree-repo-private.h"
 #include "ostree-repo-static-delta-private.h"
-#include "ostree-fetcher.h"
+#include "ostree-metalink.h"
 #include "otutil.h"
 
 typedef struct {
diff --git a/src/libostree/ostree-repo.c b/src/libostree/ostree-repo.c
index a16b049..ec281a4 100644
--- a/src/libostree/ostree-repo.c
+++ b/src/libostree/ostree-repo.c
@@ -379,7 +379,7 @@ GS_DEFINE_CLEANUP_FUNCTION0(GKeyFile*, local_keyfile_unref, g_key_file_unref)
  * ostree_repo_remote_add:
  * @self: Repo
  * @name: Name of remote
- * @url: URL for remote
+ * @url: URL for remote (if URL begins with metalink=, it will be used as such)
  * @options: (allow-none): GVariant of type a{sv}
  * @cancellable: Cancellable
  * @error: Error
@@ -444,7 +444,11 @@ ostree_repo_remote_add (OstreeRepo     *self,
       target_keyfile = ostree_repo_copy_config (self);
     }
 
-  g_key_file_set_string (target_keyfile, section, "url", url);
+  if (g_str_has_prefix (url, "metalink="))
+    g_key_file_set_string (target_keyfile, section, "metalink", url + strlen ("metalink="));
+  else
+    g_key_file_set_string (target_keyfile, section, "url", url);
+
   if (options)
     keyfile_set_from_vardict (target_keyfile, section, options);
 
@@ -2286,7 +2290,6 @@ ostree_repo_regenerate_summary (OstreeRepo     *self,
   GList *ordered_keys = NULL;
   GList *iter = NULL;
   GHashTableIter hashiter;
-  gpointer hkey, hvalue;
 
   if (!ostree_repo_list_refs (self, NULL, &refs, cancellable, error))
     goto out;
diff --git a/src/libotutil/ot-checksum-utils.c b/src/libotutil/ot-checksum-utils.c
index 6a45c01..bf9b89f 100644
--- a/src/libotutil/ot-checksum-utils.c
+++ b/src/libotutil/ot-checksum-utils.c
@@ -139,6 +139,32 @@ ot_gio_checksum_stream (GInputStream   *in,
   return ot_gio_splice_get_checksum (NULL, in, out_csum, cancellable, error);
 }
 
+char *
+ot_checksum_file (GFile          *file,
+                  GChecksumType   checksum_type,
+                  GCancellable   *cancellable,
+                  GError        **error)
+{
+  GChecksum *checksum = NULL;
+  gs_free gchar *ret = NULL;
+  gs_unref_object GInputStream *in = NULL;
+
+  in = (GInputStream*)g_file_read (file, cancellable, error);
+  if (!in)
+    goto out;
+
+  checksum = g_checksum_new (checksum_type);
+
+  if (!ot_gio_splice_update_checksum (NULL, in, checksum, cancellable, error))
+    goto out;
+
+  ret = g_strdup (g_checksum_get_string (checksum));
+ out:
+  g_clear_pointer (&checksum, (GDestroyNotify) g_checksum_free);
+  return ret;
+
+}
+
 static void
 checksum_stream_thread (GSimpleAsyncResult   *result,
                         GObject              *object,
diff --git a/src/libotutil/ot-checksum-utils.h b/src/libotutil/ot-checksum-utils.h
index 7778ed0..eb8bbc0 100644
--- a/src/libotutil/ot-checksum-utils.h
+++ b/src/libotutil/ot-checksum-utils.h
@@ -53,6 +53,11 @@ gboolean ot_gio_checksum_stream (GInputStream   *in,
                                  GCancellable   *cancellable,
                                  GError        **error);
 
+char * ot_checksum_file (GFile          *file,
+                         GChecksumType   checksum_type,
+                         GCancellable   *cancellable,
+                         GError        **error);
+
 void ot_gio_checksum_stream_async (GInputStream         *in,
                                    int                   io_priority,
                                    GCancellable         *cancellable,
diff --git a/src/ostree/main.c b/src/ostree/main.c
index b16d8c1..e114690 100644
--- a/src/ostree/main.c
+++ b/src/ostree/main.c
@@ -55,6 +55,7 @@ static OstreeCommand commands[] = {
   { "rev-parse", ostree_builtin_rev_parse, 0 },
   { "show", ostree_builtin_show, 0 },
   { "static-delta", ostree_builtin_static_delta, 0 },
+  { "summary", ostree_builtin_summary, 0 },
 #ifdef HAVE_LIBSOUP 
   { "trivial-httpd", ostree_builtin_trivial_httpd, OSTREE_BUILTIN_FLAG_NO_REPO },
 #endif
diff --git a/src/ostree/ot-builtin-summary.c b/src/ostree/ot-builtin-summary.c
new file mode 100644
index 0000000..17feb34
--- /dev/null
+++ b/src/ostree/ot-builtin-summary.c
@@ -0,0 +1,64 @@
+/* -*- 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 "ot-builtins.h"
+#include "ostree.h"
+#include "otutil.h"
+
+static gboolean opt_update;
+
+static GOptionEntry options[] = {
+  { "update", 'u', 0, G_OPTION_ARG_NONE, &opt_update, "Update the summary", NULL },
+  { NULL }
+};
+
+gboolean
+ostree_builtin_summary (int argc, char **argv, OstreeRepo *repo, GCancellable *cancellable, GError **error)
+{
+  gboolean ret = FALSE;
+  GOptionContext *context;
+  gs_unref_ptrarray GPtrArray *delta_names = NULL;
+
+  context = g_option_context_new ("Manage summary metadata");
+  g_option_context_add_main_entries (context, options, NULL);
+
+  if (!g_option_context_parse (context, &argc, &argv, error))
+    goto out;
+
+  if (opt_update)
+    {
+      if (!ostree_repo_regenerate_summary (repo, cancellable, error))
+        goto out;
+    }
+  else
+    {
+      g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                   "No option specified; use -u to update summary");
+      goto out;
+    }
+
+  ret = TRUE;
+ out:
+  if (context)
+    g_option_context_free (context);
+  return ret;
+}
diff --git a/src/ostree/ot-builtins.h b/src/ostree/ot-builtins.h
index fee66f2..b8b6507 100644
--- a/src/ostree/ot-builtins.h
+++ b/src/ostree/ot-builtins.h
@@ -46,6 +46,7 @@ BUILTINPROTO(reset);
 BUILTINPROTO(fsck);
 BUILTINPROTO(show);
 BUILTINPROTO(static_delta);
+BUILTINPROTO(summary);
 BUILTINPROTO(rev_parse);
 BUILTINPROTO(remote);
 BUILTINPROTO(write_refs);
diff --git a/tests/test-pull-metalink.sh b/tests/test-pull-metalink.sh
new file mode 100755
index 0000000..fcadd19
--- /dev/null
+++ b/tests/test-pull-metalink.sh
@@ -0,0 +1,68 @@
+#!/bin/bash
+#
+# 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.
+
+set -e
+
+. $(dirname $0)/libtest.sh
+
+setup_fake_remote_repo1 "archive-z2"
+
+# And another web server acting as the metalink server
+cd ${test_tmpdir}
+mkdir metalink-data
+cd metalink-data
+ostree trivial-httpd --daemonize -p ${test_tmpdir}/metalink-httpd-port
+metalink_port=$(cat ${test_tmpdir}/metalink-httpd-port)
+echo "http://127.0.0.1:${metalink_port}"; > ${test_tmpdir}/metalink-httpd-address
+
+ostree --repo=${test_tmpdir}/ostree-srv/gnomerepo summary -u
+
+summary_path=${test_tmpdir}/ostree-srv/gnomerepo/summary
+
+echo '1..1'
+cd ${test_tmpdir}
+
+cat <<EOF
+<?xml version="1.0" encoding="utf-8"?>
+<metalink version="3.0" xmlns="http://www.metalinker.org/";>
+  <files>
+    <file name="summary">
+      <size>$(stat -c '%s' ${summary_path})</size>
+      <verification>
+        <hash type="md5">$(md5sum ${summary_path} | cut -f 1 -d ' ')</hash>
+        <hash type="sha256">$(sha256sum ${summary_path} | cut -f 1 -d ' ')</hash>
+        <hash type="sha512">$(sha512sum ${summary_path} | cut -f 1 -d ' ')</hash>
+      </verification>
+      <resources maxconnections="1">
+        <url protocol="http" type="http" location="US" preference="100" >$(cat httpd-address)</url>
+      </resources>
+    </file>
+  </files>
+</metalink>
+EOF
+
+cd ${test_tmpdir}
+mkdir repo
+${CMD_PREFIX} ostree --repo=repo init
+${CMD_PREFIX} ostree --repo=repo remote add --set=gpg-verify=false origin metalink=$(cat 
httpd-address)/ostree/gnomerepo
+# Try both syntaxes
+${CMD_PREFIX} ostree --repo=repo pull origin main
+${CMD_PREFIX} ostree --repo=repo pull origin:main
+${CMD_PREFIX} ostree --repo=repo fsck
+echo "ok pull"


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