~launchpad-pqm/launchpad/devel

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# Copyright 2004-2007 Canonical Ltd.  All rights reserved.

"""Bazaar plugin to run the smart server on Launchpad.

Cribbed from bzrlib.builtins.cmd_serve from Bazaar 0.16.
"""

__metaclass__ = type

__all__ = ['cmd_launchpad_server']


import sys
import xmlrpclib

from bzrlib.commands import Command, register_command
from bzrlib.option import Option
from bzrlib import urlutils, ui

from bzrlib.smart import medium, server
from bzrlib.transport import chroot, get_transport, remote

from canonical.config import config


class cmd_launchpad_server(Command):
    """Run a Bazaar server that maps Launchpad branch URLs to the internal
    file-system format.
    """

    aliases = ['lp-serve']

    takes_options = [
        Option('inet',
               help='serve on stdin/out for use from inetd or sshd'),
        Option('port',
               help='listen for connections on nominated port of the form '
                    '[hostname:]portnumber. Passing 0 as the port number will'
                    ' result in a dynamically allocated port. Default port is'
                    ' 4155.',
               type=str),
        Option('upload-directory',
               help='upload branches to this directory. Defaults to '
                    'config.codehosting.branches_root.',
               type=unicode),
        Option('mirror-directory',
               help='serve branches from this directory. Defaults to '
                    'config.supermirror.branchesdest.'),
        Option('authserver_url',
               help='the url of the internal XML-RPC server. Defaults to '
                    'config.codehosting.authserver.',
               type=unicode),
        ]

    takes_args = ['user_id']

    def _get_chrooted_transport(self, url):
        chroot_server = chroot.ChrootServer(get_transport(url))
        chroot_server.setUp()
        return get_transport(chroot_server.get_url())

    def get_lp_server(self, authserver, user_id, hosted_url, mirror_url):
        """Create a Launchpad smart server.

        :param authserver: An `xmlrpclib.ServerProxy` (or equivalent) for the
            Launchpad authserver.
        :param user_id: The database ID of the user whose branches are being
            served.
        :param hosted_url: Where the branches are uploaded to.
        :param mirror_url: Where all Launchpad branches are mirrored.
        :return: A `LaunchpadTransport`.
        """
        # XXX: JonathanLange 2007-05-29: The 'chroot' lines lack unit tests.
        from canonical.codehosting import transport
        hosted_transport = self._get_chrooted_transport(hosted_url)
        mirror_transport = self._get_chrooted_transport(mirror_url)
        lp_server = transport.LaunchpadServer(
            authserver, user_id, hosted_transport, mirror_transport)
        return lp_server

    def get_smart_server(self, transport, port, inet):
        """Construct a smart server."""
        if inet:
            smart_server = medium.SmartServerPipeStreamMedium(
                sys.stdin, sys.stdout, transport)
        else:
            host = remote.BZR_DEFAULT_INTERFACE
            if port is None:
                port = remote.BZR_DEFAULT_PORT
            else:
                if ':' in port:
                    host, port = port.split(':')
                port = int(port)
            smart_server = server.SmartTCPServer(
                transport, host=host, port=port)
            print 'listening on port: ', smart_server.port
            sys.stdout.flush()
        return smart_server

    def run_server(self, smart_server):
        """Run the given smart server."""
        # for the duration of this server, no UI output is permitted.
        # note that this may cause problems with blackbox tests. This should
        # be changed with care though, as we dont want to use bandwidth
        # sending progress over stderr to smart server clients!
        old_factory = ui.ui_factory
        try:
            ui.ui_factory = ui.SilentUIFactory()
            smart_server.serve()
        finally:
            ui.ui_factory = old_factory

    def run(self, user_id, port=None, upload_directory=None,
            mirror_directory=None, authserver_url=None, inet=False):
        from canonical.codehosting import transport
        if upload_directory is None:
            upload_directory = config.codehosting.branches_root
        if mirror_directory is None:
            mirror_directory = config.supermirror.branchesdest
        if authserver_url is None:
            authserver_url = config.codehosting.authserver

        debug_log = transport.set_up_logging()
        debug_log.debug('Running smart server for %s', user_id)

        upload_url = urlutils.local_path_to_url(upload_directory)
        mirror_url = urlutils.local_path_to_url(mirror_directory)
        authserver = xmlrpclib.ServerProxy(authserver_url)

        lp_server = self.get_lp_server(
            authserver, user_id, upload_url, mirror_url)
        lp_server.setUp()

        try:
            lp_transport = get_transport(lp_server.get_url())
            smart_server = self.get_smart_server(lp_transport, port, inet)
            self.run_server(smart_server)
        finally:
            lp_server.tearDown()


register_command(cmd_launchpad_server)