Browse Source

Implement pulling and pushing via repobridge instead of HTTPS

This makes the repoBridge integration also fully work with Git <2.9, which introduced
http.extraHeader options.

Signed-off-by: Patrick Uiterwijk <patrick@puiterwijk.org>
Patrick Uiterwijk 5 years ago
parent
commit
8174a49920
7 changed files with 72 additions and 22 deletions
  1. 1 0
      .gitignore
  2. 9 0
      doc/configuration.rst
  3. 2 0
      pagure/default_config.py
  4. 49 21
      pagure/lib/git.py
  5. 3 1
      pagure/lib/model.py
  6. 2 0
      tests/__init__.py
  7. 6 0
      tests/test_pagure_repospanner.py

+ 1 - 0
.gitignore

@@ -56,4 +56,5 @@ dev/docker/test_env
 
 # A possible symlink for the testsuite
 /repospanner
+/repobridge
 /repohookrunner

+ 9 - 0
doc/configuration.rst

@@ -1521,6 +1521,15 @@ Support for this integration has been included in Pagure version 5.0 and higher.
 Here below are the different options one can/should use to integrate pagure
 with repoSpanner.
 
+REPOBRIDGE_BINARY
+~~~~~~~~~~~~~~~~~
+
+This should contain the path to the repoBridge binary, which is used for pushing
+and pulling to/from repoSpanner.
+
+Defaults to: ``/usr/libexec/repobridge``.
+
+
 REPOSPANNER_NEW_REPO
 ~~~~~~~~~~~~~~~~~~~~
 

+ 2 - 0
pagure/default_config.py

@@ -492,6 +492,8 @@ ALLOW_HTTP_PUSH = False
 HTTP_REPO_ACCESS_GITOLITE = "/usr/share/gitolite3/gitolite-shell"
 
 # repoSpanner integration settings
+# Path the the repoBridge binary
+REPOBRIDGE_BINARY = "/usr/libexec/repobridge"
 # Whether to create new repositories on repoSpanner by default.
 # Either None or a region name.
 REPOSPANNER_NEW_REPO = None

+ 49 - 21
pagure/lib/git.py

@@ -907,18 +907,32 @@ class TemporaryClone(object):
             command = [
                 "git",
                 "-c",
-                "http.sslcainfo=%s" % regioninfo["ca"],
-                "-c",
-                "http.sslcert=%s" % regioninfo["push_cert"]["cert"],
-                "-c",
-                "http.sslkey=%s" % regioninfo["push_cert"]["key"],
+                "protocol.ext.allow=always",
                 "clone",
-                repourl,
+                "ext::%s %s"
+                % (
+                    pagure_config["REPOBRIDGE_BINARY"],
+                    self._project._repospanner_repo_name(self._repotype),
+                ),
                 self.repopath,
             ]
+            environ = os.environ.copy()
+            environ.update(
+                {
+                    "USER": "pagure",
+                    "REPOBRIDGE_CONFIG": ":environment:",
+                    "REPOBRIDGE_BASEURL": regioninfo["url"],
+                    "REPOBRIDGE_CA": regioninfo["ca"],
+                    "REPOBRIDGE_CERT": regioninfo["push_cert"]["cert"],
+                    "REPOBRIDGE_KEY": regioninfo["push_cert"]["key"],
+                }
+            )
             with open(os.devnull, "w") as devnull:
                 subprocess.check_call(
-                    command, stdout=devnull, stderr=subprocess.STDOUT
+                    command,
+                    stdout=devnull,
+                    stderr=subprocess.STDOUT,
+                    env=environ,
                 )
             self.repo = pygit2.Repository(self.repopath)
 
@@ -953,7 +967,6 @@ class TemporaryClone(object):
             extra["pull_request_uid"] = extra["pull_request"].uid
             del extra["pull_request"]
 
-        opts = []
         if self._project.is_on_repospanner:
             regioninfo = pagure_config["REPOSPANNER_REGIONS"][
                 self._project.repospanner_region
@@ -970,32 +983,47 @@ class TemporaryClone(object):
                     "project_namespace": self._project.namespace or "",
                 }
             )
