lib/repo-finder: Add basic support for finding remote URIs by ref name

Add an initial OstreeRepoFinder interface (but no implementations),
which will find remote URIs by ref names and collection IDs, the
combination of which is globally unique.

The new API is used in a new ostree_repo_find_updates() function, which
resolves a list of ref names to update into a set of remote URIs to pull
them from, which can be treated as mirrors. It is an attempt to
generalise resolution of the URIs to pull from, and to generalise
determination of the order and parallelisation which they should be
downloaded from in.

Includes fixes by Krzesimir Nowak <krzesimir@kinvolk.io>.

Signed-off-by: Philip Withnall <withnall@endlessm.com>

Closes: #924
Approved by: cgwalters
This commit is contained in:
Philip Withnall 2017-04-18 23:59:33 +01:00 committed by Atomic Bot
parent 7607d94713
commit 292230301d
11 changed files with 2179 additions and 2 deletions

View File

@ -42,6 +42,7 @@ if ENABLE_EXPERIMENTAL_API
libostree_public_headers += \ libostree_public_headers += \
src/libostree/ostree-ref.h \ src/libostree/ostree-ref.h \
src/libostree/ostree-remote.h \ src/libostree/ostree-remote.h \
src/libostree/ostree-repo-finder.h \
$(NULL) $(NULL)
endif endif

View File

@ -154,6 +154,11 @@ if !ENABLE_EXPERIMENTAL_API
libostree_1_la_SOURCES += \ libostree_1_la_SOURCES += \
src/libostree/ostree-ref.h \ src/libostree/ostree-ref.h \
src/libostree/ostree-remote.h \ src/libostree/ostree-remote.h \
src/libostree/ostree-repo-finder.h \
$(NULL)
else # if ENABLE_EXPERIMENTAL_API
libostree_1_la_SOURCES += \
src/libostree/ostree-repo-finder.c \
$(NULL) $(NULL)
endif endif
@ -230,7 +235,7 @@ OSTree_1_0_gir_INCLUDES = Gio-2.0
OSTree_1_0_gir_CFLAGS = $(libostree_1_la_CFLAGS) OSTree_1_0_gir_CFLAGS = $(libostree_1_la_CFLAGS)
OSTree_1_0_gir_LIBS = libostree-1.la OSTree_1_0_gir_LIBS = libostree-1.la
OSTree_1_0_gir_SCANNERFLAGS = --warn-all --identifier-prefix=Ostree --symbol-prefix=ostree OSTree_1_0_gir_SCANNERFLAGS = --warn-all --identifier-prefix=Ostree --symbol-prefix=ostree
OSTree_1_0_gir_FILES = $(libostreeinclude_HEADERS) $(filter-out %-private.h %/ostree-soup-uri.h,$(libostree_1_la_SOURCES)) OSTree_1_0_gir_FILES = $(libostreeinclude_HEADERS) $(filter-out %-private.h %/ostree-soup-uri.h %/ostree-repo-finder.h,$(libostree_1_la_SOURCES))
INTROSPECTION_GIRS += OSTree-1.0.gir INTROSPECTION_GIRS += OSTree-1.0.gir
gir_DATA += OSTree-1.0.gir gir_DATA += OSTree-1.0.gir
typelib_DATA += OSTree-1.0.typelib typelib_DATA += OSTree-1.0.typelib

View File

@ -21,6 +21,34 @@ ostree_remote_unref
ostree_remote_get_name ostree_remote_get_name
</SECTION> </SECTION>
<SECTION>
<FILE>ostree-repo-experimental</FILE>
ostree_repo_find_remotes_async
ostree_repo_find_remotes_finish
ostree_repo_pull_from_remotes_async
ostree_repo_pull_from_remotes_finish
ostree_repo_resolve_keyring_for_collection
</SECTION>
<SECTION>
<FILE>ostree-repo-finder</FILE>
OstreeRepoFinder
ostree_repo_finder_resolve_async
ostree_repo_finder_resolve_finish
ostree_repo_finder_resolve_all_async
ostree_repo_finder_resolve_all_finish
OstreeRepoFinderResult
ostree_repo_finder_result_new
ostree_repo_finder_result_dup
ostree_repo_finder_result_free
ostree_repo_finder_result_compare
OstreeRepoFinderResultv
ostree_repo_finder_result_freev
<SUBSECTION Standard>
ostree_repo_finder_get_type
ostree_repo_finder_result_get_type
</SECTION>
<SECTION> <SECTION>
<FILE>ostree-misc-experimental</FILE> <FILE>ostree-misc-experimental</FILE>
ostree_repo_get_collection_id ostree_repo_get_collection_id

