Add support for "custom remotes"
authorColin Walters <walters@verbum.org>
Tue, 7 Sep 2021 22:02:24 +0000 (18:02 -0400)
committerColin Walters <walters@verbum.org>
Wed, 8 Sep 2021 11:42:25 +0000 (07:42 -0400)
This will be helpful for the "ostree native container" work in
https://github.com/ostreedev/ostree-rs-ext/

Basically in order to reuse GPG/signapi verification, we need
to support adding a remote, even though it can't be used via
`ostree pull`.  (At least, not until we merge ostree-rs-ext into ostree, but
 even then I think the principle stands)

man/ostree.repo-config.xml
src/libostree/ostree-repo-pull.c
src/libostree/ostree-repo.c
src/ostree/ot-remote-builtin-add.c
tests/pull-test.sh

index 335f83e4b399bb3f74d823f50db249d105aa9747..6e2bc7cc4af271da658c72cd5392feece5748f25 100644 (file)
@@ -355,6 +355,14 @@ Boston, MA 02111-1307, USA.
         <listitem><para>If set, pulls from this remote will fail with the configured text.  This is intended for OS vendors which have a subscription process to access content.</para></listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term><varname>custom-backend</varname></term>
+        <listitem><para>If set, pulls from this remote via libostree will fail with an error that mentions the value.
+                        It is recommended to make this a software identifier token (e.g. "examplecorp-fetcher"), not freeform text ("ExampleCorp Fetcher").
+                        This is intended to be used by higher level software that wants to fetch ostree commits via some other mechanism, while still reusing the core libostree infrastructure around e.g. signatures.
+                        </para></listitem>
+      </varlistentry>
+
     </variablelist>
 
   </refsect1>
index 12409e633cff6e39ca4437b63b7293a9ca734c84..04a5359f677bdf450a1c7cc77f8c276bc35bafac 100644 (file)
@@ -3965,6 +3965,7 @@ ostree_repo_pull_with_options (OstreeRepo             *self,
   else
     {
       g_autofree char *unconfigured_state = NULL;
+      g_autofree char *custom_backend = NULL;
 
       g_free (pull_data->remote_name);
       pull_data->remote_name = g_strdup (remote_name_or_baseurl);
@@ -3996,6 +3997,20 @@ ostree_repo_pull_with_options (OstreeRepo             *self,
                        "remote unconfigured-state: %s", unconfigured_state);
           goto out;
         }
+
+      if (!ostree_repo_get_remote_option (self, pull_data->remote_name,
+                                          "custom-backend", NULL,
+                                          &custom_backend,
+                                          error))
+        goto out;
+
+      if (custom_backend)
+        {
+          g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+                       "Cannot fetch via libostree - remote '%s' uses custom backend '%s'", 
+                       pull_data->remote_name, custom_backend);
+          goto out;
+        }
     }
 
   if (pull_data->remote_name && !(disable_sign_verify && disable_sign_verify_summary))
