launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #25152
[Merge] ~pappacena/turnip:run-v2-commands into turnip:master
Thiago F. Pappacena has proposed merging ~pappacena/turnip:run-v2-commands into turnip:master with ~pappacena/turnip:git-backend-stdin as a prerequisite.
Commit message:
Running, at the backend, v2 commands when requested
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~pappacena/turnip/+git/turnip/+merge/389118
This MP makes the backend run a git using the v2 protocol when a frontend connection requests it. For now, the execution is controlled by `get_capabilities_advertisement` method, which enables the v2 execution path only if we are advertising v2 compatibility.
get_capabilities_advertisement should be changed in a future MP in order to enable compatibility only for certain frontend types.
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~pappacena/turnip:run-v2-commands into turnip:master.
diff --git a/turnip/pack/git.py b/turnip/pack/git.py
index 6ae6dd5..1b34ac0 100644
--- a/turnip/pack/git.py
+++ b/turnip/pack/git.py
@@ -31,11 +31,14 @@ from turnip.config import config
from turnip.helpers import compose_path
from turnip.pack.helpers import (
decode_packet,
+ DELIM_PKT,
decode_request,
encode_packet,
encode_request,
ensure_config,
ensure_hooks,
+ FLUSH_PKT,
+ get_capabilities_advertisement,
INCOMPLETE_PKT,
translate_xmlrpc_fault,
)
@@ -77,7 +80,7 @@ class UnstoppableProducerWrapper(object):
pass
-class PackProtocol(protocol.Protocol):
+class PackProtocol(protocol.Protocol, object):
paused = False
raw = False
@@ -420,7 +423,7 @@ class PackProxyServerProtocol(PackServerProtocol):
def resumeProducing(self):
# Send our translated request and then open the gate to the client.
self.sendNextCommand()
- PackServerProtocol.resumeProducing(self)
+ super(PackProxyServerProtocol, self).resumeProducing()
def readConnectionLost(self):
# Forward the closed stdin down the stack.
@@ -437,6 +440,24 @@ class PackBackendProtocol(PackServerProtocol):
hookrpc_key = None
expect_set_symbolic_ref = False
+ def getV2CommandInput(self, params):
+ """Reconstruct what should be sent to git's stdin from the
+ parameters received."""
+ cmd_input = encode_packet(b"command=%s\n" % params.get(b'command'))
+ for capability in params["capabilities"].split(b"\n"):
+ cmd_input += encode_packet(b"%s\n" % capability)
+ cmd_input += DELIM_PKT
+ ignore_keys = (b'capabilities', b'version')
+ for k, v in params.items():
+ k = six.ensure_binary(k)
+ if k.startswith(b"turnip-") or k in ignore_keys:
+ continue
+ for param_value in v.split(b'\n'):
+ value = (b"" if not param_value else b" %s" % param_value)
+ cmd_input += encode_packet(b"%s%s\n" % (k, value))
+ cmd_input += FLUSH_PKT
+ return cmd_input
+
@defer.inlineCallbacks
def requestReceived(self, command, raw_pathname, params):
self.extractRequestMeta(command, raw_pathname, params)
@@ -465,14 +486,27 @@ class PackBackendProtocol(PackServerProtocol):
cmd_input = None
cmd_env = {}
write_operation = False
- if command == b'git-upload-pack':
- subcmd = b'upload-pack'
- elif command == b'git-receive-pack':
- subcmd = b'receive-pack'
- write_operation = True
+ if not get_capabilities_advertisement(params.get(b'version', 1)):
+ if command == b'git-upload-pack':
+ subcmd = b'upload-pack'
+ elif command == b'git-receive-pack':
+ subcmd = b'receive-pack'
+ write_operation = True
+ else:
+ self.die(b'Unsupported command in request')
+ return
else:
- self.die(b'Unsupported command in request')
- return
+ v2_command = params.get(b'command')
+ if command == b'git-upload-pack' and not v2_command:
+ self.expectNextCommand()
+ self.transport.loseConnection()
+ return
+ subcmd = b'upload-pack'
+ cmd_env["GIT_PROTOCOL"] = 'version=2'
+ send_path_as_option = True
+ # Do not include "advertise-refs" parameter.
+ params.pop(b'turnip-advertise-refs', None)
+ cmd_input = self.getV2CommandInput(params)
args = []
if params.pop(b'turnip-stateless-rpc', None):
diff --git a/turnip/pack/helpers.py b/turnip/pack/helpers.py
index e0d95a1..93aba33 100644
--- a/turnip/pack/helpers.py
+++ b/turnip/pack/helpers.py
@@ -289,3 +289,13 @@ def translate_xmlrpc_fault(code):
else:
result = TurnipFaultCode.INTERNAL_SERVER_ERROR
return result
+
+
+def get_capabilities_advertisement(version='1'):
+ """Returns the capability advertisement binary string to be sent to
+ clients for a given protocol version requested.
+
+ If no binary data is sent, no advertisement is done and we declare to
+ not be compatible with that specific version."""
+ # XXX pappacena 2020-08-11: Return the correct data for protocol v2.
+ return b""
diff --git a/turnip/pack/tests/test_git.py b/turnip/pack/tests/test_git.py
index 5adffb1..b69049c 100644
--- a/turnip/pack/tests/test_git.py
+++ b/turnip/pack/tests/test_git.py
@@ -9,6 +9,7 @@ from __future__ import (
import hashlib
import os.path
+from collections import OrderedDict
from fixtures import TempDir, MonkeyPatch
from pygit2 import init_repository
@@ -283,6 +284,45 @@ class TestPackBackendProtocol(TestCase):
{}),
self.proto.test_process)
+ def test_git_upload_pack_v2_calls_spawnProcess(self):
+ # If the command is git-upload-pack using v2 protocol, requestReceived
+ # calls spawnProcess with appropriate arguments.
+ advertise_capabilities = mock.Mock()
+ self.useFixture(
+ MonkeyPatch("turnip.pack.git.get_capabilities_advertisement",
+ advertise_capabilities))
+ advertise_capabilities.return_value = b'fake capability'
+
+ self.proto.requestReceived(
+ b'git-upload-pack', b'/foo.git', OrderedDict([
+ (b'turnip-x', b'yes'),
+ (b'turnip-request-id', b'123'),
+ (b'version', b'2'),
+ (b'command', b'ls-refs'),
+ (b'capabilities', b'agent=git/2.25.1'),
+ (b'peel', b''),
+ (b'symrefs', b''),
+ (b'ref-prefix', b'HEAD\nrefs/heads/\nrefs/tags/')
+ ]))
+ full_path = os.path.join(six.ensure_binary(self.root), b'foo.git')
+ self.assertEqual(
+ (b'git',
+ [b'git', b'-C', full_path, b'upload-pack', full_path],
+ {'GIT_PROTOCOL': 'version=2'}),
+ self.proto.test_process)
+ stdin_content = (
+ b'0014command=ls-refs\n'
+ b'0015agent=git/2.25.1\n'
+ b'00010014command ls-refs\n'
+ b'0009peel\n'
+ b'000csymrefs\n'
+ b'0014ref-prefix HEAD\n'
+ b'001bref-prefix refs/heads/\n'
+ b'001aref-prefix refs/tags/\n'
+ b'0000'
+ )
+ self.assertEqual(stdin_content, self.proto.peer.cmd_input)
+
def test_git_receive_pack_calls_spawnProcess(self):
# If the command is git-receive-pack, requestReceived calls
# spawnProcess with appropriate arguments.
Follow ups