View File

@ -45,8 +45,24 @@ global:
ostree_collection_ref_get_type; ostree_collection_ref_get_type;
ostree_collection_ref_hash; ostree_collection_ref_hash;
ostree_collection_ref_new; ostree_collection_ref_new;
ostree_repo_find_remotes_async;
ostree_repo_find_remotes_finish;
ostree_repo_finder_get_type;
ostree_repo_finder_resolve_async;
ostree_repo_finder_resolve_all_async;
ostree_repo_finder_resolve_all_finish;
ostree_repo_finder_resolve_finish;
ostree_repo_finder_result_compare;
ostree_repo_finder_result_dup;
ostree_repo_finder_result_free;
ostree_repo_finder_result_freev;
ostree_repo_finder_result_get_type;
ostree_repo_finder_result_new;
ostree_repo_get_collection_id; ostree_repo_get_collection_id;
ostree_repo_list_collection_refs; ostree_repo_list_collection_refs;
ostree_repo_pull_from_remotes_async;
ostree_repo_pull_from_remotes_finish;
ostree_repo_resolve_keyring_for_collection;
ostree_repo_set_collection_id; ostree_repo_set_collection_id;
ostree_repo_set_collection_ref_immediate; ostree_repo_set_collection_ref_immediate;
ostree_repo_transaction_set_collection_ref; ostree_repo_transaction_set_collection_ref;

View File

@ -63,6 +63,9 @@ G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (OstreeRepoCommitTraverseIter, ostree_repo_comm
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeCollectionRef, ostree_collection_ref_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeCollectionRef, ostree_collection_ref_free)
G_DEFINE_AUTO_CLEANUP_FREE_FUNC (OstreeCollectionRefv, ostree_collection_ref_freev, NULL) G_DEFINE_AUTO_CLEANUP_FREE_FUNC (OstreeCollectionRefv, ostree_collection_ref_freev, NULL)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRemote, ostree_remote_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRemote, ostree_remote_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinder, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinderResult, ostree_repo_finder_result_free)
G_DEFINE_AUTO_CLEANUP_FREE_FUNC (OstreeRepoFinderResultv, ostree_repo_finder_result_freev, NULL)
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */ #endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
#endif #endif

View File

@ -182,6 +182,11 @@ gboolean ostree_validate_collection_id (const char *collection_id, GError **erro
#include "ostree-ref.h" #include "ostree-ref.h"
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeCollectionRef, ostree_collection_ref_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeCollectionRef, ostree_collection_ref_free)
G_DEFINE_AUTO_CLEANUP_FREE_FUNC (OstreeCollectionRefv, ostree_collection_ref_freev, NULL) G_DEFINE_AUTO_CLEANUP_FREE_FUNC (OstreeCollectionRefv, ostree_collection_ref_freev, NULL)
#include "ostree-repo-finder.h"
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinder, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinderResult, ostree_repo_finder_result_free)
G_DEFINE_AUTO_CLEANUP_FREE_FUNC (OstreeRepoFinderResultv, ostree_repo_finder_result_freev, NULL)
#endif #endif
G_END_DECLS G_END_DECLS

View File

