lib/repo-finder: Add config-file based OstreeRepoFinder implementation

This is a basic implementation of OstreeRepoFinder which resolves ref
names to remote URIs by looking their collection IDs up in the local
configuration of remotes who have their collection-id key set.

Unit tests are included.

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

Closes: #924
Approved by: cgwalters
This commit is contained in:
Philip Withnall authored and Atomic Bot committed 2017-06-26 15:56:07 +00:00
1 parent 292230301d
commit d15f83c922
13 files changed
+653 -1

No files matched your search

+2
View File
@@ -47,6 +47,8 @@ global:
ostree_collection_ref_new;
ostree_repo_find_remotes_async;
ostree_repo_find_remotes_finish;
ostree_repo_finder_config_get_type;
ostree_repo_finder_config_new;
ostree_repo_finder_get_type;
ostree_repo_finder_resolve_async;
ostree_repo_finder_resolve_all_async;
+1
View File
@@ -64,6 +64,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeCollectionRef, ostree_collection_ref_free)
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 (OstreeRepoFinder, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinderConfig, 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 */
+3
View File
@@ -187,6 +187,9 @@ G_DEFINE_AUTO_CLEANUP_FREE_FUNC (OstreeCollectionRefv, ostree_collection_ref_fre
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)
#include "ostree-repo-finder-config.h"
G_DEFINE_AUTOPTR_CLEANUP_FUNC (OstreeRepoFinderConfig, g_object_unref)
#endif
G_END_DECLS
+235
View File
@@ -0,0 +1,235 @@
/* -*- 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 <fcntl.h>
#include <gio/gio.h>
#include <glib.h>
#include <glib-object.h>
#include <libglnx.h>
#include "ostree-remote-private.h"
#include "ostree-repo.h"
#include "ostree-repo-private.h"
#include "ostree-repo-finder.h"
#include "ostree-repo-finder-config.h"
/**
* SECTION:ostree-repo-finder-config
* @title: OstreeRepoFinderConfig
* @short_description: Finds remote repositories from ref names using the local
* repository configuration files
* @stability: Unstable
* @include: libostree/ostree-repo-finder-config.h
*
* #OstreeRepoFinderConfig is an implementation of #OstreeRepoFinder which looks
* refs up in locally configured remotes and returns remote URIs.
* Duplicate remote URIs are combined into a single #OstreeRepoFinderResult
* which lists multiple refs.
*
* For all the locally configured remotes which have an `collection-id` specified
* (see [ostree.repo-config(5)](man:ostree.repo-config(5))), it finds the
* intersection of their refs and the set of refs to resolve. If the
* intersection is non-empty, that remote is returned as a result. Remotes which
* do not have their `collection-id` key configured are ignored.
*
* Since: 2017.8
*/
static void ostree_repo_finder_config_iface_init (OstreeRepoFinderInterface *iface);
struct _OstreeRepoFinderConfig
{
GObject parent_instance;
};
G_DEFINE_TYPE_WITH_CODE (OstreeRepoFinderConfig, ostree_repo_finder_config, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (OSTREE_TYPE_REPO_FINDER, ostree_repo_finder_config_iface_init))
static gint
results_compare_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);
}
static void
ostree_repo_finder_config_resolve_async (OstreeRepoFinder *finder,
const OstreeCollectionRef * const *refs,
OstreeRepo *parent_repo,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr(GTask) task = NULL;
g_autoptr(GPtrArray) results = NULL;
const gint priority = 100; /* arbitrarily chosen; lower than the others */
gsize i, j;
g_autoptr(GHashTable) repo_name_to_refs = NULL; /* (element-type utf8 GHashTable) */
GHashTable *supported_ref_to_checksum; /* (element-type OstreeCollectionRef utf8) */
GHashTableIter iter;
const gchar *remote_name;
g_auto(GStrv) remotes = NULL;
gsize n_remotes = 0;
task = g_task_new (finder, cancellable, callback, user_data);
g_task_set_source_tag (task, ostree_repo_finder_config_resolve_async);
results = g_ptr_array_new_with_free_func ((GDestroyNotify) ostree_repo_finder_result_free);
repo_name_to_refs = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
(GDestroyNotify) g_hash_table_unref);
/* List all remotes in this #OstreeRepo and see which of their ref lists
* intersect with @refs. */
remotes = ostree_repo_remote_list (parent_repo, (guint *) &n_remotes);
g_debug ("%s: Checking %" G_GSIZE_FORMAT " remotes", G_STRFUNC, n_remotes);
for (i = 0; i < n_remotes; i++)
{
g_autoptr(GError) local_error = NULL;
g_autoptr(GHashTable) remote_refs = NULL; /* (element-type utf8 utf8) */
const gchar *checksum;
g_autofree gchar *remote_collection_id = NULL;
remote_name = remotes[i];
if (!ostree_repo_get_remote_option (parent_repo, remote_name, "collection-id",
NULL, &remote_collection_id, &local_error) ||
!ostree_validate_collection_id (remote_collection_id, &local_error))
{
g_debug ("Ignoring remote ‘%s’ due to no valid collection ID being configured for it: %s",
remote_name, local_error->message);
g_clear_error (&local_error);
continue;
}
if (!ostree_repo_remote_list_refs (parent_repo, remote_name, &remote_refs,
cancellable, &local_error))
{
g_debug ("Ignoring remote ‘%s’ due to error loading its refs: %s",
remote_name, local_error->message);
g_clear_error (&local_error);
continue;
}
for (j = 0; refs[j] != NULL; j++)
{
if (g_strcmp0 (refs[j]->collection_id, remote_collection_id) == 0 &&
g_hash_table_lookup_extended (remote_refs, refs[j]->ref_name, NULL, (gpointer *) &checksum))
{
/* The requested ref is listed in the refs for this remote. Add
* the remote to the results, and the ref to its
* @supported_ref_to_checksum. */
g_debug ("Resolved ref (%s, %s) to remote ‘%s’.",
refs[j]->collection_id, refs[j]->ref_name, remote_name);
supported_ref_to_checksum = g_hash_table_lookup (repo_name_to_refs, remote_name);
if (supported_ref_to_checksum == NULL)
{
supported_ref_to_checksum = g_hash_table_new_full (ostree_collection_ref_hash,
ostree_collection_ref_equal,
NULL, g_free);
g_hash_table_insert (repo_name_to_refs, (gpointer) remote_name, supported_ref_to_checksum /* transfer */);
}
g_hash_table_insert (supported_ref_to_checksum,
(gpointer) refs[j], g_strdup (checksum));
}
}
}
/* Aggregate the results. */
g_hash_table_iter_init (&iter, repo_name_to_refs);
while (g_hash_table_iter_next (&iter, (gpointer *) &remote_name, (gpointer *) &supported_ref_to_checksum))
{
g_autoptr(GError) local_error = NULL;
OstreeRemote *remote;
/* We don’t know what last-modified timestamp the remote has without
* making expensive HTTP queries, so leave that information blank. We
* assume that the configuration which says the refs and commits in
* @supported_ref_to_checksum are in the repository is correct; the code
* in ostree_repo_find_remotes_async() will check that. */
remote = _ostree_repo_get_remote_inherited (parent_repo, remote_name, &local_error);
if (remote == NULL)
{
g_debug ("Configuration for remote ‘%s’ could not be found. Ignoring.",
remote_name);
continue;
}
g_ptr_array_add (results, ostree_repo_finder_result_new (remote, finder, priority, supported_ref_to_checksum, 0));
}
g_ptr_array_sort (results, results_compare_cb);
g_task_return_pointer (task, g_steal_pointer (&results), (GDestroyNotify) g_ptr_array_unref);
}
static GPtrArray *
ostree_repo_finder_config_resolve_finish (OstreeRepoFinder *finder,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (g_task_is_valid (result, finder), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
static void
ostree_repo_finder_config_init (OstreeRepoFinderConfig *self)
{
/* Nothing to see here. */
}
static void
ostree_repo_finder_config_class_init (OstreeRepoFinderConfigClass *klass)
{
/* Nothing to see here. */
}
static void
ostree_repo_finder_config_iface_init (OstreeRepoFinderInterface *iface)
{
iface->resolve_async = ostree_repo_finder_config_resolve_async;
iface->resolve_finish = ostree_repo_finder_config_resolve_finish;
}
/**
* ostree_repo_finder_config_new:
*
* Create a new #OstreeRepoFinderConfig.
*
* Returns: (transfer full): a new #OstreeRepoFinderConfig
* Since: 2017.8
*/
OstreeRepoFinderConfig *
ostree_repo_finder_config_new (void)
{
return g_object_new (OSTREE_TYPE_REPO_FINDER_CONFIG, NULL);
}
+55
View File
@@ -0,0 +1,55 @@
/* -*- 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-repo-finder.h"
#include "ostree-types.h"
G_BEGIN_DECLS
#define OSTREE_TYPE_REPO_FINDER_CONFIG (ostree_repo_finder_config_get_type ())
/* Manually expanded version of the following, omitting autoptr support (for GLib < 2.44):
_OSTREE_PUBLIC
G_DECLARE_FINAL_TYPE (OstreeRepoFinderConfig, ostree_repo_finder_config, OSTREE, REPO_FINDER_CONFIG, GObject) */
_OSTREE_PUBLIC
GType ostree_repo_finder_config_get_type (void);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
typedef struct _OstreeRepoFinderConfig OstreeRepoFinderConfig;
typedef struct { GObjectClass parent_class; } OstreeRepoFinderConfigClass;
static inline OstreeRepoFinderConfig *OSTREE_REPO_FINDER_CONFIG (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_CAST (ptr, ostree_repo_finder_config_get_type (), OstreeRepoFinderConfig); }
static inline gboolean OSTREE_IS_REPO_FINDER_CONFIG (gpointer ptr) { return G_TYPE_CHECK_INSTANCE_TYPE (ptr, ostree_repo_finder_config_get_type ()); }
G_GNUC_END_IGNORE_DEPRECATIONS
_OSTREE_PUBLIC
OstreeRepoFinderConfig *ostree_repo_finder_config_new (void);
G_END_DECLS
+6
View File
@@ -41,6 +41,7 @@
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
#include "ostree-repo-finder.h"
#include "ostree-repo-finder-config.h"
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
#include <gio/gunixinputstream.h>
@@ -4085,6 +4086,7 @@ ostree_repo_find_remotes_async (OstreeRepo *self,
g_autoptr(FindRemotesData) data = NULL;
GMainContext *context;
OstreeRepoFinder *default_finders[4] = { NULL, };
g_autoptr(OstreeRepoFinder) finder_config = NULL;
g_return_if_fail (OSTREE_IS_REPO (self));
g_return_if_fail (is_valid_collection_ref_array (refs));
@@ -4103,6 +4105,10 @@ ostree_repo_find_remotes_async (OstreeRepo *self,
/* Are we using #OstreeRepoFinders provided by the user, or the defaults? */
if (finders == NULL)
{
finder_config = OSTREE_REPO_FINDER (ostree_repo_finder_config_new ());
default_finders[0] = finder_config;
finders = default_finders;
}
+1
View File
@@ -38,6 +38,7 @@
#ifdef OSTREE_ENABLE_EXPERIMENTAL_API
#include <ostree-ref.h>
#include <ostree-repo-finder.h>
#include <ostree-repo-finder-config.h>
#endif /* OSTREE_ENABLE_EXPERIMENTAL_API */
#include <ostree-autocleanups.h>