~launchpad-pqm/launchpad/devel

« back to all changes in this revision

Viewing changes to lib/lp/codehosting/tests/test_rewrite.py

  • Committer: Stuart Bishop
  • Date: 2011-09-05 15:42:27 UTC
  • mto: (13813.4.4 pgbouncer-fixture)
  • mto: This revision was merged to the branch mainline in revision 13875.
  • Revision ID: stuart.bishop@canonical.com-20110905154227-ke7d9fy9jr9uk87y
Revert reversion in launchpad/devel r13865

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2009 Canonical Ltd.  This software is licensed under the
 
1
# Copyright 2009-2011 Canonical Ltd.  This software is licensed under the
2
2
# GNU Affero General Public License version 3 (see the file LICENSE).
3
3
 
4
4
"""Tests for the dynamic RewriteMap used to serve branches over HTTP."""
14
14
from zope.security.proxy import removeSecurityProxy
15
15
 
16
16
from canonical.config import config
17
 
from canonical.testing.layers import DatabaseFunctionalLayer
 
17
from canonical.testing.layers import (
 
18
    DatabaseFunctionalLayer,
 
19
    DatabaseLayer,
 
20
    )
18
21
from lp.code.interfaces.codehosting import branch_id_alias
19
22
from lp.codehosting.rewrite import BranchRewriter
20
23
from lp.codehosting.vfs import branch_id_to_path
21
24
from lp.services.log.logger import BufferLogger
22
25
from lp.testing import (
23
26
    FakeTime,
 
27
    nonblocking_readline,
 
28
    TestCase,
24
29
    TestCaseWithFactory,
25
30
    )
 
31
from lp.testing.fixture import PGBouncerFixture
26
32
 
27
33
 
28
34
class TestBranchRewriter(TestCaseWithFactory):
177
183
        transaction.commit()
178
184
        rewriter.rewriteLine('/' + branch.unique_name + '/.bzr/README')
179
185
        rewriter.rewriteLine('/' + branch.unique_name + '/.bzr/README')
180
 
        logging_output_lines = self.getLoggerOutput(rewriter).strip().split('\n')
 
186
        logging_output_lines = self.getLoggerOutput(
 
187
            rewriter).strip().split('\n')
181
188
        self.assertEqual(2, len(logging_output_lines))
182
189
        self.assertIsNot(
183
190
            None,
194
201
        self.fake_time.advance(
195
202
            config.codehosting.branch_rewrite_cache_lifetime + 1)
196
203
        rewriter.rewriteLine('/' + branch.unique_name + '/.bzr/README')
197
 
        logging_output_lines = self.getLoggerOutput(rewriter).strip().split('\n')
 
204
        logging_output_lines = self.getLoggerOutput(
 
205
            rewriter).strip().split('\n')
198
206
        self.assertEqual(2, len(logging_output_lines))
199
207
        self.assertIsNot(
200
208
            None,
246
254
        # buffering, write a complete line of output.
247
255
        for input_line in input_lines:
248
256
            proc.stdin.write(input_line + '\n')
249
 
            output_lines.append(proc.stdout.readline().rstrip('\n'))
 
257
            output_lines.append(
 
258
                nonblocking_readline(proc.stdout, 60).rstrip('\n'))
250
259
        # If we create a new branch after the branch-rewrite.py script has
251
260
        # connected to the database, or edit a branch name that has already
252
261
        # been rewritten, both are rewritten successfully.
260
269
            'file:///var/tmp/bazaar.launchpad.dev/mirrors/%s/.bzr/README'
261
270
            % branch_id_to_path(new_branch.id))
262
271
        proc.stdin.write(new_branch_input + '\n')
263
 
        output_lines.append(proc.stdout.readline().rstrip('\n'))
 
272
        output_lines.append(
 
273
            nonblocking_readline(proc.stdout, 60).rstrip('\n'))
264
274
 
265
275
        edited_branch_input = '/%s/.bzr/README' % edited_branch.unique_name
266
276
        expected_lines.append(
267
277
            'file:///var/tmp/bazaar.launchpad.dev/mirrors/%s/.bzr/README'
268
278
            % branch_id_to_path(edited_branch.id))
269
279
        proc.stdin.write(edited_branch_input + '\n')
270
 
        output_lines.append(proc.stdout.readline().rstrip('\n'))
 
280
        output_lines.append(
 
281
            nonblocking_readline(proc.stdout, 60).rstrip('\n'))
271
282
 
272
283
        os.kill(proc.pid, signal.SIGINT)
273
284
        err = proc.stderr.read()
274
285
        # The script produces logging output, but not to stderr.
275
286
        self.assertEqual('', err)
276
287
        self.assertEqual(expected_lines, output_lines)
 
288
 
 
289
 
 
290
class TestBranchRewriterScriptHandlesDisconnects(TestCase):
 
291
    """Ensure branch-rewrite.py survives fastdowntime deploys."""
 
292
    layer = DatabaseLayer
 
293
 
 
294
    def spawn(self):
 
295
        script_file = os.path.join(
 
296
            config.root, 'scripts', 'branch-rewrite.py')
 
297
 
 
298
        self.rewriter_proc = subprocess.Popen(
 
299
            [script_file], stdin=subprocess.PIPE, stdout=subprocess.PIPE,
 
300
            stderr=subprocess.PIPE, bufsize=0)
 
301
 
 
302
        self.addCleanup(self.rewriter_proc.terminate)
 
303
 
 
304
    def request(self, query):
 
305
        self.rewriter_proc.stdin.write(query + '\n')
 
306
        self.rewriter_proc.stdin.flush()
 
307
 
 
308
        # 60 second timeout as we might need to wait for the script to
 
309
        # finish starting up.
 
310
        result = nonblocking_readline(self.rewriter_proc.stdout, 60)
 
311
 
 
312
        if result.endswith('\n'):
 
313
            return result[:-1]
 
314
        self.fail(
 
315
            "Incomplete line or no result retrieved from subprocess: %s"
 
316
            % repr(result.getvalue()))
 
317
 
 
318
    def test_reconnects_when_disconnected(self):
 
319
        pgbouncer = self.useFixture(PGBouncerFixture())
 
320
 
 
321
        self.spawn()
 
322
 
 
323
        # Everything should be working, and we get valid output.
 
324
        out = self.request('foo')
 
325
        self.assertEndsWith(out, '/foo')
 
326
 
 
327
        pgbouncer.stop()
 
328
 
 
329
        # Now with pgbouncer down, we should get NULL messages and
 
330
        # stderr spam, and this keeps happening. We test more than
 
331
        # once to ensure that we will keep trying to reconnect even
 
332
        # after several failures.
 
333
        for count in range(5):
 
334
            out = self.request('foo')
 
335
            self.assertEqual(out, 'NULL')
 
336
 
 
337
        pgbouncer.start()
 
338
 
 
339
        # Everything should be working, and we get valid output.
 
340
        out = self.request('foo')
 
341
        self.assertEndsWith(out, '/foo')
 
342
 
 
343
    def test_starts_with_db_down(self):
 
344
        pgbouncer = self.useFixture(PGBouncerFixture())
 
345
 
 
346
        # Start with the database down.
 
347
        pgbouncer.stop()
 
348
 
 
349
        self.spawn()
 
350
 
 
351
        for count in range(5):
 
352
            out = self.request('foo')
 
353
            self.assertEqual(out, 'NULL')
 
354
 
 
355
        pgbouncer.start()
 
356
 
 
357
        out = self.request('foo')
 
358
        self.assertEndsWith(out, '/foo')