Add more flexible _remote_change() API , expose via 'ostree remote'

For Anaconda, I needed OSTREE_REPO_REMOTE_CHANGE_ADD_IF_NOT_EXISTS,
with the GFile *sysroot argument to avoid ugly hacks.  We want to
write the content provided via "ostreesetup" as a remote to the target
chroot only in the case where it isn't provided as part of the tree
content itself.

This is also potentially useful in idempotent systems management tools
like Ansible.

https://bugzilla.gnome.org/show_bug.cgi?id=741577
This commit is contained in:
Colin Walters 2014-12-15 16:21:15 -05:00
parent 8067e977a7
commit f6a6e68412
4 changed files with 193 additions and 44 deletions

View File

@ -616,33 +616,16 @@ keyfile_set_from_vardict (GKeyFile *keyfile,
}
}
/**
* ostree_repo_remote_add:
* @self: Repo
* @name: Name of 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
*
* Create a new remote named @name pointing to @url. If @options is
* provided, then it will be mapped to #GKeyFile entries, where the
* GVariant dictionary key is an option string, and the value is
* mapped as follows:
* * s: g_key_file_set_string()
* * b: g_key_file_set_boolean()
* * as: g_key_file_set_string_list()
*
*/
gboolean
ostree_repo_remote_add (OstreeRepo *self,
const char *name,
const char *url,
GVariant *options,
GCancellable *cancellable,
GError **error)
static gboolean
impl_repo_remote_add (OstreeRepo *self,
GFile *sysroot,
gboolean if_not_exists,
const char *name,
const char *url,
GVariant *options,
GCancellable *cancellable,
GError **error)
{
gs_unref_object GFile *etc_ostree_remotes_d = g_file_new_for_path (SYSCONFDIR "/ostree/remotes.d");
local_cleanup_remote OstreeRemote *remote = NULL;
gboolean ret = FALSE;
@ -659,8 +642,12 @@ ostree_repo_remote_add (OstreeRepo *self,
}
remote = ost_repo_get_remote (self, name, NULL);
if (remote != NULL)
if (remote != NULL && if_not_exists)
{
ret = TRUE;
goto out;
}
else if (remote != NULL)
{
GFile *file;
@ -680,9 +667,17 @@ ostree_repo_remote_add (OstreeRepo *self,
remote->name = g_strdup (name);
remote->group = g_strdup_printf ("remote \"%s\"", name);
if (ostree_repo_is_system (self))
if (sysroot != NULL || ostree_repo_is_system (self))
{
const char *sysconf_remotes = SYSCONFDIR "/ostree/remotes.d";
gs_free char *basename = g_strconcat (name, ".conf", NULL);
gs_unref_object GFile *etc_ostree_remotes_d = NULL;
if (sysroot == NULL)
etc_ostree_remotes_d = g_file_new_for_path (sysconf_remotes);
else
etc_ostree_remotes_d = g_file_resolve_relative_path (sysroot, sysconf_remotes + 1);
remote->file = g_file_get_child (etc_ostree_remotes_d, basename);
}
@ -727,21 +722,42 @@ ostree_repo_remote_add (OstreeRepo *self,
}
/**
* ostree_repo_remote_delete:
* ostree_repo_remote_add:
* @self: Repo
* @name: Name of 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
*
* Delete the remote named @name. It is an error if the provided
* remote does not exist.
* Create a new remote named @name pointing to @url. If @options is
* provided, then it will be mapped to #GKeyFile entries, where the
* GVariant dictionary key is an option string, and the value is
* mapped as follows:
* * s: g_key_file_set_string()
* * b: g_key_file_set_boolean()
* * as: g_key_file_set_string_list()
*
*/
gboolean
ostree_repo_remote_delete (OstreeRepo *self,
const char *name,
GCancellable *cancellable,
GError **error)
ostree_repo_remote_add (OstreeRepo *self,
const char *name,
const char *url,
GVariant *options,
GCancellable *cancellable,
GError **error)
{
return impl_repo_remote_add (self, NULL, FALSE, name, url, options,
cancellable, error);
}
static gboolean
impl_repo_remote_delete (OstreeRepo *self,
GFile *sysroot,
gboolean if_exists,
const char *name,
GCancellable *cancellable,
GError **error)
{
local_cleanup_remote OstreeRemote *remote = NULL;
gboolean ret = FALSE;
@ -756,7 +772,17 @@ ostree_repo_remote_delete (OstreeRepo *self,
goto out;
}
remote = ost_repo_get_remote (self, name, error);
if (if_exists)
{
remote = ost_repo_get_remote (self, name, NULL);
if (!remote)
{
ret = TRUE;
goto out;
}
}
else
remote = ost_repo_get_remote (self, name, error);
if (remote == NULL)
goto out;
@ -789,6 +815,71 @@ ostree_repo_remote_delete (OstreeRepo *self,
return ret;
}
/**
* ostree_repo_remote_delete:
* @self: Repo
* @name: Name of remote
* @cancellable: Cancellable
* @error: Error
*
* Delete the remote named @name. It is an error if the provided
* remote does not exist.
*
*/
gboolean
ostree_repo_remote_delete (OstreeRepo *self,
const char *name,
GCancellable *cancellable,
GError **error)
{
return impl_repo_remote_delete (self, NULL, FALSE, name, cancellable, error);
}
/**
* ostree_repo_remote_change:
* @self: Repo
* @sysroot: (allow-none): System root
* @changeop: Operation to perform
* @name: Name of 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
*
* A combined function handling the equivalent of
* ostree_repo_remote_add(), ostree_repo_remote_delete(), with more
* options.
*
*
*/
gboolean
ostree_repo_remote_change (OstreeRepo *self,
GFile *sysroot,
OstreeRepoRemoteChange changeop,
const char *name,
const char *url,
GVariant *options,
GCancellable *cancellable,
GError **error)
{
switch (changeop)
{
case OSTREE_REPO_REMOTE_CHANGE_ADD:
return impl_repo_remote_add (self, sysroot, FALSE, name, url, options,
cancellable, error);
case OSTREE_REPO_REMOTE_CHANGE_ADD_IF_NOT_EXISTS:
return impl_repo_remote_add (self, sysroot, TRUE, name, url, options,
cancellable, error);
case OSTREE_REPO_REMOTE_CHANGE_DELETE:
return impl_repo_remote_delete (self, sysroot, FALSE, name,
cancellable, error);
case OSTREE_REPO_REMOTE_CHANGE_DELETE_IF_EXISTS:
return impl_repo_remote_delete (self, sysroot, TRUE, name,
cancellable, error);
}
g_assert_not_reached ();
}
/**
* ostree_repo_remote_get_url:
* @self: Repo

View File

@ -79,6 +79,22 @@ gboolean ostree_repo_remote_delete (OstreeRepo *self,
GCancellable *cancellable,
GError **error);
typedef enum {
OSTREE_REPO_REMOTE_CHANGE_ADD,
OSTREE_REPO_REMOTE_CHANGE_ADD_IF_NOT_EXISTS,
OSTREE_REPO_REMOTE_CHANGE_DELETE,
OSTREE_REPO_REMOTE_CHANGE_DELETE_IF_EXISTS
} OstreeRepoRemoteChange;
gboolean ostree_repo_remote_change (OstreeRepo *self,
GFile *sysroot,
OstreeRepoRemoteChange changeop,
const char *name,
const char *url,
GVariant *options,
GCancellable *cancellable,
GError **error);
gboolean ostree_repo_remote_get_url (OstreeRepo *self,
const char *name,
char **out_url,

View File

@ -55,10 +55,12 @@ parse_keyvalue (const char *keyvalue,
static char **opt_set;
static gboolean opt_no_gpg_verify;
static gboolean opt_if_not_exists;
static GOptionEntry add_option_entries[] = {
{ "set", 0, 0, G_OPTION_ARG_STRING_ARRAY, &opt_set, "Set config option KEY=VALUE for remote", "KEY=VALUE" },
{ "no-gpg-verify", 0, 0, G_OPTION_ARG_NONE, &opt_no_gpg_verify, "Disable GPG verification", NULL },
{ "if-not-exists", 0, 0, G_OPTION_ARG_NONE, &opt_if_not_exists, "Do nothing if the provided remote exists", NULL },
{ NULL }
};
@ -124,17 +126,25 @@ ostree_remote_builtin_add (int argc, char **argv, GCancellable *cancellable, GEr
"gpg-verify",
g_variant_new_variant (g_variant_new_boolean (FALSE)));
ret = ostree_repo_remote_add (repo, remote_name, remote_url,
g_variant_builder_end (optbuilder),
cancellable, error);
if (!ostree_repo_remote_change (repo, NULL,
opt_if_not_exists ? OSTREE_REPO_REMOTE_CHANGE_ADD_IF_NOT_EXISTS :
OSTREE_REPO_REMOTE_CHANGE_ADD,
remote_name, remote_url,
g_variant_builder_end (optbuilder),
cancellable, error))
goto out;
ret = TRUE;
out:
g_option_context_free (context);
return ret;
}
gboolean opt_if_exists = FALSE;
static GOptionEntry delete_option_entries[] = {
{ "if-exists", 0, 0, G_OPTION_ARG_NONE, &opt_if_exists, "Do nothing if the provided remote does not exist", NULL },
{ NULL }
};
@ -160,8 +170,14 @@ ostree_remote_builtin_delete (int argc, char **argv, GCancellable *cancellable,
remote_name = argv[1];
ret = ostree_repo_remote_delete (repo, remote_name, cancellable, error);
if (!ostree_repo_remote_change (repo, NULL,
opt_if_exists ? OSTREE_REPO_REMOTE_CHANGE_DELETE_IF_EXISTS :
OSTREE_REPO_REMOTE_CHANGE_DELETE,
remote_name, NULL, NULL,
cancellable, error))
goto out;
ret = TRUE;
out:
g_option_context_free (context);

View File

@ -25,14 +25,26 @@ echo '1..3'
setup_test_repository "bare"
$OSTREE remote add origin http://example.com/ostree/gnome
echo "ok remote add"
assert_file_has_content $test_tmpdir/repo/config "example.com"
$OSTREE remote show-url origin >/dev/null
echo "ok config"
$OSTREE remote add --no-gpg-verify another http://another.com/repo
assert_file_has_content $test_tmpdir/repo/config "gpg-verify=false"
$OSTREE remote show-url another >/dev/null
echo "ok remote no gpg-verify"
if $OSTREE remote add --no-gpg-verify another http://another.example.com/anotherrepo 2>err.txt; then
assert_not_reached "Adding duplicate remote unexpectedly succeeded"
fi
echo "ok"
$OSTREE remote add --if-not-exists --no-gpg-verify another http://another.example.com/anotherrepo
$OSTREE remote show-url another >/dev/null
echo "ok"
$OSTREE remote add --if-not-exists --no-gpg-verify another-noexist http://another-noexist.example.com/anotherrepo
$OSTREE remote show-url another-noexist >/dev/null
echo "ok"
$OSTREE remote delete another
echo "ok remote delete"
@ -41,4 +53,18 @@ if $OSTREE remote delete nosuchremote 2>err.txt; then
fi
assert_file_has_content err.txt "error: "
$OSTREE remote delete --if-exists nosuchremote
echo "ok"
if $OSTREE remote show-url nosuchremote 2>/dev/null; then
assert_not_reached "Deleting remote unexpectedly failed"
fi
echo "ok"
$OSTREE remote delete --if-exists origin
echo "ok"
if $OSTREE remote show-url origin 2>/dev/null; then
assert_not_reached "Deleting remote unexpectedly failed"
fi
echo "ok"