~bzr-pqm/bzr/bzr.dev

2245.8.3 by Martin Pool
Start adding indirection transport
1
# Copyright (C) 2007 Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
18
"""Transport indirection that uses Launchpad as a directory lookup.
19
20
When the transport is opened, it immediately redirects to a url
21
on Launchpad, which can then either serve the branch itself or redirect
22
again.
23
"""
24
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
25
from urlparse import urlsplit, urlunsplit
2898.4.4 by James Henstridge
Changes to account for modifications to the XMLRPC API.
26
import xmlrpclib
27
2245.8.4 by Martin Pool
lp:/// indirection works
28
from bzrlib import (
2898.4.14 by James Henstridge
* Use urlsplit() to process URLs.
29
    debug,
2245.8.4 by Martin Pool
lp:/// indirection works
30
    errors,
2898.4.14 by James Henstridge
* Use urlsplit() to process URLs.
31
    trace,
2898.4.11 by James Henstridge
Switch back to RedirectRequested based implementation.
32
    urlutils,
2245.8.4 by Martin Pool
lp:/// indirection works
33
    )
34
from bzrlib.transport import (
35
    get_transport,
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
36
    register_urlparse_netloc_protocol,
2245.8.4 by Martin Pool
lp:/// indirection works
37
    Transport,
38
    )
39
2898.4.7 by James Henstridge
Fix up tests.
40
from bzrlib.plugins.launchpad.lp_registration import (
41
    LaunchpadService, ResolveLaunchpadPathRequest)
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
42
from bzrlib.plugins.launchpad.account import get_lp_login
43
44
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
45
# As bzrlib.transport.remote may not be loaded yet, make sure bzr+ssh
46
# is counted as a netloc protocol.
47
register_urlparse_netloc_protocol('bzr+ssh')
2898.4.14 by James Henstridge
* Use urlsplit() to process URLs.
48
register_urlparse_netloc_protocol('lp')
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
49
50
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
51
class LaunchpadTransport(Transport):
52
    """lp:/// URL transport
53
54
    This transport redirects requests to the real branch location
55
    after resolving the URL via an XMLRPC request to Launchpad.
56
    """
57
58
    def __init__(self, base):
59
        super(LaunchpadTransport, self).__init__(base)
2898.4.14 by James Henstridge
* Use urlsplit() to process URLs.
60
        # We only support URLs without a netloc
61
        netloc = urlsplit(base)[1]
62
        if netloc != '':
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
63
            raise errors.InvalidURL(path=base)
64
3031.2.4 by jml at canonical
Only split the URL once.
65
    def _requires_launchpad_login(self, scheme, netloc, path, query,
66
                                  fragment):
67
        """Does the URL require a Launchpad login in order to be reached?
68
69
        The URL is specified by its parsed components, as returned from
70
        urlsplit.
71
        """
3031.2.3 by jml at canonical
Make the test pass -- don't include sftp URLs if there's no lp login.
72
        return (scheme in ('bzr+ssh', 'sftp')
73
                and (netloc.endswith('launchpad.net')
74
                     or netloc.endswith('launchpad.dev')))
3031.2.1 by jml at canonical
Factor out the method that determines if a URL is a LP url.
75
2898.4.11 by James Henstridge
Switch back to RedirectRequested based implementation.
76
    def _resolve(self, abspath,
77
                 _request_factory=ResolveLaunchpadPathRequest,
78
                 _lp_login=None):
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
79
        """Resolve the base URL for this transport."""
2898.4.14 by James Henstridge
* Use urlsplit() to process URLs.
80
        path = urlsplit(abspath)[2].lstrip('/')
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
81
        # Perform an XMLRPC request to resolve the path
82
        resolve = _request_factory(path)
83
        service = LaunchpadService()
84
        try:
85
            result = resolve.submit(service)
86
        except xmlrpclib.Fault, fault:
87
            raise errors.InvalidURL(
2898.4.11 by James Henstridge
Switch back to RedirectRequested based implementation.
88
                path=abspath, extra=fault.faultString)
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
89
2898.4.14 by James Henstridge
* Use urlsplit() to process URLs.
90
        if 'launchpad' in debug.debug_flags:
91
            trace.mutter("resolve_lp_path(%r) == %r", path, result)
92
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
93
        if _lp_login is None:
94
            _lp_login = get_lp_login()
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
95
        for url in result['urls']:
96
            scheme, netloc, path, query, fragment = urlsplit(url)
3031.2.4 by jml at canonical
Only split the URL once.
97
            if self._requires_launchpad_login(scheme, netloc, path, query,
98
                                              fragment):
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
99
                # Only accept launchpad.net bzr+ssh URLs if we know
100
                # the user's Launchpad login:
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
101
                if _lp_login is None:
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
102
                    continue
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
103
                url = urlunsplit((scheme, '%s@%s' % (_lp_login, netloc),
104
                                  path, query, fragment))
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
105
                break
2898.4.15 by James Henstridge
Use get_transport() to decide whether Bazaar supports a given URL.
106
            else:
107
                # Use the URL if we can create a transport for it.
108
                try:
109
                    get_transport(url)
110
                except (errors.PathError, errors.TransportError):
111
                    pass
112
                else:
113
                    break
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
114
        else:
2898.4.11 by James Henstridge
Switch back to RedirectRequested based implementation.
115
            raise errors.InvalidURL(path=abspath,
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
116
                                    extra='no supported schemes')
117
        return url
118
2898.4.11 by James Henstridge
Switch back to RedirectRequested based implementation.
119
    def _request_redirect(self, relpath):
120
        source = urlutils.join(self.base, relpath)
121
        # Split the source location into the branch location, and the
122
        # extra path components.
123
        pos = source.find('/.bzr/')
124
        if pos >= 0:
125
            branchpath = source[:pos]
126
            extra = source[pos:]
127
        else:
128
            branchpath = source
129
            extra = ''
130
        target = self._resolve(branchpath) + extra
131
        raise errors.RedirectRequested(
132
            source=source,
133
            target=target)
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
134
135
    def get(self, relpath):
136
        """See Transport.get()."""
2898.4.11 by James Henstridge
Switch back to RedirectRequested based implementation.
137
        self._request_redirect(relpath)
2898.4.9 by James Henstridge
Add some more tests for the bzr+ssh://bazaar.launchpad.net URL
138
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
139
    def mkdir(self, relpath, mode=None):
140
        """See Transport.mkdir()."""
2898.4.11 by James Henstridge
Switch back to RedirectRequested based implementation.
141
        self._request_redirect(relpath)
2898.4.8 by James Henstridge
Switch lp: over to a pass-through transport, so that the XMLRPC gets
142
2245.8.3 by Martin Pool
Start adding indirection transport
143
144
def get_test_permutations():
145
    # Since this transport doesn't do anything once opened, it's not subjected
146
    # to the usual transport tests.
147
    return []