index 0ff2c53f68d39dca25b0c4d187e7276ed5703025..42d2b0e08c395fc683ef6dc883a53d62b6d2316b 100644 (file)
@@ -1589,7 +1589,6 @@ impl_repo_remote_add (OstreeRepo     *self,
                       GError        **error)
 {
   g_return_val_if_fail (name != NULL, FALSE);
-  g_return_val_if_fail (url != NULL, FALSE);
   g_return_val_if_fail (options == NULL || g_variant_is_of_type (options, G_VARIANT_TYPE ("a{sv}")), FALSE);
 
   if (!ostree_validate_remote_name (name, error))
@@ -1637,10 +1636,13 @@ impl_repo_remote_add (OstreeRepo     *self,
       remote->file = g_file_get_child (etc_ostree_remotes_d, basename);
     }
 
-  if (g_str_has_prefix (url, "metalink="))
-    g_key_file_set_string (remote->options, remote->group, "metalink", url + strlen ("metalink="));
-  else
-    g_key_file_set_string (remote->options, remote->group, "url", url);
+  if (url)
+    {
+      if (g_str_has_prefix (url, "metalink="))
+        g_key_file_set_string (remote->options, remote->group, "metalink", url + strlen ("metalink="));
+      else
+        g_key_file_set_string (remote->options, remote->group, "url", url);
+    }
 
   if (options)
     keyfile_set_from_vardict (remote->options, remote->group, options);
@@ -1676,7 +1678,7 @@ impl_repo_remote_add (OstreeRepo     *self,
  * 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)
+ * @url: (allow-none): 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
@@ -1789,7 +1791,6 @@ impl_repo_remote_replace (OstreeRepo     *self,
                           GError        **error)
 {
   g_return_val_if_fail (name != NULL, FALSE);
-  g_return_val_if_fail (url != NULL, FALSE);
   g_return_val_if_fail (options == NULL || g_variant_is_of_type (options, G_VARIANT_TYPE ("a{sv}")), FALSE);
 
   if (!ostree_validate_remote_name (name, error))
@@ -1815,11 +1816,14 @@ impl_repo_remote_replace (OstreeRepo     *self,
       if (!g_key_file_remove_group (remote->options, remote->group, error))
         return FALSE;
 
-      if (g_str_has_prefix (url, "metalink="))
-        g_key_file_set_string (remote->options, remote->group, "metalink",
-                               url + strlen ("metalink="));
-      else
-        g_key_file_set_string (remote->options, remote->group, "url", url);
+      if (url)
+        {
+          if (g_str_has_prefix (url, "metalink="))
+            g_key_file_set_string (remote->options, remote->group, "metalink",
+                                   url + strlen ("metalink="));
+          else
+            g_key_file_set_string (remote->options, remote->group, "url", url);
+        }
 
       if (options != NULL)
         keyfile_set_from_vardict (remote->options, remote->group, options);
@@ -1866,7 +1870,7 @@ impl_repo_remote_replace (OstreeRepo     *self,
  * @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)
+ * @url: (allow-none): 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
index 61539ec134d4877f1ab6eb3dc98ed5667c6b48d6..b08153ec0832d3ea3c1ac3b4ab040f839b350d2d 100644 (file)
@@ -35,6 +35,7 @@ static char *opt_gpg_import;
 static char **opt_sign_verify;
 static char *opt_contenturl;
 static char *opt_collection_id;
+static char *opt_custom_backend;
 static char *opt_sysroot;
 static char *opt_repo;
 
@@ -51,6 +52,7 @@ static GOptionEntry option_entries[] = {
   { "if-not-exists", 0, 0, G_OPTION_ARG_NONE, &opt_if_not_exists, "Do nothing if the provided remote exists", NULL },
   { "force", 0, 0, G_OPTION_ARG_NONE, &opt_force, "Replace the provided remote if it exists", NULL },
   { "gpg-import", 0, 0, G_OPTION_ARG_FILENAME, &opt_gpg_import, "Import GPG key from FILE", "FILE" },
+  { "custom-backend", 0, 0, G_OPTION_ARG_STRING, &opt_custom_backend, "This remote has content not fetched via libostree", "NAME" },
   { "contenturl", 0, 0, G_OPTION_ARG_STRING, &opt_contenturl, "Use URL when fetching content", "URL" },
   { "collection-id", 0, 0, G_OPTION_ARG_STRING, &opt_collection_id,
     "Globally unique ID for this repository as an collection of refs for redistribution to other repositories", "COLLECTION-ID" },
@@ -103,7 +105,7 @@ ot_remote_builtin_add (int argc, char **argv, OstreeCommandInvocation *invocatio
   g_autoptr(OstreeRepo) repo = NULL;
   g_autoptr(GString) sign_verify = NULL;
   const char *remote_name;
-  const char *remote_url;
+  const char *remote_url = NULL;
   g_autoptr(GVariantBuilder) optbuilder = NULL;
   g_autoptr(GVariant) options = NULL;
   gboolean ret = FALSE;
@@ -119,11 +121,26 @@ ot_remote_builtin_add (int argc, char **argv, OstreeCommandInvocation *invocatio
                                             cancellable, error))
     goto out;
 
-  if (argc < 3)
+  if (opt_custom_backend)
     {
-      ot_util_usage_error (context, "NAME and URL must be specified", error);
-      goto out;
+      if (argc < 2)
+        {
+          ot_util_usage_error (context, "NAME must be specified", error);
+          goto out;
+        }
+      if (argc >= 3)
+        remote_url = argv[2];
+    }
+  else
+    {
+      if (argc < 3)
+        {
+          ot_util_usage_error (context, "NAME and URL must be specified", error);
+          goto out;
+        }
+      remote_url = argv[2];
     }
+  remote_name = argv[1];
 
   if (opt_if_not_exists && opt_force)
     {
@@ -133,9 +150,6 @@ ot_remote_builtin_add (int argc, char **argv, OstreeCommandInvocation *invocatio
       goto out;
     }
 
-  remote_name = argv[1];
-  remote_url  = argv[2];
-
   optbuilder = g_variant_builder_new (G_VARIANT_TYPE ("a{sv}"));
 
   if (argc > 3)
@@ -159,6 +173,9 @@ ot_remote_builtin_add (int argc, char **argv, OstreeCommandInvocation *invocatio
   if (opt_contenturl != NULL)
     g_variant_builder_add (optbuilder, "{s@v}",
                            "contenturl", g_variant_new_variant (g_variant_new_string (opt_contenturl)));
+  if (opt_custom_backend != NULL)
+    g_variant_builder_add (optbuilder, "{s@v}",
+                           "custom-backend", g_variant_new_variant (g_variant_new_string (opt_custom_backend)));
 
   for (char **iter = opt_set; iter && *iter; iter++)
     {
index 274bd97863b98a84fc2b6048e43d5832d51fce65..fcc2281266e78819cfe4b6c2426cf9fbe372724c 100644 (file)
@@ -55,10 +55,10 @@ function verify_initial_contents() {
 }
 
 if has_gpgme; then
-    echo "1..37"
+    echo "1..38"
 else
     # 3 tests needs GPG support
-    echo "1..34"
+    echo "1..35"
 fi
 
 # Try both syntaxes
@@ -598,6 +598,33 @@ assert_file_has_content err.txt "ONE BILLION DOLLARS"
 
 echo "ok unconfigured"
 
+cd ${test_tmpdir}
+${CMD_PREFIX} ostree --repo=repo remote add --custom-backend=ostree-rs-ext fromcontainer
+if ${CMD_PREFIX} ostree --repo=repo pull fromcontainer 2>err.txt; then
+    assert_not_reached "pull unexpectedly succeeded?"
+fi
+assert_file_has_content err.txt "remote 'fromcontainer' uses custom backend 'ostree-rs-ext'"
+
+for x in show-url refs; do
+if ${CMD_PREFIX} ostree --repo=repo remote "$x" fromcontainer 2>err.txt; then
+assert_file_has_content err.txt "remote 'fromcontainer' uses custom backend 'ostree-rs-ext'"
+    assert_not_reached "no url expected"
+fi
+assert_file_has_content err.txt "No \"url\" option in remote"
+done
+${CMD_PREFIX} ostree --repo=repo remote delete fromcontainer
+
+${CMD_PREFIX} ostree --repo=repo remote add --custom-backend=ostree-rs-ext fromcontainer2 docker://quay.io/examplecorp/foo
+if ${CMD_PREFIX} ostree --repo=repo pull fromcontainer2 main 2>err.txt; then
+    assert_not_reached "pull unexpectedly succeeded?"
+fi
+assert_file_has_content err.txt "remote 'fromcontainer2' uses custom backend 'ostree-rs-ext'"
+${CMD_PREFIX} ostree --repo=repo remote show-url fromcontainer2 >out.txt
+assert_file_has_content out.txt docker://quay.io/examplecorp/foo
+${CMD_PREFIX} ostree --repo=repo remote delete fromcontainer2
+
+echo "ok custom backend"
+
 cd ${test_tmpdir}
 repo_init
 ${CMD_PREFIX} ostree --repo=repo remote add origin-bad $(cat httpd-address)/ostree/noent