-            opts = [
-                "-c",
-                "http.sslcainfo=%s" % regioninfo["ca"],
-                "-c",
-                "http.sslcert=%s" % regioninfo["push_cert"]["cert"],
+            args = []
+            for opt in extra:
+                args.extend(["--extra", opt, extra[opt]])
+            command = [
+                "git",
                 "-c",
-                "http.sslkey=%s" % regioninfo["push_cert"]["key"],
+                "protocol.ext.allow=always",
+                "push",
+                "ext::%s %s %s"
+                % (
+                    pagure_config["REPOBRIDGE_BINARY"],
+                    " ".join(args),
+                    self._project._repospanner_repo_name(self._repotype),
+                ),
+                "--repo",
+                self.repopath,
             ]
-            for extrakey in extra:
-                val = extra[extrakey]
-                opts.extend(
-                    ["-c", "http.extraHeader=X-Extra-%s: %s" % (extrakey, val)]
-                )
+            environ = {
+                "USER": "pagure",
+                "REPOBRIDGE_CONFIG": ":environment:",
+                "REPOBRIDGE_BASEURL": regioninfo["url"],
+                "REPOBRIDGE_CA": regioninfo["ca"],
+                "REPOBRIDGE_CERT": regioninfo["push_cert"]["cert"],
+                "REPOBRIDGE_KEY": regioninfo["push_cert"]["key"],
+            }
+        else:
+            command = ["git", "push", "origin"]
+            environ = {}
 
         try:
             _log.debug(
                 "Running a git push of %s to %s"
                 % (pushref, self._project.fullname)
             )
-            _log.debug("Opts: %s", opts)
             env = os.environ.copy()
             env["GL_USER"] = username
             env["GL_BYPASS_ACCESS_CHECKS"] = "1"
+            env.update(environ)
             env.update(extra)
             out = subprocess.check_output(
-                ["git"] + opts + ["push", "origin", pushref],
+                command + [pushref],
                 cwd=self.repopath,
                 stderr=subprocess.STDOUT,
                 env=env,

+ 3 - 1
pagure/lib/model.py

@@ -522,7 +522,7 @@ class Project(BASE):
         )
         return url, regioninfo
 
-    def _repospanner_repo_name(self, repotype, region):
+    def _repospanner_repo_name(self, repotype, region=None):
         """ Returns the name of a repo as named in repoSpanner.
 
         Args:
@@ -530,6 +530,8 @@ class Project(BASE):
             region (string): repoSpanner region name
         Return type: (string)
         """
+        if region is None:
+            region = self.repospanner_region
         return os.path.join(
             pagure_config["REPOSPANNER_REGIONS"][region].get(
                 "repo_prefix", ""

+ 2 - 0
tests/__init__.py

@@ -100,6 +100,7 @@ CELERY_CONFIG = {
 }
 GIT_AUTH_BACKEND = '%(authbackend)s'
 TEST_AUTH_STATUS = '%(path)s/testauth_status.json'
+REPOBRIDGE_BINARY = '%(repobridge_binary)s'
 REPOSPANNER_NEW_REPO = %(repospanner_new_repo)s
 REPOSPANNER_NEW_REPO_ADMIN_OVERRIDE = %(repospanner_admin_override)s
 REPOSPANNER_NEW_FORK = %(repospanner_new_fork)s
@@ -386,6 +387,7 @@ class SimplePagureTest(unittest.TestCase):
             'global_path': tests_state["path"],
             'authbackend': 'gitolite3',
 
+            'repobridge_binary': '/usr/libexec/repobridge',
             'repospanner_gitport': str(8443 + sys.version_info.major),
             'repospanner_new_repo': 'None',
             'repospanner_admin_override': 'False',

+ 6 - 0
tests/test_pagure_repospanner.py

@@ -138,6 +138,12 @@ class PagureRepoSpannerTests(tests.Modeltests):
                                   'repohookrunner')
         if not os.path.exists(hookrunbin):
             raise Exception('repoSpanner found, but repohookrunner not')
+        repobridgebin = os.path.join(os.path.dirname(self.repospanner_binary),
+                                     'repobridge')
+        if not os.path.exists(repobridgebin):
+            raise Exception('repoSpanner found, but repobridge not')
+
+        self.config_values['repobridge_binary'] = repobridgebin
 
         codepath = os.path.normpath(
             os.path.join(