~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/blackbox/test_serve.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-09-19 03:52:02 UTC
  • mfrom: (2018.1.11 bzr+ssh:// testing)
  • Revision ID: pqm@pqm.ubuntu.com-20060919035202-8174b4dc7ff91add
(Andrew Bennetts, Robert Collins) Add bzr+ssh:// url support and turn the smart server into a anonymous readonly server by default.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
"""Tests of the bzr serve command."""
19
19
 
 
20
import os
20
21
import signal
 
22
import subprocess
 
23
import threading
21
24
 
 
25
from bzrlib import errors
22
26
from bzrlib.branch import Branch
23
27
from bzrlib.bzrdir import BzrDir
24
 
from bzrlib.tests import TestCaseWithTransport
25
 
from bzrlib.transport import smart
 
28
from bzrlib.errors import ParamikoNotPresent
 
29
from bzrlib.tests import TestCaseWithTransport, TestSkipped
 
30
from bzrlib.transport import get_transport, smart
26
31
 
27
32
 
28
33
class DoesNotCloseStdOutClient(smart.SmartStreamClient):
41
46
 
42
47
class TestBzrServe(TestCaseWithTransport):
43
48
 
44
 
    def test_bzr_serve_inet(self):
45
 
        # Make a branch
46
 
        self.make_branch('.')
47
 
 
48
 
        # Serve that branch from the current directory
49
 
        process = self.start_bzr_subprocess(['serve', '--inet'])
50
 
 
51
 
        # Connect to the server
52
 
        # We use this url because while this is no valid URL to connect to this
53
 
        # server instance, the transport needs a URL.
54
 
        client = DoesNotCloseStdOutClient(
55
 
            lambda: (process.stdout, process.stdin))
56
 
        transport = smart.SmartTransport('bzr://localhost/', client=client)
57
 
 
58
 
        # We get a working branch
59
 
        branch = BzrDir.open_from_transport(transport).open_branch()
60
 
        branch.repository.get_revision_graph()
61
 
        self.assertEqual(None, branch.last_revision())
62
 
 
63
 
        # finish with the transport
64
 
        del transport
 
49
    def assertInetServerShutsdownCleanly(self, client, process):
 
50
        """Shutdown the server process looking for errors."""
65
51
        # Disconnect the client forcefully JUST IN CASE because of __del__'s use
66
52
        # in the smart module.
67
53
        client.disconnect()
75
61
        self.assertEqual('', result[0])
76
62
        self.assertEqual('', result[1])
77
63
    
78
 
    def test_bzr_serve_port(self):
79
 
        # Make a branch
80
 
        self.make_branch('.')
81
 
 
82
 
        # Serve that branch from the current directory
83
 
        process = self.start_bzr_subprocess(['serve', '--port', 'localhost:0'],
84
 
                                            skip_if_plan_to_signal=True)
 
64
    def assertServerFinishesCleanly(self, process):
 
65
        """Shutdown the bzr serve instance process looking for errors."""
 
66
        # Shutdown the server
 
67
        result = self.finish_bzr_subprocess(process, retcode=3,
 
68
                                            send_signal=signal.SIGINT)
 
69
        self.assertEqual('', result[0])
 
70
        self.assertEqual('bzr: interrupted\n', result[1])
 
71
 
 
72
    def start_server_inet(self, extra_options=()):
 
73
        """Start a bzr server subprocess using the --inet option.
 
74
 
 
75
        :param extra_options: extra options to give the server.
 
76
        :return: a tuple with the bzr process handle for passing to
 
77
            finish_bzr_subprocess, a client for the server, and a transport.
 
78
        """
 
79
        # Serve from the current directory
 
80
        process = self.start_bzr_subprocess(['serve', '--inet'])
 
81
 
 
82
        # Connect to the server
 
83
        # We use this url because while this is no valid URL to connect to this
 
84
        # server instance, the transport needs a URL.
 
85
        client = DoesNotCloseStdOutClient(
 
86
            lambda: (process.stdout, process.stdin))
 
87
        transport = smart.SmartTransport('bzr://localhost/', client=client)
 
88
        return process, client, transport
 
89
 
 
90
    def start_server_port(self, extra_options=()):
 
91
        """Start a bzr server subprocess.
 
92
 
 
93
        :param extra_options: extra options to give the server.
 
94
        :return: a tuple with the bzr process handle for passing to
 
95
            finish_bzr_subprocess, and the base url for the server.
 
96
        """
 
97
        # Serve from the current directory
 
98
        args = ['serve', '--port', 'localhost:0']
 
99
        args.extend(extra_options)
 
100
        process = self.start_bzr_subprocess(args, skip_if_plan_to_signal=True)
85
101
        port_line = process.stdout.readline()
86
102
        prefix = 'listening on port: '
87
103
        self.assertStartsWith(port_line, prefix)
88
104
        port = int(port_line[len(prefix):])
 
105
        return process,'bzr://localhost:%d/' % port
 
106
 
 
107
    def test_bzr_serve_inet_readonly(self):
 
108
        """bzr server should provide a read only filesystem by default."""
 
109
        process, client, transport = self.start_server_inet()
 
110
        self.assertRaises(errors.TransportNotPossible, transport.mkdir, 'adir')
 
111
        # finish with the transport
 
112
        del transport
 
113
        self.assertInetServerShutsdownCleanly(client, process)
 
114
 
 
115
    def test_bzr_serve_inet_readwrite(self):
 
116
        # Make a branch
 
117
        self.make_branch('.')
 
118
 
 
119
        process, client, transport = self.start_server_inet(['--allow-writes'])
 
120
 
 
121
        # We get a working branch
 
122
        branch = BzrDir.open_from_transport(transport).open_branch()
 
123
        branch.repository.get_revision_graph()
 
124
        self.assertEqual(None, branch.last_revision())
 
125
 
 
126
        # finish with the transport
 
127
        del transport
 
128
 
 
129
        self.assertInetServerShutsdownCleanly(client, process)
 
130
 
 
131
    def test_bzr_serve_port_readonly(self):
 
132
        """bzr server should provide a read only filesystem by default."""
 
133
        process, url = self.start_server_port()
 
134
        transport = get_transport(url)
 
135
        self.assertRaises(errors.TransportNotPossible, transport.mkdir, 'adir')
 
136
        self.assertServerFinishesCleanly(process)
 
137
 
 
138
    def test_bzr_serve_port_readwrite(self):
 
139
        # Make a branch
 
140
        self.make_branch('.')
 
141
 
 
142
        process, url = self.start_server_port(['--allow-writes'])
89
143
 
90
144
        # Connect to the server
91
 
        branch = Branch.open('bzr://localhost:%d/' % port)
 
145
        branch = Branch.open(url)
92
146
 
93
147
        # We get a working branch
94
148
        branch.repository.get_revision_graph()
95
149
        self.assertEqual(None, branch.last_revision())
96
150
 
97
 
        # Shutdown the server
98
 
        result = self.finish_bzr_subprocess(process, retcode=3,
99
 
                                            send_signal=signal.SIGINT)
100
 
        self.assertEqual('', result[0])
101
 
        self.assertEqual('bzr: interrupted\n', result[1])
 
151
        self.assertServerFinishesCleanly(process)
102
152
 
103
153
    def test_bzr_serve_no_args(self):
104
154
        """'bzr serve' with no arguments or options should not traceback."""
105
155
        out, err = self.run_bzr_error(
106
156
            ['bzr serve requires one of --inet or --port'], 'serve')
107
157
 
 
158
    def test_bzr_connect_to_bzr_ssh(self):
 
159
        """User acceptance that get_transport of a bzr+ssh:// behaves correctly.
 
160
 
 
161
        bzr+ssh:// should cause bzr to run a remote bzr smart server over SSH.
 
162
        """
 
163
        try:
 
164
            from bzrlib.transport.sftp import SFTPServer
 
165
        except ParamikoNotPresent:
 
166
            raise TestSkipped('Paramiko not installed')
 
167
        from bzrlib.tests.stub_sftp import StubServer
 
168
        
 
169
        # Make a branch
 
170
        self.make_branch('a_branch')
 
171
 
 
172
        # Start an SSH server
 
173
        self.command_executed = []
 
174
        # XXX: This is horrible -- we define a really dumb SSH server that
 
175
        # executes commands, and manage the hooking up of stdin/out/err to the
 
176
        # SSH channel ourselves.  Surely this has already been implemented
 
177
        # elsewhere?
 
178
        class StubSSHServer(StubServer):
 
179
 
 
180
            test = self
 
181
 
 
182
            def check_channel_exec_request(self, channel, command):
 
183
                self.test.command_executed.append(command)
 
184
                proc = subprocess.Popen(
 
185
                    command, shell=True, stdin=subprocess.PIPE,
 
186
                    stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
187
                
 
188
                # XXX: horribly inefficient, not to mention ugly.
 
189
                # Start a thread for each of stdin/out/err, and relay bytes from
 
190
                # the subprocess to channel and vice versa.
 
191
                def ferry_bytes(read, write, close):
 
192
                    while True:
 
193
                        bytes = read(1)
 
194
                        if bytes == '':
 
195
                            close()
 
196
                            break
 
197
                        write(bytes)
 
198
 
 
199
                file_functions = [
 
200
                    (channel.recv, proc.stdin.write, proc.stdin.close),
 
201
                    (proc.stdout.read, channel.sendall, channel.close),
 
202
                    (proc.stderr.read, channel.sendall_stderr, channel.close)]
 
203
                for read, write, close in file_functions:
 
204
                    t = threading.Thread(
 
205
                        target=ferry_bytes, args=(read, write, close))
 
206
                    t.start()
 
207
 
 
208
                return True
 
209
 
 
210
        ssh_server = SFTPServer(StubSSHServer)
 
211
        # XXX: We *don't* want to override the default SSH vendor, so we set
 
212
        # _vendor to what _get_ssh_vendor returns.
 
213
        ssh_server.setUp()
 
214
        self.addCleanup(ssh_server.tearDown)
 
215
        port = ssh_server._listener.port
 
216
 
 
217
        # Access the branch via a bzr+ssh URL.  The BZR_REMOTE_PATH environment
 
218
        # variable is used to tell bzr what command to run on the remote end.
 
219
        path_to_branch = os.path.abspath('a_branch')
 
220
        
 
221
        orig_bzr_remote_path = os.environ.get('BZR_REMOTE_PATH')
 
222
        os.environ['BZR_REMOTE_PATH'] = self.get_bzr_path()
 
223
        try:
 
224
            branch = Branch.open(
 
225
                'bzr+ssh://fred:secret@localhost:%d%s' % (port, path_to_branch))
 
226
            
 
227
            branch.repository.get_revision_graph()
 
228
            self.assertEqual(None, branch.last_revision())
 
229
            # Check we can perform write operations
 
230
            branch.bzrdir.root_transport.mkdir('foo')
 
231
        finally:
 
232
            # Restore the BZR_REMOTE_PATH environment variable back to its
 
233
            # original state.
 
234
            if orig_bzr_remote_path is None:
 
235
                del os.environ['BZR_REMOTE_PATH']
 
236
            else:
 
237
                os.environ['BZR_REMOTE_PATH'] = orig_bzr_remote_path
 
238
 
 
239
        self.assertEqual(
 
240
            ['%s serve --inet --directory=/ --allow-writes'
 
241
             % self.get_bzr_path()],
 
242
            self.command_executed)
 
243