27
28
from bzrlib import (
30
33
revision as _mod_revision,
33
36
from bzrlib.branch import Branch
34
37
from bzrlib.bzrdir import BzrDir
35
from bzrlib.errors import ParamikoNotPresent
36
38
from bzrlib.smart import client, medium
37
from bzrlib.smart.server import SmartTCPServer
38
from bzrlib.tests import TestCaseWithTransport, TestSkipped
39
from bzrlib.smart.server import BzrServerFactory, SmartTCPServer
40
from bzrlib.tests import (
41
TestCaseWithMemoryTransport,
42
TestCaseWithTransport,
39
45
from bzrlib.trace import mutter
40
46
from bzrlib.transport import get_transport, remote
43
class TestBzrServe(TestCaseWithTransport):
49
class TestBzrServeBase(TestCaseWithTransport):
51
def run_bzr_serve_then_func(self, serve_args, retcode=0, func=None,
52
*func_args, **func_kwargs):
53
"""Run 'bzr serve', and run the given func in a thread once the server
56
When 'func' terminates, the server will be terminated too.
58
Returns stdout and stderr.
61
def on_server_start(backing_urls, tcp_server):
63
target=on_server_start_thread, args=(tcp_server,))
65
def on_server_start_thread(tcp_server):
68
self.tcp_server = tcp_server
71
func(*func_args, **func_kwargs)
73
# Log errors to make some test failures a little less
75
mutter('func broke: %r', e)
77
# Then stop the server
78
mutter('interrupting...')
79
thread.interrupt_main()
80
SmartTCPServer.hooks.install_named_hook(
81
'server_started_ex', on_server_start,
82
'run_bzr_serve_then_func hook')
85
out, err = self.run_bzr(['serve'] + list(serve_args))
86
except KeyboardInterrupt, e:
91
class TestBzrServe(TestBzrServeBase):
94
super(TestBzrServe, self).setUp()
95
self.disable_missing_extensions_warning()
45
97
def assertInetServerShutsdownCleanly(self, process):
46
98
"""Shutdown the server process looking for errors."""
79
131
finish_bzr_subprocess, a client for the server, and a transport.
81
133
# Serve from the current directory
82
process = self.start_bzr_subprocess(['serve', '--inet'])
134
args = ['serve', '--inet']
135
args.extend(extra_options)
136
process = self.start_bzr_subprocess(args)
84
138
# Connect to the server
85
139
# We use this url because while this is no valid URL to connect to this
86
140
# server instance, the transport needs a URL.
87
141
url = 'bzr://localhost/'
88
143
client_medium = medium.SmartSimplePipesClientMedium(
89
144
process.stdout, process.stdin, url)
90
145
transport = remote.RemoteTransport(url, medium=client_medium)
105
160
prefix = 'listening on port: '
106
161
self.assertStartsWith(port_line, prefix)
107
162
port = int(port_line[len(prefix):])
108
return process,'bzr://localhost:%d/' % port
163
url = 'bzr://localhost:%d/' % port
167
def test_bzr_serve_quiet(self):
168
self.make_branch('.')
169
args = ['--port', 'localhost:0', '--quiet']
170
out, err = self.run_bzr_serve_then_func(args, retcode=3)
171
self.assertEqual('', out)
172
self.assertEqual('', err)
110
174
def test_bzr_serve_inet_readonly(self):
111
175
"""bzr server should provide a read only filesystem by default."""
154
219
self.make_read_requests(branch)
155
220
self.assertServerFinishesCleanly(process)
157
def test_bzr_connect_to_bzr_ssh(self):
158
"""User acceptance that get_transport of a bzr+ssh:// behaves correctly.
160
bzr+ssh:// should cause bzr to run a remote bzr smart server over SSH.
163
from bzrlib.transport.sftp import SFTPServer
164
except ParamikoNotPresent:
165
raise TestSkipped('Paramiko not installed')
166
from bzrlib.tests.stub_sftp import StubServer
169
self.make_branch('a_branch')
171
# Start an SSH server
172
self.command_executed = []
173
# XXX: This is horrible -- we define a really dumb SSH server that
174
# executes commands, and manage the hooking up of stdin/out/err to the
175
# SSH channel ourselves. Surely this has already been implemented
177
class StubSSHServer(StubServer):
181
def check_channel_exec_request(self, channel, command):
182
self.test.command_executed.append(command)
183
proc = subprocess.Popen(
184
command, shell=True, stdin=subprocess.PIPE,
185
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
187
# XXX: horribly inefficient, not to mention ugly.
188
# Start a thread for each of stdin/out/err, and relay bytes from
189
# the subprocess to channel and vice versa.
190
def ferry_bytes(read, write, close):
199
(channel.recv, proc.stdin.write, proc.stdin.close),
200
(proc.stdout.read, channel.sendall, channel.close),
201
(proc.stderr.read, channel.sendall_stderr, channel.close)]
202
for read, write, close in file_functions:
203
t = threading.Thread(
204
target=ferry_bytes, args=(read, write, close))
209
ssh_server = SFTPServer(StubSSHServer)
210
# XXX: We *don't* want to override the default SSH vendor, so we set
211
# _vendor to what _get_ssh_vendor returns.
212
self.start_server(ssh_server)
213
port = ssh_server._listener.port
215
# Access the branch via a bzr+ssh URL. The BZR_REMOTE_PATH environment
216
# variable is used to tell bzr what command to run on the remote end.
217
path_to_branch = osutils.abspath('a_branch')
219
orig_bzr_remote_path = os.environ.get('BZR_REMOTE_PATH')
220
bzr_remote_path = self.get_bzr_path()
221
if sys.platform == 'win32':
222
bzr_remote_path = sys.executable + ' ' + self.get_bzr_path()
223
os.environ['BZR_REMOTE_PATH'] = bzr_remote_path
225
if sys.platform == 'win32':
226
path_to_branch = os.path.splitdrive(path_to_branch)[1]
227
branch = Branch.open(
228
'bzr+ssh://fred:secret@localhost:%d%s' % (port, path_to_branch))
229
self.make_read_requests(branch)
230
# Check we can perform write operations
231
branch.bzrdir.root_transport.mkdir('foo')
233
# Restore the BZR_REMOTE_PATH environment variable back to its
235
if orig_bzr_remote_path is None:
236
del os.environ['BZR_REMOTE_PATH']
238
os.environ['BZR_REMOTE_PATH'] = orig_bzr_remote_path
241
['%s serve --inet --directory=/ --allow-writes'
243
self.command_executed)
246
class TestCmdServeChrooting(TestCaseWithTransport):
222
def test_bzr_serve_dhpss(self):
223
# This is a smoke test that the server doesn't crash when run with
224
# -Dhpss, and does drop some hpss logging to the file.
225
self.make_branch('.')
226
log_fname = os.getcwd() + '/server.log'
227
self._captureVar('BZR_LOG', log_fname)
228
process, transport = self.start_server_inet(['-Dhpss'])
229
branch = BzrDir.open_from_transport(transport).open_branch()
230
self.make_read_requests(branch)
231
self.assertInetServerShutsdownCleanly(process)
232
f = open(log_fname, 'rb')
235
self.assertContainsRe(content, r'hpss request: \[[0-9-]+\]')
238
class TestCmdServeChrooting(TestBzrServeBase):
248
240
def test_serve_tcp(self):
249
241
"""'bzr serve' wraps the given --directory in a ChrootServer.
255
247
t = self.get_transport()
256
248
t.mkdir('server-root')
257
249
self.run_bzr_serve_then_func(
258
['--port', '0', '--directory', t.local_abspath('server-root'),
250
['--port', '127.0.0.1:0',
251
'--directory', t.local_abspath('server-root'),
259
252
'--allow-writes'],
260
self.when_server_started)
253
func=self.when_server_started)
261
254
# The when_server_started method issued a find_repositoryV3 that should
262
255
# fail with 'norepository' because there are no repositories inside the
264
257
self.assertEqual(('norepository',), self.client_resp)
266
def run_bzr_serve_then_func(self, serve_args, func, *func_args,
268
"""Run 'bzr serve', and run the given func in a thread once the server
271
When 'func' terminates, the server will be terminated too.
274
def on_server_start(backing_urls, tcp_server):
275
t = threading.Thread(
276
target=on_server_start_thread, args=(tcp_server,))
278
def on_server_start_thread(tcp_server):
281
self.tcp_server = tcp_server
283
func(*func_args, **func_kwargs)
285
# Log errors to make some test failures a little less
287
mutter('func broke: %r', e)
289
# Then stop the server
290
mutter('interrupting...')
291
thread.interrupt_main()
292
SmartTCPServer.hooks.install_named_hook(
293
'server_started_ex', on_server_start,
294
'run_bzr_serve_then_func hook')
297
self.run_bzr(['serve'] + list(serve_args))
298
except KeyboardInterrupt:
301
259
def when_server_started(self):
302
260
# Connect to the TCP server and issue some requests and see what comes
315
273
client_medium.disconnect()
276
class TestUserdirExpansion(TestCaseWithMemoryTransport):
278
def fake_expanduser(self, path):
279
"""A simple, environment-independent, function for the duration of this
282
Paths starting with a path segment of '~user' will expand to start with
283
'/home/user/'. Every other path will be unchanged.
285
if path.split('/', 1)[0] == '~user':
286
return '/home/user' + path[len('~user'):]
289
def make_test_server(self, base_path='/'):
290
"""Make and setUp a BzrServerFactory, backed by a memory transport, and
291
creat '/home/user' in that transport.
293
bzr_server = BzrServerFactory(
294
self.fake_expanduser, lambda t: base_path)
295
mem_transport = self.get_transport()
296
mem_transport.mkdir_multi(['home', 'home/user'])
297
bzr_server.set_up(mem_transport, None, None, inet=True)
298
self.addCleanup(bzr_server.tear_down)
301
def test_bzr_serve_expands_userdir(self):
302
bzr_server = self.make_test_server()
303
self.assertTrue(bzr_server.smart_server.backing_transport.has('~user'))
305
def test_bzr_serve_does_not_expand_userdir_outside_base(self):
306
bzr_server = self.make_test_server('/foo')
307
self.assertFalse(bzr_server.smart_server.backing_transport.has('~user'))
309
def test_get_base_path(self):
310
"""cmd_serve will turn the --directory option into a LocalTransport
311
(optionally decorated with 'readonly+'). BzrServerFactory can
312
determine the original --directory from that transport.
314
# URLs always include the trailing slash, and get_base_path returns it
315
base_dir = osutils.abspath('/a/b/c') + '/'
316
base_url = urlutils.local_path_to_url(base_dir) + '/'
317
# Define a fake 'protocol' to capture the transport that cmd_serve
318
# passes to serve_bzr.
319
def capture_transport(transport, host, port, inet):
320
self.bzr_serve_transport = transport
321
cmd = builtins.cmd_serve()
323
cmd.run(directory=base_dir, protocol=capture_transport)
324
server_maker = BzrServerFactory()
326
'readonly+%s' % base_url, self.bzr_serve_transport.base)
328
base_dir, server_maker.get_base_path(self.bzr_serve_transport))
330
cmd.run(directory=base_dir, protocol=capture_transport,
332
server_maker = BzrServerFactory()
333
self.assertEqual(base_url, self.bzr_serve_transport.base)
334
self.assertEqual(base_dir,
335
server_maker.get_base_path(self.bzr_serve_transport))