18
18
"""Tests of the bzr serve command."""
26
28
from bzrlib import (
29
32
revision as _mod_revision,
31
35
from bzrlib.branch import Branch
32
36
from bzrlib.bzrdir import BzrDir
33
from bzrlib.errors import ParamikoNotPresent
34
from bzrlib.smart import medium
35
from bzrlib.tests import TestCaseWithTransport, TestSkipped
37
from bzrlib.smart import client, medium
38
from bzrlib.smart.server import BzrServerFactory, SmartTCPServer
39
from bzrlib.tests import (
41
TestCaseWithMemoryTransport,
42
TestCaseWithTransport,
45
from bzrlib.trace import mutter
36
46
from bzrlib.transport import get_transport, remote
39
class TestBzrServe(TestCaseWithTransport):
48
class TestBzrServeBase(TestCaseWithTransport):
50
def run_bzr_serve_then_func(self, serve_args, retcode=0, func=None,
51
*func_args, **func_kwargs):
52
"""Run 'bzr serve', and run the given func in a thread once the server
55
When 'func' terminates, the server will be terminated too.
57
Returns stdout and stderr.
60
def on_server_start(backing_urls, tcp_server):
62
target=on_server_start_thread, args=(tcp_server,))
64
def on_server_start_thread(tcp_server):
67
self.tcp_server = tcp_server
70
func(*func_args, **func_kwargs)
72
# Log errors to make some test failures a little less
74
mutter('func broke: %r', e)
76
# Then stop the server
77
mutter('interrupting...')
78
thread.interrupt_main()
79
SmartTCPServer.hooks.install_named_hook(
80
'server_started_ex', on_server_start,
81
'run_bzr_serve_then_func hook')
84
out, err = self.run_bzr(['serve'] + list(serve_args))
85
except KeyboardInterrupt, e:
90
class TestBzrServe(TestBzrServeBase):
93
super(TestBzrServe, self).setUp()
94
self.disable_missing_extensions_warning()
41
96
def assertInetServerShutsdownCleanly(self, process):
42
97
"""Shutdown the server process looking for errors."""
150
215
self.make_read_requests(branch)
151
216
self.assertServerFinishesCleanly(process)
153
def test_bzr_connect_to_bzr_ssh(self):
154
"""User acceptance that get_transport of a bzr+ssh:// behaves correctly.
156
bzr+ssh:// should cause bzr to run a remote bzr smart server over SSH.
159
from bzrlib.transport.sftp import SFTPServer
160
except ParamikoNotPresent:
161
raise TestSkipped('Paramiko not installed')
162
from bzrlib.tests.stub_sftp import StubServer
165
self.make_branch('a_branch')
167
# Start an SSH server
168
self.command_executed = []
169
# XXX: This is horrible -- we define a really dumb SSH server that
170
# executes commands, and manage the hooking up of stdin/out/err to the
171
# SSH channel ourselves. Surely this has already been implemented
173
class StubSSHServer(StubServer):
177
def check_channel_exec_request(self, channel, command):
178
self.test.command_executed.append(command)
179
proc = subprocess.Popen(
180
command, shell=True, stdin=subprocess.PIPE,
181
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
183
# XXX: horribly inefficient, not to mention ugly.
184
# Start a thread for each of stdin/out/err, and relay bytes from
185
# the subprocess to channel and vice versa.
186
def ferry_bytes(read, write, close):
195
(channel.recv, proc.stdin.write, proc.stdin.close),
196
(proc.stdout.read, channel.sendall, channel.close),
197
(proc.stderr.read, channel.sendall_stderr, channel.close)]
198
for read, write, close in file_functions:
199
t = threading.Thread(
200
target=ferry_bytes, args=(read, write, close))
205
ssh_server = SFTPServer(StubSSHServer)
206
# XXX: We *don't* want to override the default SSH vendor, so we set
207
# _vendor to what _get_ssh_vendor returns.
209
self.addCleanup(ssh_server.tearDown)
210
port = ssh_server._listener.port
212
# Access the branch via a bzr+ssh URL. The BZR_REMOTE_PATH environment
213
# variable is used to tell bzr what command to run on the remote end.
214
path_to_branch = osutils.abspath('a_branch')
216
orig_bzr_remote_path = os.environ.get('BZR_REMOTE_PATH')
217
bzr_remote_path = self.get_bzr_path()
218
if sys.platform == 'win32':
219
bzr_remote_path = sys.executable + ' ' + self.get_bzr_path()
220
os.environ['BZR_REMOTE_PATH'] = bzr_remote_path
222
if sys.platform == 'win32':
223
path_to_branch = os.path.splitdrive(path_to_branch)[1]
224
branch = Branch.open(
225
'bzr+ssh://fred:secret@localhost:%d%s' % (port, path_to_branch))
226
self.make_read_requests(branch)
227
# Check we can perform write operations
228
branch.bzrdir.root_transport.mkdir('foo')
230
# Restore the BZR_REMOTE_PATH environment variable back to its
232
if orig_bzr_remote_path is None:
233
del os.environ['BZR_REMOTE_PATH']
235
os.environ['BZR_REMOTE_PATH'] = orig_bzr_remote_path
238
['%s serve --inet --directory=/ --allow-writes'
240
self.command_executed)
219
class TestCmdServeChrooting(TestBzrServeBase):
221
def test_serve_tcp(self):
222
"""'bzr serve' wraps the given --directory in a ChrootServer.
224
So requests that search up through the parent directories (like
225
find_repositoryV3) will give "not found" responses, rather than
226
InvalidURLJoin or jail break errors.
228
t = self.get_transport()
229
t.mkdir('server-root')
230
self.run_bzr_serve_then_func(
231
['--port', '127.0.0.1:0',
232
'--directory', t.local_abspath('server-root'),
234
func=self.when_server_started)
235
# The when_server_started method issued a find_repositoryV3 that should
236
# fail with 'norepository' because there are no repositories inside the
238
self.assertEqual(('norepository',), self.client_resp)
240
def when_server_started(self):
241
# Connect to the TCP server and issue some requests and see what comes
243
client_medium = medium.SmartTCPClientMedium(
244
'127.0.0.1', self.tcp_server.port,
245
'bzr://localhost:%d/' % (self.tcp_server.port,))
246
smart_client = client._SmartClient(client_medium)
247
resp = smart_client.call('mkdir', 'foo', '')
248
resp = smart_client.call('BzrDirFormat.initialize', 'foo/')
250
resp = smart_client.call('BzrDir.find_repositoryV3', 'foo/')
251
except errors.ErrorFromSmartServer, e:
253
self.client_resp = resp
254
client_medium.disconnect()
257
class TestUserdirExpansion(TestCaseWithMemoryTransport):
259
def fake_expanduser(self, path):
260
"""A simple, environment-independent, function for the duration of this
263
Paths starting with a path segment of '~user' will expand to start with
264
'/home/user/'. Every other path will be unchanged.
266
if path.split('/', 1)[0] == '~user':
267
return '/home/user' + path[len('~user'):]
270
def make_test_server(self, base_path='/'):
271
"""Make and setUp a BzrServerFactory, backed by a memory transport, and
272
creat '/home/user' in that transport.
274
bzr_server = BzrServerFactory(
275
self.fake_expanduser, lambda t: base_path)
276
mem_transport = self.get_transport()
277
mem_transport.mkdir_multi(['home', 'home/user'])
278
bzr_server.set_up(mem_transport, None, None, inet=True)
279
self.addCleanup(bzr_server.tear_down)
282
def test_bzr_serve_expands_userdir(self):
283
bzr_server = self.make_test_server()
284
self.assertTrue(bzr_server.smart_server.backing_transport.has('~user'))
286
def test_bzr_serve_does_not_expand_userdir_outside_base(self):
287
bzr_server = self.make_test_server('/foo')
288
self.assertFalse(bzr_server.smart_server.backing_transport.has('~user'))
290
def test_get_base_path(self):
291
"""cmd_serve will turn the --directory option into a LocalTransport
292
(optionally decorated with 'readonly+'). BzrServerFactory can
293
determine the original --directory from that transport.
295
# URLs always include the trailing slash, and get_base_path returns it
296
base_dir = osutils.abspath('/a/b/c') + '/'
297
base_url = urlutils.local_path_to_url(base_dir) + '/'
298
# Define a fake 'protocol' to capture the transport that cmd_serve
299
# passes to serve_bzr.
300
def capture_transport(transport, host, port, inet):
301
self.bzr_serve_transport = transport
302
cmd = builtins.cmd_serve()
304
cmd.run(directory=base_dir, protocol=capture_transport)
305
server_maker = BzrServerFactory()
307
'readonly+%s' % base_url, self.bzr_serve_transport.base)
309
base_dir, server_maker.get_base_path(self.bzr_serve_transport))
311
cmd.run(directory=base_dir, protocol=capture_transport,
313
server_maker = BzrServerFactory()
314
self.assertEqual(base_url, self.bzr_serve_transport.base)
315
self.assertEqual(base_dir,
316
server_maker.get_base_path(self.bzr_serve_transport))