@ -0,0 +1,576 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright © 2017 Endless Mobile, Inc.
*
* 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.
*
* Authors:
* - Philip Withnall <withnall@endlessm.com>
*/
#include "config.h"
#include <gio/gio.h>
#include <glib.h>
#include <glib-object.h>
#include "ostree-autocleanups.h"
#include "ostree-core.h"
#include "ostree-remote-private.h"
#include "ostree-repo-finder.h"
#include "ostree-repo.h"
static void ostree_repo_finder_default_init (OstreeRepoFinderInterface *iface);
G_DEFINE_INTERFACE (OstreeRepoFinder, ostree_repo_finder, G_TYPE_OBJECT)
static void
ostree_repo_finder_default_init (OstreeRepoFinderInterface *iface)
{
/* Nothing to see here. */
}
/* Validate the given struct contains a valid collection ID and ref name, and that
* the collection ID is non-%NULL. */
static gboolean
is_valid_collection_ref (const OstreeCollectionRef *ref)
{
return (ref != NULL &&
ostree_validate_rev (ref->ref_name, NULL) &&
ostree_validate_collection_id (ref->collection_id, NULL));
}
/* Validate @refs is non-%NULL, non-empty, and contains only valid collection
* and ref names. */
static gboolean
is_valid_collection_ref_array (const OstreeCollectionRef * const *refs)
{
gsize i;
if (refs == NULL || *refs == NULL)
return FALSE;
for (i = 0; refs[i] != NULL; i++)
{
if (!is_valid_collection_ref (refs[i]))
return FALSE;
}
return TRUE;
}
/* Validate @ref_to_checksum is non-%NULL, non-empty, and contains only valid
* OstreeCollectionRefs as keys and only valid commit checksums as values. */
static gboolean
is_valid_collection_ref_map (GHashTable *ref_to_checksum)
{
GHashTableIter iter;
const OstreeCollectionRef *ref;
const gchar *checksum;
if (ref_to_checksum == NULL || g_hash_table_size (ref_to_checksum) == 0)
return FALSE;
g_hash_table_iter_init (&iter, ref_to_checksum);
while (g_hash_table_iter_next (&iter, (gpointer *) &ref, (gpointer *) &checksum))
{
g_assert (ref != NULL);
g_assert (checksum != NULL);
if (!is_valid_collection_ref (ref))
return FALSE;
if (!ostree_validate_checksum_string (checksum, NULL))
return FALSE;
}
return TRUE;
}
static void resolve_cb (GObject *obj,
GAsyncResult *result,
gpointer user_data);
/**
* ostree_repo_finder_resolve_async:
* @self: an #OstreeRepoFinder
* @refs: (array zero-terminated=1): non-empty array of collectionref pairs to find remotes for
* @parent_repo: (transfer none): the local repository which the refs are being resolved for,
* which provides configuration information and GPG keys
* @cancellable: (nullable): a #GCancellable, or %NULL
* @callback: asynchronous completion callback
* @user_data: data to pass to @callback
*
* Find reachable remote URIs which claim to provide any of the given @refs. The
* specific method for finding the remotes depends on the #OstreeRepoFinder
* implementation.
*
* Any remote which is found and which claims to support any of the given @refs
* will be returned in the results. It is possible that a remote claims to
* support a given ref, but turns out not to it is not possible to verify this
* until ostree_repo_pull_from_remotes_async() is called.
*
* The returned results will be sorted with the most useful first this is
* typically the remote which claims to provide the most @refs, at the lowest
* latency.
*
* Each result contains a mapping of @refs to the checksums of the commits
* which the result provides. If the result provides the latest commit for a ref
* across all of the results, the checksum will be set. Otherwise, if the
* result provides an outdated commit, or doesnt provide a given ref at all,
* the ref will not be set. Results which provide none of the requested @refs
* may be listed with an empty refs map.
*
* Pass the results to ostree_repo_pull_from_remotes_async() to pull the given
* @refs from those remotes.
*
* Since: 2017.8
*/
void
ostree_repo_finder_resolve_async (OstreeRepoFinder *self,
const OstreeCollectionRef * const *refs,
OstreeRepo *parent_repo,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
OstreeRepoFinder *finders[2] = { NULL, };
g_return_if_fail (OSTREE_IS_REPO_FINDER (self));
g_return_if_fail (is_valid_collection_ref_array (refs));
g_return_if_fail (OSTREE_IS_REPO (parent_repo));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
task = g_task_new (self, cancellable, callback, user_data);
g_task_set_source_tag (task, ostree_repo_finder_resolve_async);
finders[0] = self;
ostree_repo_finder_resolve_all_async (finders, refs, parent_repo, cancellable,
resolve_cb, g_steal_pointer (&task));
}
static void
resolve_cb (GObject *obj,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_autoptr(GPtrArray) results = NULL;
g_autoptr(GError) local_error = NULL;
task = G_TASK (user_data);
results = ostree_repo_finder_resolve_all_finish (result, &local_error);
g_assert ((local_error == NULL) != (results == NULL));
if (local_error != NULL)
g_task_return_error (task, g_steal_pointer (&local_error));
else
g_task_return_pointer (task, g_steal_pointer (&results), (GDestroyNotify) g_ptr_array_unref);
}
/**
* ostree_repo_finder_resolve_finish:
* @self: an #OstreeRepoFinder
* @result: #GAsyncResult from the callback
* @error: return location for a #GError
*
* Get the results from a ostree_repo_finder_resolve_async() operation.
*
* Returns: (transfer full) (element-type OstreeRepoFinderResult): array of zero
* or more results
* Since: 2017.8
*/
GPtrArray *
ostree_repo_finder_resolve_finish (OstreeRepoFinder *self,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (OSTREE_IS_REPO_FINDER (self), NULL);
g_return_val_if_fail (g_task_is_valid (result, self), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
static gint
sort_results_cb (gconstpointer a,
gconstpointer b)
{
const OstreeRepoFinderResult *result_a = *((const OstreeRepoFinderResult **) a);
const OstreeRepoFinderResult *result_b = *((const OstreeRepoFinderResult **) b);
return ostree_repo_finder_result_compare (result_a, result_b);
}
typedef struct
{
gsize n_finders_pending;
GPtrArray *results;
} ResolveAllData;
static void
resolve_all_data_free (ResolveAllData *data)
{
g_assert (data->n_finders_pending == 0);
g_clear_pointer (&data->results, g_ptr_array_unref);
g_free (data);
}
G_DEFINE_AUTOPTR_CLEANUP_FUNC (ResolveAllData, resolve_all_data_free)
static void resolve_all_cb (GObject *obj,
GAsyncResult *result,
gpointer user_data);
static void resolve_all_finished_one (GTask *task);
/**
* ostree_repo_finder_resolve_all_async:
* @finders: (array zero-terminated=1): non-empty array of #OstreeRepoFinders
* @refs: (array zero-terminated=1): non-empty array of collectionref pairs to find remotes for
* @parent_repo: (transfer none): the local repository which the refs are being resolved for,
* which provides configuration information and GPG keys
* @cancellable: (nullable): a #GCancellable, or %NULL
* @callback: asynchronous completion callback
* @user_data: data to pass to @callback
*
* A version of ostree_repo_finder_resolve_async() which queries one or more
* @finders in parallel and combines the results.
*
* Since: 2017.8
*/
void
ostree_repo_finder_resolve_all_async (OstreeRepoFinder * const *finders,
const OstreeCollectionRef * const *refs,
OstreeRepo *parent_repo,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_autoptr(ResolveAllData) data = NULL;
gsize i;
g_autoptr(GString) refs_str = NULL;
g_autoptr(GString) finders_str = NULL;
g_return_if_fail (finders != NULL && finders[0] != NULL);
g_return_if_fail (is_valid_collection_ref_array (refs));
g_return_if_fail (OSTREE_IS_REPO (parent_repo));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
refs_str = g_string_new ("");
for (i = 0; refs[i] != NULL; i++)
{
if (i != 0)
g_string_append (refs_str, ", ");
g_string_append_printf (refs_str, "(%s, %s)",
refs[i]->collection_id, refs[i]->ref_name);
}
finders_str = g_string_new ("");
for (i = 0; finders[i] != NULL; i++)
{
if (i != 0)
g_string_append (finders_str, ", ");
g_string_append (finders_str, g_type_name (G_TYPE_FROM_INSTANCE (finders[i])));
}
g_debug ("%s: Resolving refs [%s] with finders [%s]", G_STRFUNC,
refs_str->str, finders_str->str);
task = g_task_new (NULL, cancellable, callback, user_data);
g_task_set_source_tag (task, ostree_repo_finder_resolve_all_async);
data = g_new0 (ResolveAllData, 1);
data->n_finders_pending = 1; /* while setting up the loop */
data->results = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_repo_finder_result_free);
g_task_set_task_data (task, data, (GDestroyNotify) resolve_all_data_free);
/* Start all the asynchronous queries in parallel. */
for (i = 0; finders[i] != NULL; i++)
{
OstreeRepoFinder *finder = OSTREE_REPO_FINDER (finders[i]);
OstreeRepoFinderInterface *iface;
iface = OSTREE_REPO_FINDER_GET_IFACE (finder);
g_assert (iface->resolve_async != NULL);
iface->resolve_async (finder, refs, parent_repo, cancellable, resolve_all_cb, g_object_ref (task));
data->n_finders_pending++;
}
resolve_all_finished_one (task);
data = NULL; /* passed to the GTask above */
}
/* Modifies both arrays in place. */
static void
array_concatenate_steal (GPtrArray *array,
GPtrArray *to_concatenate) /* (transfer full) */
{
g_autoptr(GPtrArray) array_to_concatenate = to_concatenate;
gsize i;
for (i = 0; i < array_to_concatenate->len; i++)
{
/* Sanity check that the arrays do not contain any %NULL elements
* (particularly NULL terminators). */
g_assert (g_ptr_array_index (array_to_concatenate, i) != NULL);
g_ptr_array_add (array, g_steal_pointer (&g_ptr_array_index (array_to_concatenate, i)));
}
g_ptr_array_set_free_func (array_to_concatenate, NULL);
g_ptr_array_set_size (array_to_concatenate, 0);
}
static void
resolve_all_cb (GObject *obj,
GAsyncResult *result,
gpointer user_data)
{
OstreeRepoFinder *finder;
OstreeRepoFinderInterface *iface;
g_autoptr(GTask) task = NULL;
g_autoptr(GPtrArray) results = NULL;
g_autoptr(GError) local_error = NULL;
ResolveAllData *data;
finder = OSTREE_REPO_FINDER (obj);
iface = OSTREE_REPO_FINDER_GET_IFACE (finder);
task = G_TASK (user_data);
data = g_task_get_task_data (task);
results = iface->resolve_finish (finder, result, &local_error);
g_assert ((local_error == NULL) != (results == NULL));
if (local_error != NULL)
g_debug ("Error resolving refs to repository URI using %s: %s",
g_type_name (G_TYPE_FROM_INSTANCE (finder)), local_error->message);
else
array_concatenate_steal (data->results, g_steal_pointer (&results));
resolve_all_finished_one (task);
}
static void
resolve_all_finished_one (GTask *task)
{
ResolveAllData *data;
data = g_task_get_task_data (task);
data->n_finders_pending--;
if (data->n_finders_pending == 0)
{
gsize i;
g_autoptr(GString) results_str = NULL;
g_ptr_array_sort (data->results, sort_results_cb);
results_str = g_string_new ("");
for (i = 0; i < data->results->len; i++)
{
const OstreeRepoFinderResult *result = g_ptr_array_index (data->results, i);
if (i != 0)
g_string_append (results_str, ", ");
g_string_append (results_str, ostree_remote_get_name (result->remote));
}
if (i == 0)
g_string_append (results_str, "(none)");
g_debug ("%s: Finished, results: %s", G_STRFUNC, results_str->str);
g_task_return_pointer (task, g_steal_pointer (&data->results), (GDestroyNotify) g_ptr_array_unref);
}
}
/**
* ostree_repo_finder_resolve_all_finish:
* @result: #GAsyncResult from the callback
* @error: return location for a #GError
*
* Get the results from a ostree_repo_finder_resolve_all_async() operation.
*
* Returns: (transfer full) (element-type OstreeRepoFinderResult): array of zero
* or more results
* Since: 2017.8
*/
GPtrArray *
ostree_repo_finder_resolve_all_finish (GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (result, NULL), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
G_DEFINE_BOXED_TYPE (OstreeRepoFinderResult, ostree_repo_finder_result,
ostree_repo_finder_result_dup, ostree_repo_finder_result_free)
/**
* ostree_repo_finder_result_new:
* @remote: (transfer none): an #OstreeRemote containing the transport details
* for the result
* @finder: (transfer none): the #OstreeRepoFinder instance which produced the
* result
* @priority: static priority of the result, where higher numbers indicate lower
* priority
* @ref_to_checksum: (element-type OstreeCollectionRef utf8): map of collectionref pairs
* to checksums provided by this result
* @summary_last_modified: Unix timestamp (seconds since the epoch, UTC) when
* the summary file for the result was last modified, or `0` if this is unknown
*
* Create a new #OstreeRepoFinderResult instance. The semantics for the arguments
* are as described in the #OstreeRepoFinderResult documentation.
*
* Returns: (transfer full): a new #OstreeRepoFinderResult
* Since: 2017.8
*/
OstreeRepoFinderResult *
ostree_repo_finder_result_new (OstreeRemote *remote,
OstreeRepoFinder *finder,
gint priority,
GHashTable *ref_to_checksum,
guint64 summary_last_modified)
{
g_autoptr(OstreeRepoFinderResult) result = NULL;
g_return_val_if_fail (remote != NULL, NULL);
g_return_val_if_fail (OSTREE_IS_REPO_FINDER (finder), NULL);
g_return_val_if_fail (is_valid_collection_ref_map (ref_to_checksum), NULL);
result = g_new0 (OstreeRepoFinderResult, 1);
result->remote = ostree_remote_ref (remote);
result->finder = g_object_ref (finder);
result->priority = priority;
result->ref_to_checksum = g_hash_table_ref (ref_to_checksum);
result->summary_last_modified = summary_last_modified;
return g_steal_pointer (&result);
}
/**
* ostree_repo_finder_result_dup:
* @result: (transfer none): an #OstreeRepoFinderResult to copy
*
* Copy an #OstreeRepoFinderResult.
*
* Returns: (transfer full): a newly allocated copy of @result
* Since: 2017.8
*/
OstreeRepoFinderResult *
ostree_repo_finder_result_dup (OstreeRepoFinderResult *result)
{
g_return_val_if_fail (result != NULL, NULL);
return ostree_repo_finder_result_new (result->remote, result->finder,
result->priority, result->ref_to_checksum,
result->summary_last_modified);
}
/**
* ostree_repo_finder_result_compare:
* @a: an #OstreeRepoFinderResult
* @b: an #OstreeRepoFinderResult
*
* Compare two #OstreeRepoFinderResult instances to work out which one is better
* to pull from, and hence needs to be ordered before the other.
*
* Returns: <0 if @a is ordered before @b, 0 if they are ordered equally,
* >0 if @b is ordered before @a
* Since: 2017.8
*/
gint
ostree_repo_finder_result_compare (const OstreeRepoFinderResult *a,
const OstreeRepoFinderResult *b)
{
guint a_n_refs, b_n_refs;
g_return_val_if_fail (a != NULL, 0);
g_return_val_if_fail (b != NULL, 0);
/* FIXME: Check if this is really the ordering we want. For example, we
* probably dont want a result with 0 refs to be ordered before one with >0
* refs, just because its priority is higher. */
if (a->priority != b->priority)
return a->priority - b->priority;
if (a->summary_last_modified != 0 && b->summary_last_modified != 0 &&
a->summary_last_modified != b->summary_last_modified)
return a->summary_last_modified - b->summary_last_modified;
gpointer value;
GHashTableIter iter;
a_n_refs = b_n_refs = 0;
g_hash_table_iter_init (&iter, a->ref_to_checksum);
while (g_hash_table_iter_next (&iter, NULL, &value))
if (value != NULL)
a_n_refs++;
g_hash_table_iter_init (&iter, b->ref_to_checksum);
while (g_hash_table_iter_next (&iter, NULL, &value))
if (value != NULL)
b_n_refs++;
if (a_n_refs != b_n_refs)
return (gint) a_n_refs - (gint) b_n_refs;
return g_strcmp0 (a->remote->name, b->remote->name);
}
/**
* ostree_repo_finder_result_free:
* @result: (transfer full): an #OstreeRepoFinderResult
*
* Free the given @result.
*
* Since: 2017.8
*/
void
ostree_repo_finder_result_free (OstreeRepoFinderResult *result)
{
g_return_if_fail (result != NULL);
g_hash_table_unref (result->ref_to_checksum);
g_object_unref (result->finder);
ostree_remote_unref (result->remote);
g_free (result);
}
/**
* ostree_repo_finder_result_freev:
* @results: (array zero-terminated=1) (transfer full): an #OstreeRepoFinderResult
*
* Free the given @results array, freeing each element and the container.
*
* Since: 2017.8
*/
void
ostree_repo_finder_result_freev (OstreeRepoFinderResult **results)
{
gsize i;
for (i = 0; results[i] != NULL; i++)
ostree_repo_finder_result_free (results[i]);
g_free (results);
}

View File

@ -0,0 +1,172 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
*
* Copyright © 2017 Endless Mobile, Inc.
*
* 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.
*
* Authors:
* - Philip Withnall <withnall@endlessm.com>
*/
#pragma once
#include <gio/gio.h>
#include <glib.h>
#include <glib-object.h>
#include "ostree-ref.h"
#include "ostree-remote.h"
#include "ostree-types.h"
G_BEGIN_DECLS
#define OSTREE_TYPE_REPO_FINDER (ostree_repo_finder_get_type ())
/* Manually expanded version of the following, omitting autoptr support (for GLib < 2.44):
_OSTREE_PUBLIC
G_DECLARE_INTERFACE (OstreeRepoFinder, ostree_repo_finder, OSTREE, REPO_FINDER, GObject) */
_OSTREE_PUBLIC
GType ostree_repo_finder_get_type (void);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
typedef struct _OstreeRepoFinder OstreeRepoFinder;
typedef struct _OstreeRepoFinderInterface OstreeRepoFinderInterface;
static inline OstreeRepoFinder *OSTREE_REPO_FINDER (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, ostree_repo_finder_get_type (), OstreeRepoFinder); }
static inline gboolean OSTREE_IS_REPO_FINDER (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, ostree_repo_finder_get_type ()); }
static inline OstreeRepoFinderInterface *OSTREE_REPO_FINDER_GET_IFACE (gpointer ptr) { return G_TYPE_INSTANCE_GET_INTERFACE (ptr, ostree_repo_finder_get_type (), OstreeRepoFinderInterface); }
G_GNUC_END_IGNORE_DEPRECATIONS
struct _OstreeRepoFinderInterface
{
GTypeInterface g_iface;
void (*resolve_async) (OstreeRepoFinder *self,
const OstreeCollectionRef * const *refs,
OstreeRepo *parent_repo,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GPtrArray *(*resolve_finish) (OstreeRepoFinder *self,
GAsyncResult *result,
GError **error);
};
_OSTREE_PUBLIC
void ostree_repo_finder_resolve_async (OstreeRepoFinder *self,
const OstreeCollectionRef * const *refs,
OstreeRepo *parent_repo,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
_OSTREE_PUBLIC
GPtrArray *ostree_repo_finder_resolve_finish (OstreeRepoFinder *self,
GAsyncResult *result,
GError **error);
_OSTREE_PUBLIC
void ostree_repo_finder_resolve_all_async (OstreeRepoFinder * const *finders,
const OstreeCollectionRef * const *refs,
OstreeRepo *parent_repo,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
_OSTREE_PUBLIC
GPtrArray *ostree_repo_finder_resolve_all_finish (GAsyncResult *result,
GError **error);
/**
* OstreeRepoFinderResult:
* @remote: #OstreeRemote which contains the transport details for the result,
* such as its URI and GPG key
* @finder: the #OstreeRepoFinder instance which produced this result
* @priority: static priority of the result, where higher numbers indicate lower
* priority
* @ref_to_checksum: (element-type OstreeCollectionRef utf8): map of collectionref
* pairs to checksums provided by this remote; values may be %NULL to
* indicate this remote doesnt provide that ref
* @summary_last_modified: Unix timestamp (seconds since the epoch, UTC) when
* the summary file on the remote was last modified, or `0` if unknown
*
* #OstreeRepoFinderResult gives a single result from an
* ostree_repo_finder_resolve_async() or ostree_repo_finder_resolve_all_async()
* operation. This represents a single remote which provides none, some or all
* of the refs being resolved. The structure includes various bits of metadata
* which allow ostree_repo_pull_from_remotes_async() (for example) to prioritise
* how to pull the refs.
*
* The @priority is used as one input of many to ordering functions like
* ostree_repo_finder_result_compare().
*
* @ref_to_checksum indicates which refs (out of the ones queried for as inputs
* to ostree_repo_finder_resolve_async()) are provided by this remote. The refs
* are present as keys (of type #OstreeCollectionRef), and the corresponding values
* are the checksums of the commits the remote currently has for those refs. (These
* might not be the latest commits available out of all results.) A
* checksum may be %NULL if the remote does not advertise the corresponding ref.
* After ostree_repo_finder_resolve_async() has been called, the commit metadata
* should be available locally, so the details for each checksum can be looked
* up using ostree_repo_load_commit().
*
* Since: 2017.8
*/
typedef struct
{
OstreeRemote *remote;
OstreeRepoFinder *finder;
gint priority;
GHashTable *ref_to_checksum;
guint64 summary_last_modified;
/*< private >*/
gpointer padding[4];
} OstreeRepoFinderResult;
_OSTREE_PUBLIC
GType ostree_repo_finder_result_get_type (void);
_OSTREE_PUBLIC
OstreeRepoFinderResult *ostree_repo_finder_result_new (OstreeRemote *remote,
OstreeRepoFinder *finder,
gint priority,
GHashTable *ref_to_checksum,
guint64 summary_last_modified);
_OSTREE_PUBLIC
OstreeRepoFinderResult *ostree_repo_finder_result_dup (OstreeRepoFinderResult *result);
_OSTREE_PUBLIC
gint ostree_repo_finder_result_compare (const OstreeRepoFinderResult *a,
const OstreeRepoFinderResult *b);
_OSTREE_PUBLIC
void ostree_repo_finder_result_free (OstreeRepoFinderResult *result);
/**
* OstreeRepoFinderResultv:
*
* A %NULL-terminated array of #OstreeRepoFinderResult instances, designed to
* be used with g_auto():
*
* |[<!-- language="C" -->
* g_auto(OstreeRepoFinderResultv) results = NULL;
* ]|
*
* Since: 2017.8
*/
typedef OstreeRepoFinderResult** OstreeRepoFinderResultv;
_OSTREE_PUBLIC
void ostree_repo_finder_result_freev (OstreeRepoFinderResult **results);
G_END_DECLS

File diff suppressed because it is too large Load Diff

View File

@ -27,6 +27,7 @@
#include "ostree-async-progress.h" #include "ostree-async-progress.h"
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API #ifdef OSTREE_ENABLE_EXPERIMENTAL_API
#include "ostree-ref.h" #include "ostree-ref.h"
#include "ostree-repo-finder.h"
#endif #endif
#include "ostree-sepolicy.h" #include "ostree-sepolicy.h"
#include "ostree-gpg-verify-result.h" #include "ostree-gpg-verify-result.h"
@ -1080,6 +1081,33 @@ ostree_repo_pull_one_dir (OstreeRepo *self,
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);
#if 0
FIXME
Called with: remote_name, refs, override-commit-ids
or: URL, refs, override-commit-ids
=> we only need refs; could use the remote_name or URL as additional results
Summary file is downloaded first, so this would result in multiple downloads of
the summary, but we dont care because of caching.
Big problem preventing this from being the overall API: presenting the download
sizes in the gnome-software UI before the user chooses to download.
_OSTREE_PUBLIC
gboolean ostree_repo_find_remotes_squashed (OstreeRepo *self,
const gchar * const *refs, -> options
GVariant *options,
OstreeRepoFinder **finders, -> options
GMainContext *context, -> nope
OstreeAsyncProgress *progress,
GCancellable *cancellable,
GError **error);
#endif
_OSTREE_PUBLIC _OSTREE_PUBLIC
gboolean ostree_repo_pull_with_options (OstreeRepo *self, gboolean ostree_repo_pull_with_options (OstreeRepo *self,
const char *remote_name_or_baseurl, const char *remote_name_or_baseurl,
@ -1090,6 +1118,39 @@ gboolean ostree_repo_pull_with_options (OstreeRepo *self,
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API #ifdef OSTREE_ENABLE_EXPERIMENTAL_API
_OSTREE_PUBLIC
void ostree_repo_find_remotes_async (OstreeRepo *self,
const OstreeCollectionRef * const *refs,
GVariant *options,
OstreeRepoFinder **finders,
OstreeAsyncProgress *progress,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
_OSTREE_PUBLIC
OstreeRepoFinderResult **ostree_repo_find_remotes_finish (OstreeRepo *self,
GAsyncResult *result,
GError **error);
_OSTREE_PUBLIC
void ostree_repo_pull_from_remotes_async (OstreeRepo *self,
const OstreeRepoFinderResult * const *results,
GVariant *options,
OstreeAsyncProgress *progress,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
_OSTREE_PUBLIC
gboolean ostree_repo_pull_from_remotes_finish (OstreeRepo *self,
GAsyncResult *result,
GError **error);
_OSTREE_PUBLIC
gchar *ostree_repo_resolve_keyring_for_collection (OstreeRepo *self,
const gchar *collection_id,
GCancellable *cancellable,
GError **error);
_OSTREE_PUBLIC _OSTREE_PUBLIC
gboolean ostree_repo_list_collection_refs (OstreeRepo *self, gboolean ostree_repo_list_collection_refs (OstreeRepo *self,
const char *match_collection_id, const char *match_collection_id,

View File

@ -37,6 +37,7 @@
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API #ifdef OSTREE_ENABLE_EXPERIMENTAL_API
#include <ostree-ref.h> #include <ostree-ref.h>
#include <ostree-repo-finder.h>
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */ #endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
#include <ostree-autocleanups.h> #include <ostree-autocleanups.h>