~bzr-pqm/bzr/bzr.dev

4763.2.4 by John Arbash Meinel
merge bzr.2.1 in preparation for NEWS entry.
1
# Copyright (C) 2006-2010 Canonical Ltd
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
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
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
16
17
"""Server-side repository related request implmentations."""
18
3211.5.2 by Robert Collins
Change RemoteRepository.get_parent_map to use bz2 not gzip for compression.
19
import bz2
2571.2.2 by Ian Clatworthy
use basename as poolie recommended
20
import os
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
21
import Queue
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
22
import sys
23
import tempfile
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
24
import threading
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
25
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
26
from bzrlib import (
2694.5.4 by Jelmer Vernooij
Move bzrlib.util.bencode to bzrlib._bencode_py.
27
    bencode,
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
28
    errors,
4070.9.2 by Andrew Bennetts
Rough prototype of allowing a SearchResult to be passed to fetch, and using that to improve network conversations.
29
    graph,
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
30
    osutils,
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
31
    pack,
4634.124.5 by Martin Pool
Warn about inventory-delta streams when encoding for the network
32
    ui,
4476.3.29 by Andrew Bennetts
Add Repository.get_stream_1.18 verb.
33
    versionedfile,
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
34
    )
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
35
from bzrlib.bzrdir import BzrDir
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
36
from bzrlib.smart.request import (
37
    FailedSmartServerResponse,
38
    SmartServerRequest,
39
    SuccessfulSmartServerResponse,
40
    )
4419.2.5 by Andrew Bennetts
Add Repository.get_rev_id_for_revno, and use it both as the _ensure_real fallback and as the server-side implementation.
41
from bzrlib.repository import _strip_NULL_ghosts, network_format_registry
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
42
from bzrlib import revision as _mod_revision
4476.3.15 by Andrew Bennetts
Partially working fallback for pre-1.17 servers.
43
from bzrlib.versionedfile import (
44
    NetworkRecordStream,
45
    record_to_fulltext_bytes,
46
    )
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
47
48
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
49
class SmartServerRepositoryRequest(SmartServerRequest):
50
    """Common base class for Repository requests."""
51
52
    def do(self, path, *args):
53
        """Execute a repository request.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
54
2692.1.10 by Andrew Bennetts
More docstring polish
55
        All Repository requests take a path to the repository as their first
56
        argument.  The repository must be at the exact path given by the
57
        client - no searching is done.
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
58
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
59
        The actual logic is delegated to self.do_repository_request.
60
2692.1.10 by Andrew Bennetts
More docstring polish
61
        :param client_path: The path for the repository as received from the
62
            client.
63
        :return: A SmartServerResponse from self.do_repository_request().
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
64
        """
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
65
        transport = self.transport_from_client_path(path)
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
66
        bzrdir = BzrDir.open_from_transport(transport)
3184.1.10 by Robert Collins
Change the smart server verb for Repository.stream_revisions_chunked to use SearchResults as the request mechanism for downloads.
67
        # Save the repository for use with do_body.
68
        self._repository = bzrdir.open_repository()
69
        return self.do_repository_request(self._repository, *args)
70
71
    def do_repository_request(self, repository, *args):
72
        """Override to provide an implementation for a verb."""
73
        # No-op for verbs that take bodies (None as a result indicates a body
74
        # is expected)
75
        return None
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
76
4332.2.1 by Robert Collins
Fix bug 360791 by not raising an error when a smart server is asked for more content than it has locally; the client is assumed to be monitoring what it gets.
77
    def recreate_search(self, repository, search_bytes, discard_excess=False):
78
        """Recreate a search from its serialised form.
79
80
        :param discard_excess: If True, and the search refers to data we don't
81
            have, just silently accept that fact - the verb calling
82
            recreate_search trusts that clients will look for missing things
83
            they expected and get it from elsewhere.
84
        """
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
85
        lines = search_bytes.split('\n')
86
        if lines[0] == 'ancestry-of':
4070.9.14 by Andrew Bennetts
Tweaks requested by Robert's review.
87
            heads = lines[1:]
88
            search_result = graph.PendingAncestryResult(heads, repository)
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
89
            return search_result, None
90
        elif lines[0] == 'search':
4332.2.1 by Robert Collins
Fix bug 360791 by not raising an error when a smart server is asked for more content than it has locally; the client is assumed to be monitoring what it gets.
91
            return self.recreate_search_from_recipe(repository, lines[1:],
92
                discard_excess=discard_excess)
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
93
        else:
94
            return (None, FailedSmartServerResponse(('BadSearch',)))
95
4332.2.1 by Robert Collins
Fix bug 360791 by not raising an error when a smart server is asked for more content than it has locally; the client is assumed to be monitoring what it gets.
96
    def recreate_search_from_recipe(self, repository, lines,
97
        discard_excess=False):
98
        """Recreate a specific revision search (vs a from-tip search).
99
100
        :param discard_excess: If True, and the search refers to data we don't
101
            have, just silently accept that fact - the verb calling
102
            recreate_search trusts that clients will look for missing things
103
            they expected and get it from elsewhere.
104
        """
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
105
        start_keys = set(lines[0].split(' '))
106
        exclude_keys = set(lines[1].split(' '))
107
        revision_count = int(lines[2])
108
        repository.lock_read()
109
        try:
110
            search = repository.get_graph()._make_breadth_first_searcher(
111
                start_keys)
112
            while True:
113
                try:
114
                    next_revs = search.next()
115
                except StopIteration:
116
                    break
117
                search.stop_searching_any(exclude_keys.intersection(next_revs))
118
            search_result = search.get_result()
4332.2.1 by Robert Collins
Fix bug 360791 by not raising an error when a smart server is asked for more content than it has locally; the client is assumed to be monitoring what it gets.
119
            if (not discard_excess and
120
                search_result.get_recipe()[3] != revision_count):
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
121
                # we got back a different amount of data than expected, this
122
                # gets reported as NoSuchRevision, because less revisions
123
                # indicates missing revisions, and more should never happen as
124
                # the excludes list considers ghosts and ensures that ghost
125
                # filling races are not a problem.
126
                return (None, FailedSmartServerResponse(('NoSuchRevision',)))
4070.9.2 by Andrew Bennetts
Rough prototype of allowing a SearchResult to be passed to fetch, and using that to improve network conversations.
127
            return (search_result, None)
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
128
        finally:
129
            repository.unlock()
130
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
131
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
132
class SmartServerRepositoryReadLocked(SmartServerRepositoryRequest):
133
    """Calls self.do_readlocked_repository_request."""
134
135
    def do_repository_request(self, repository, *args):
136
        """Read lock a repository for do_readlocked_repository_request."""
137
        repository.lock_read()
138
        try:
139
            return self.do_readlocked_repository_request(repository, *args)
140
        finally:
141
            repository.unlock()
142
143
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
144
class SmartServerRepositoryGetParentMap(SmartServerRepositoryRequest):
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
145
    """Bzr 1.2+ - get parent data for revisions during a graph search."""
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
146
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
147
    no_extra_results = False
148
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
149
    def do_repository_request(self, repository, *revision_ids):
150
        """Get parent details for some revisions.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
151
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
152
        All the parents for revision_ids are returned. Additionally up to 64KB
153
        of additional parent data found by performing a breadth first search
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
154
        from revision_ids is returned. The verb takes a body containing the
155
        current search state, see do_body for details.
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
156
4190.1.5 by Robert Collins
Review tweaks.
157
        If 'include-missing:' is in revision_ids, ghosts encountered in the
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
158
        graph traversal for getting parent data are included in the result with
159
        a prefix of 'missing:'.
160
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
161
        :param repository: The repository to query in.
3172.5.8 by Robert Collins
Review feedback.
162
        :param revision_ids: The utf8 encoded revision_id to answer for.
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
163
        """
164
        self._revision_ids = revision_ids
165
        return None # Signal that we want a body.
166
167
    def do_body(self, body_bytes):
168
        """Process the current search state and perform the parent lookup.
169
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
170
        :return: A smart server response where the body contains an utf8
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
171
            encoded flattened list of the parents of the revisions (the same
3211.5.3 by Robert Collins
Adjust size of batch and change gzip comments to bzip2.
172
            format as Repository.get_revision_graph) which has been bz2
173
            compressed.
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
174
        """
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
175
        repository = self._repository
176
        repository.lock_read()
177
        try:
178
            return self._do_repository_request(body_bytes)
179
        finally:
180
            repository.unlock()
181
182
    def _do_repository_request(self, body_bytes):
183
        repository = self._repository
184
        revision_ids = set(self._revision_ids)
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
185
        include_missing = 'include-missing:' in revision_ids
186
        if include_missing:
187
            revision_ids.remove('include-missing:')
4070.9.5 by Andrew Bennetts
Better wire protocol: don't shoehorn MiniSearchResult serialisation into previous serialisation format.
188
        body_lines = body_bytes.split('\n')
189
        search_result, error = self.recreate_search_from_recipe(
190
            repository, body_lines)
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
191
        if error is not None:
192
            return error
193
        # TODO might be nice to start up the search again; but thats not
194
        # written or tested yet.
4070.9.2 by Andrew Bennetts
Rough prototype of allowing a SearchResult to be passed to fetch, and using that to improve network conversations.
195
        client_seen_revs = set(search_result.get_keys())
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
196
        # Always include the requested ids.
197
        client_seen_revs.difference_update(revision_ids)
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
198
        lines = []
199
        repo_graph = repository.get_graph()
200
        result = {}
201
        queried_revs = set()
202
        size_so_far = 0
203
        next_revs = revision_ids
204
        first_loop_done = False
205
        while next_revs:
206
            queried_revs.update(next_revs)
207
            parent_map = repo_graph.get_parent_map(next_revs)
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
208
            current_revs = next_revs
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
209
            next_revs = set()
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
210
            for revision_id in current_revs:
211
                missing_rev = False
212
                parents = parent_map.get(revision_id)
213
                if parents is not None:
214
                    # adjust for the wire
215
                    if parents == (_mod_revision.NULL_REVISION,):
216
                        parents = ()
217
                    # prepare the next query
218
                    next_revs.update(parents)
219
                    encoded_id = revision_id
220
                else:
221
                    missing_rev = True
222
                    encoded_id = "missing:" + revision_id
223
                    parents = []
224
                if (revision_id not in client_seen_revs and
225
                    (not missing_rev or include_missing)):
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
226
                    # Client does not have this revision, give it to it.
227
                    # add parents to the result
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
228
                    result[encoded_id] = parents
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
229
                    # Approximate the serialized cost of this revision_id.
4190.1.3 by Robert Collins
Allow optional inclusion of ghost data in server get_parent_map calls.
230
                    size_so_far += 2 + len(encoded_id) + sum(map(len, parents))
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
231
            # get all the directly asked for parents, and then flesh out to
232
            # 64K (compressed) or so. We do one level of depth at a time to
3211.5.3 by Robert Collins
Adjust size of batch and change gzip comments to bzip2.
233
            # stay in sync with the client. The 250000 magic number is
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
234
            # estimated compression ratio taken from bzr.dev itself.
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
235
            if self.no_extra_results or (
236
                first_loop_done and size_so_far > 250000):
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
237
                next_revs = set()
238
                break
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
239
            # don't query things we've already queried
240
            next_revs.difference_update(queried_revs)
241
            first_loop_done = True
242
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
243
        # sorting trivially puts lexographically similar revision ids together.
244
        # Compression FTW.
245
        for revision, parents in sorted(result.items()):
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
246
            lines.append(' '.join((revision, ) + tuple(parents)))
247
3211.5.1 by Robert Collins
Change the smart server get_parents method to take a graph search to exclude already recieved parents from. This prevents history shortcuts causing huge numbers of duplicates.
248
        return SuccessfulSmartServerResponse(
3211.5.2 by Robert Collins
Change RemoteRepository.get_parent_map to use bz2 not gzip for compression.
249
            ('ok', ), bz2.compress('\n'.join(lines)))
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
250
251
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
252
class SmartServerRepositoryGetRevisionGraph(SmartServerRepositoryReadLocked):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
253
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
254
    def do_readlocked_repository_request(self, repository, revision_id):
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
255
        """Return the result of repository.get_revision_graph(revision_id).
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
256
257
        Deprecated as of bzr 1.4, but supported for older clients.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
258
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
259
        :param repository: The repository to query in.
260
        :param revision_id: The utf8 encoded revision_id to get a graph from.
261
        :return: A smart server response where the body contains an utf8
262
            encoded flattened list of the revision graph.
263
        """
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
264
        if not revision_id:
265
            revision_id = None
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
266
267
        lines = []
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
268
        graph = repository.get_graph()
269
        if revision_id:
270
            search_ids = [revision_id]
271
        else:
272
            search_ids = repository.all_revision_ids()
273
        search = graph._make_breadth_first_searcher(search_ids)
274
        transitive_ids = set()
275
        map(transitive_ids.update, list(search))
276
        parent_map = graph.get_parent_map(transitive_ids)
3287.6.8 by Robert Collins
Reduce code duplication as per review.
277
        revision_graph = _strip_NULL_ghosts(parent_map)
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
278
        if revision_id and revision_id not in revision_graph:
2018.14.1 by Andrew Bennetts
Update to current hpss branch? Fix lots of test failures.
279
            # Note that we return an empty body, rather than omitting the body.
280
            # This way the client knows that it can always expect to find a body
281
            # in the response for this method, even in the error case.
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
282
            return FailedSmartServerResponse(('nosuchrevision', revision_id), '')
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
283
284
        for revision, parents in revision_graph.items():
2592.3.50 by Robert Collins
Merge bzr.dev.
285
            lines.append(' '.join((revision, ) + tuple(parents)))
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
286
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
287
        return SuccessfulSmartServerResponse(('ok', ), '\n'.join(lines))
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
288
289
4419.2.4 by Andrew Bennetts
Add Repository.get_rev_id_for_revno RPC, removes VFS calls from 'pull -r 123' case.
290
class SmartServerRepositoryGetRevIdForRevno(SmartServerRepositoryReadLocked):
291
292
    def do_readlocked_repository_request(self, repository, revno,
293
            known_pair):
294
        """Find the revid for a given revno, given a known revno/revid pair.
295
        
4419.2.16 by Andrew Bennetts
New in 1.17, not 1.16.
296
        New in 1.17.
4419.2.4 by Andrew Bennetts
Add Repository.get_rev_id_for_revno RPC, removes VFS calls from 'pull -r 123' case.
297
        """
4419.2.6 by Andrew Bennetts
Add tests for server-side logic, and fix the bugs exposed by those tests.
298
        try:
299
            found_flag, result = repository.get_rev_id_for_revno(revno, known_pair)
300
        except errors.RevisionNotPresent, err:
301
            if err.revision_id != known_pair[1]:
302
                raise AssertionError(
303
                    'get_rev_id_for_revno raised RevisionNotPresent for '
304
                    'non-initial revision: ' + err.revision_id)
305
            return FailedSmartServerResponse(
306
                ('nosuchrevision', err.revision_id))
4419.2.5 by Andrew Bennetts
Add Repository.get_rev_id_for_revno, and use it both as the _ensure_real fallback and as the server-side implementation.
307
        if found_flag:
308
            return SuccessfulSmartServerResponse(('ok', result))
309
        else:
310
            earliest_revno, earliest_revid = result
4419.2.4 by Andrew Bennetts
Add Repository.get_rev_id_for_revno RPC, removes VFS calls from 'pull -r 123' case.
311
            return SuccessfulSmartServerResponse(
4419.2.5 by Andrew Bennetts
Add Repository.get_rev_id_for_revno, and use it both as the _ensure_real fallback and as the server-side implementation.
312
                ('history-incomplete', earliest_revno, earliest_revid))
4419.2.4 by Andrew Bennetts
Add Repository.get_rev_id_for_revno RPC, removes VFS calls from 'pull -r 123' case.
313
314
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
315
class SmartServerRequestHasRevision(SmartServerRepositoryRequest):
316
317
    def do_repository_request(self, repository, revision_id):
318
        """Return ok if a specific revision is in the repository at path.
319
320
        :param repository: The repository to query in.
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
321
        :param revision_id: The utf8 encoded revision_id to lookup.
322
        :return: A smart server response of ('ok', ) if the revision is
323
            present.
324
        """
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
325
        if repository.has_revision(revision_id):
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
326
            return SuccessfulSmartServerResponse(('yes', ))
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
327
        else:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
328
            return SuccessfulSmartServerResponse(('no', ))
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
329
330
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
331
class SmartServerRepositoryGatherStats(SmartServerRepositoryRequest):
332
333
    def do_repository_request(self, repository, revid, committers):
334
        """Return the result of repository.gather_stats().
335
336
        :param repository: The repository to query in.
337
        :param revid: utf8 encoded rev id or an empty string to indicate None
338
        :param committers: 'yes' or 'no'.
339
340
        :return: A SmartServerResponse ('ok',), a encoded body looking like
341
              committers: 1
342
              firstrev: 1234.230 0
343
              latestrev: 345.700 3600
344
              revisions: 2
345
346
              But containing only fields returned by the gather_stats() call
347
        """
348
        if revid == '':
349
            decoded_revision_id = None
350
        else:
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
351
            decoded_revision_id = revid
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
352
        if committers == 'yes':
353
            decoded_committers = True
354
        else:
355
            decoded_committers = None
356
        stats = repository.gather_stats(decoded_revision_id, decoded_committers)
357
358
        body = ''
359
        if stats.has_key('committers'):
360
            body += 'committers: %d\n' % stats['committers']
361
        if stats.has_key('firstrev'):
362
            body += 'firstrev: %.3f %d\n' % stats['firstrev']
363
        if stats.has_key('latestrev'):
364
             body += 'latestrev: %.3f %d\n' % stats['latestrev']
365
        if stats.has_key('revisions'):
366
            body += 'revisions: %d\n' % stats['revisions']
367
        if stats.has_key('size'):
368
            body += 'size: %d\n' % stats['size']
369
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
370
        return SuccessfulSmartServerResponse(('ok', ), body)
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
371
372
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
373
class SmartServerRepositoryIsShared(SmartServerRepositoryRequest):
374
375
    def do_repository_request(self, repository):
376
        """Return the result of repository.is_shared().
377
378
        :param repository: The repository to query in.
379
        :return: A smart server response of ('yes', ) if the repository is
380
            shared, and ('no', ) if it is not.
381
        """
382
        if repository.is_shared():
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
383
            return SuccessfulSmartServerResponse(('yes', ))
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
384
        else:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
385
            return SuccessfulSmartServerResponse(('no', ))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
386
387
388
class SmartServerRepositoryLockWrite(SmartServerRepositoryRequest):
389
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
390
    def do_repository_request(self, repository, token=''):
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
391
        # XXX: this probably should not have a token.
392
        if token == '':
393
            token = None
394
        try:
395
            token = repository.lock_write(token=token)
396
        except errors.LockContention, e:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
397
            return FailedSmartServerResponse(('LockContention',))
2018.5.95 by Andrew Bennetts
Add a Transport.is_readonly remote call, let {Branch,Repository}.lock_write remote call return UnlockableTransport, and miscellaneous test fixes.
398
        except errors.UnlockableTransport:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
399
            return FailedSmartServerResponse(('UnlockableTransport',))
2872.5.3 by Martin Pool
Pass back LockFailed from smart server lock methods
400
        except errors.LockFailed, e:
401
            return FailedSmartServerResponse(('LockFailed',
402
                str(e.lock), str(e.why)))
3015.2.7 by Robert Collins
In the RemoteServer repository methods handle repositories that cannot be remotely locked like pack repositories, and add a read lock in SmartServerRepositoryStreamKnitDataForRevisions.
403
        if token is not None:
404
            repository.leave_lock_in_place()
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
405
        repository.unlock()
406
        if token is None:
407
            token = ''
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
408
        return SuccessfulSmartServerResponse(('ok', token))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
409
410
4060.1.5 by Robert Collins
Verb change name requested by Andrew.
411
class SmartServerRepositoryGetStream(SmartServerRepositoryRequest):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
412
413
    def do_repository_request(self, repository, to_network_name):
414
        """Get a stream for inserting into a to_format repository.
415
416
        :param repository: The repository to stream from.
417
        :param to_network_name: The network name of the format of the target
418
            repository.
419
        """
420
        self._to_format = network_format_registry.get(to_network_name)
4476.3.29 by Andrew Bennetts
Add Repository.get_stream_1.18 verb.
421
        if self._should_fake_unknown():
422
            return FailedSmartServerResponse(
423
                ('UnknownMethod', 'Repository.get_stream'))
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
424
        return None # Signal that we want a body.
425
4476.3.29 by Andrew Bennetts
Add Repository.get_stream_1.18 verb.
426
    def _should_fake_unknown(self):
4476.3.80 by Andrew Bennetts
Comment/docstring tweaks prompted by review.
427
        """Return True if we should return UnknownMethod to the client.
428
        
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
429
        This is a workaround for bugs in pre-1.19 clients that claim to
430
        support receiving streams of CHK repositories.  The pre-1.19 client
4476.3.80 by Andrew Bennetts
Comment/docstring tweaks prompted by review.
431
        expects inventory records to be serialized in the format defined by
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
432
        to_network_name, but in pre-1.19 (at least) that format definition
4476.3.80 by Andrew Bennetts
Comment/docstring tweaks prompted by review.
433
        tries to use the xml5 serializer, which does not correctly handle
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
434
        rich-roots.  After 1.19 the client can also accept inventory-deltas
4476.3.80 by Andrew Bennetts
Comment/docstring tweaks prompted by review.
435
        (which avoids this issue), and those clients will use the
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
436
        Repository.get_stream_1.19 verb instead of this one.
4476.3.80 by Andrew Bennetts
Comment/docstring tweaks prompted by review.
437
        So: if this repository is CHK, and the to_format doesn't match,
438
        we should just fake an UnknownSmartMethod error so that the client
439
        will fallback to VFS, rather than sending it a stream we know it
440
        cannot handle.
441
        """
4476.3.29 by Andrew Bennetts
Add Repository.get_stream_1.18 verb.
442
        from_format = self._repository._format
443
        to_format = self._to_format
444
        if not from_format.supports_chks:
445
            # Source not CHK: that's ok
446
            return False
447
        if (to_format.supports_chks and
448
            from_format.repository_class is to_format.repository_class and
449
            from_format._serializer == to_format._serializer):
450
            # Source is CHK, but target matches: that's ok
451
            # (e.g. 2a->2a, or CHK2->2a)
452
            return False
453
        # Source is CHK, and target is not CHK or incompatible CHK.  We can't
454
        # generate a compatible stream.
455
        return True
456
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
457
    def do_body(self, body_bytes):
458
        repository = self._repository
459
        repository.lock_read()
460
        try:
4332.2.1 by Robert Collins
Fix bug 360791 by not raising an error when a smart server is asked for more content than it has locally; the client is assumed to be monitoring what it gets.
461
            search_result, error = self.recreate_search(repository, body_bytes,
462
                discard_excess=True)
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
463
            if error is not None:
464
                repository.unlock()
465
                return error
466
            source = repository._get_source(self._to_format)
4070.9.2 by Andrew Bennetts
Rough prototype of allowing a SearchResult to be passed to fetch, and using that to improve network conversations.
467
            stream = source.get_stream(search_result)
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
468
        except Exception:
469
            exc_info = sys.exc_info()
470
            try:
471
                # On non-error, unlocking is done by the body stream handler.
472
                repository.unlock()
473
            finally:
474
                raise exc_info[0], exc_info[1], exc_info[2]
475
        return SuccessfulSmartServerResponse(('ok',),
476
            body_stream=self.body_stream(stream, repository))
477
478
    def body_stream(self, stream, repository):
479
        byte_stream = _stream_to_byte_stream(stream, repository._format)
480
        try:
481
            for bytes in byte_stream:
482
                yield bytes
483
        except errors.RevisionNotPresent, e:
484
            # This shouldn't be able to happen, but as we don't buffer
485
            # everything it can in theory happen.
486
            repository.unlock()
487
            yield FailedSmartServerResponse(('NoSuchRevision', e.revision_id))
488
        else:
489
            repository.unlock()
490
491
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
492
class SmartServerRepositoryGetStream_1_19(SmartServerRepositoryGetStream):
4476.3.29 by Andrew Bennetts
Add Repository.get_stream_1.18 verb.
493
494
    def _should_fake_unknown(self):
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
495
        """Returns False; we don't need to workaround bugs in 1.19+ clients."""
4476.3.29 by Andrew Bennetts
Add Repository.get_stream_1.18 verb.
496
        return False
497
498
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
499
def _stream_to_byte_stream(stream, src_format):
500
    """Convert a record stream to a self delimited byte stream."""
501
    pack_writer = pack.ContainerSerialiser()
502
    yield pack_writer.begin()
503
    yield pack_writer.bytes_record(src_format.network_name(), '')
504
    for substream_type, substream in stream:
505
        for record in substream:
506
            if record.storage_kind in ('chunked', 'fulltext'):
507
                serialised = record_to_fulltext_bytes(record)
4476.3.15 by Andrew Bennetts
Partially working fallback for pre-1.17 servers.
508
            elif record.storage_kind == 'inventory-delta':
4476.3.4 by Andrew Bennetts
Network serialisation, and most tests passing with InterDifferingSerializer commented out.
509
                serialised = record_to_inventory_delta_bytes(record)
4392.2.2 by John Arbash Meinel
Add tests that ensure we can fetch branches with ghosts in their ancestry.
510
            elif record.storage_kind == 'absent':
511
                raise ValueError("Absent factory for %s" % (record.key,))
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
512
            else:
513
                serialised = record.get_bytes_as(record.storage_kind)
514
            if serialised:
515
                # Some streams embed the whole stream into the wire
516
                # representation of the first record, which means that
517
                # later records have no wire representation: we skip them.
518
                yield pack_writer.bytes_record(serialised, [(substream_type,)])
519
    yield pack_writer.end()
520
521
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
522
class _ByteStreamDecoder(object):
523
    """Helper for _byte_stream_to_stream.
524
4634.19.2 by Robert Collins
Review feedback.
525
    The expected usage of this class is via the function _byte_stream_to_stream
526
    which creates a _ByteStreamDecoder, pops off the stream format and then
527
    yields the output of record_stream(), the main entry point to
528
    _ByteStreamDecoder.
529
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
530
    Broadly this class has to unwrap two layers of iterators:
531
    (type, substream)
532
    (substream details)
533
534
    This is complicated by wishing to return type, iterator_for_type, but
535
    getting the data for iterator_for_type when we find out type: we can't
536
    simply pass a generator down to the NetworkRecordStream parser, instead
537
    we have a little local state to seed each NetworkRecordStream instance,
538
    and gather the type that we'll be yielding.
539
540
    :ivar byte_stream: The byte stream being decoded.
541
    :ivar stream_decoder: A pack parser used to decode the bytestream
542
    :ivar current_type: The current type, used to join adjacent records of the
543
        same type into a single stream.
544
    :ivar first_bytes: The first bytes to give the next NetworkRecordStream.
545
    """
546
547
    def __init__(self, byte_stream):
548
        """Create a _ByteStreamDecoder."""
549
        self.stream_decoder = pack.ContainerPushParser()
550
        self.current_type = None
551
        self.first_bytes = None
552
        self.byte_stream = byte_stream
553
554
    def iter_stream_decoder(self):
555
        """Iterate the contents of the pack from stream_decoder."""
556
        # dequeue pending items
557
        for record in self.stream_decoder.read_pending_records():
558
            yield record
559
        # Pull bytes of the wire, decode them to records, yield those records.
560
        for bytes in self.byte_stream:
561
            self.stream_decoder.accept_bytes(bytes)
562
            for record in self.stream_decoder.read_pending_records():
563
                yield record
564
565
    def iter_substream_bytes(self):
566
        if self.first_bytes is not None:
567
            yield self.first_bytes
568
            # If we run out of pack records, single the outer layer to stop.
569
            self.first_bytes = None
570
        for record in self.iter_pack_records:
571
            record_names, record_bytes = record
572
            record_name, = record_names
573
            substream_type = record_name[0]
574
            if substream_type != self.current_type:
575
                # end of a substream, seed the next substream.
576
                self.current_type = substream_type
577
                self.first_bytes = record_bytes
578
                return
579
            yield record_bytes
580
581
    def record_stream(self):
582
        """Yield substream_type, substream from the byte stream."""
583
        self.seed_state()
584
        # Make and consume sub generators, one per substream type:
585
        while self.first_bytes is not None:
586
            substream = NetworkRecordStream(self.iter_substream_bytes())
587
            # after substream is fully consumed, self.current_type is set to
588
            # the next type, and self.first_bytes is set to the matching bytes.
589
            yield self.current_type, substream.read()
590
591
    def seed_state(self):
592
        """Prepare the _ByteStreamDecoder to decode from the pack stream."""
593
        # Set a single generator we can use to get data from the pack stream.
594
        self.iter_pack_records = self.iter_stream_decoder()
595
        # Seed the very first subiterator with content; after this each one
596
        # seeds the next.
597
        list(self.iter_substream_bytes())
598
599
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
600
def _byte_stream_to_stream(byte_stream):
4060.1.5 by Robert Collins
Verb change name requested by Andrew.
601
    """Convert a byte stream into a format and a stream.
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
602
603
    :param byte_stream: A bytes iterator, as output by _stream_to_byte_stream.
604
    :return: (RepositoryFormat, stream_generator)
605
    """
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
606
    decoder = _ByteStreamDecoder(byte_stream)
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
607
    for bytes in byte_stream:
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
608
        decoder.stream_decoder.accept_bytes(bytes)
609
        for record in decoder.stream_decoder.read_pending_records(max=1):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
610
            record_names, src_format_name = record
611
            src_format = network_format_registry.get(src_format_name)
4634.19.1 by Robert Collins
Combine adjacent substreams of the same type in bzrlib.smart.repository._byte_stream_to_stream.
612
            return src_format, decoder.record_stream()
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
613
614
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
615
class SmartServerRepositoryUnlock(SmartServerRepositoryRequest):
616
617
    def do_repository_request(self, repository, token):
618
        try:
619
            repository.lock_write(token=token)
620
        except errors.TokenMismatch, e:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
621
            return FailedSmartServerResponse(('TokenMismatch',))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
622
        repository.dont_leave_lock_in_place()
623
        repository.unlock()
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
624
        return SuccessfulSmartServerResponse(('ok',))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
625
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
626
4017.3.4 by Robert Collins
Create a verb for Repository.set_make_working_trees.
627
class SmartServerRepositorySetMakeWorkingTrees(SmartServerRepositoryRequest):
628
629
    def do_repository_request(self, repository, str_bool_new_value):
630
        if str_bool_new_value == 'True':
631
            new_value = True
632
        else:
633
            new_value = False
634
        repository.set_make_working_trees(new_value)
635
        return SuccessfulSmartServerResponse(('ok',))
636
637
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
638
class SmartServerRepositoryTarball(SmartServerRepositoryRequest):
2018.18.11 by Martin Pool
merge hpss changes
639
    """Get the raw repository files as a tarball.
640
641
    The returned tarball contains a .bzr control directory which in turn
642
    contains a repository.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
643
644
    This takes one parameter, compression, which currently must be
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
645
    "", "gz", or "bz2".
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
646
647
    This is used to implement the Repository.copy_content_into operation.
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
648
    """
649
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
650
    def do_repository_request(self, repository, compression):
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
651
        tmp_dirname, tmp_repo = self._copy_to_tempdir(repository)
2018.18.5 by Martin Pool
Repository.tarball locks repository while running for consistency
652
        try:
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
653
            controldir_name = tmp_dirname + '/.bzr'
654
            return self._tarfile_response(controldir_name, compression)
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
655
        finally:
656
            osutils.rmtree(tmp_dirname)
657
658
    def _copy_to_tempdir(self, from_repo):
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
659
        tmp_dirname = osutils.mkdtemp(prefix='tmpbzrclone')
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
660
        tmp_bzrdir = from_repo.bzrdir._format.initialize(tmp_dirname)
661
        tmp_repo = from_repo._format.initialize(tmp_bzrdir)
662
        from_repo.copy_content_into(tmp_repo)
663
        return tmp_dirname, tmp_repo
664
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
665
    def _tarfile_response(self, tmp_dirname, compression):
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
666
        temp = tempfile.NamedTemporaryFile()
667
        try:
2557.1.1 by Martin Pool
[BUG 119330] Fix tempfile permissions error in smart server tar bundling (under windows) (Martin_)
668
            self._tarball_of_dir(tmp_dirname, compression, temp.file)
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
669
            # all finished; write the tempfile out to the network
670
            temp.seek(0)
2420.2.2 by Andrew Bennetts
Merge tarball branch that's already with PQM, resolving conflicts.
671
            return SuccessfulSmartServerResponse(('ok',), temp.read())
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
672
            # FIXME: Don't read the whole thing into memory here; rather stream
673
            # it out from the file onto the network. mbp 20070411
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
674
        finally:
675
            temp.close()
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
676
2557.1.1 by Martin Pool
[BUG 119330] Fix tempfile permissions error in smart server tar bundling (under windows) (Martin_)
677
    def _tarball_of_dir(self, dirname, compression, ofile):
5017.2.4 by Martin Pool
Move or remove some unconditionally loaded code
678
        import tarfile
2571.2.2 by Ian Clatworthy
use basename as poolie recommended
679
        filename = os.path.basename(ofile.name)
680
        tarball = tarfile.open(fileobj=ofile, name=filename,
2571.2.1 by Ian Clatworthy
fix #123485 - selftest broken under Python 2.5.1 because of tafile API change
681
            mode='w|' + compression)
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
682
        try:
683
            # The tarball module only accepts ascii names, and (i guess)
684
            # packs them with their 8bit names.  We know all the files
685
            # within the repository have ASCII names so the should be safe
686
            # to pack in.
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
687
            dirname = dirname.encode(sys.getfilesystemencoding())
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
688
            # python's tarball module includes the whole path by default so
689
            # override it
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
690
            if not dirname.endswith('.bzr'):
691
                raise ValueError(dirname)
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
692
            tarball.add(dirname, '.bzr') # recursive by default
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
693
        finally:
694
            tarball.close()
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
695
696
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
697
class SmartServerRepositoryInsertStreamLocked(SmartServerRepositoryRequest):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
698
    """Insert a record stream from a RemoteSink into a repository.
699
700
    This gets bytes pushed to it by the network infrastructure and turns that
701
    into a bytes iterator using a thread. That is then processed by
702
    _byte_stream_to_stream.
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
703
704
    New in 1.14.
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
705
    """
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
706
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
707
    def do_repository_request(self, repository, resume_tokens, lock_token):
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
708
        """StreamSink.insert_stream for a remote repository."""
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
709
        repository.lock_write(token=lock_token)
710
        self.do_insert_stream_request(repository, resume_tokens)
711
712
    def do_insert_stream_request(self, repository, resume_tokens):
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
713
        tokens = [token for token in resume_tokens.split(' ') if token]
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
714
        self.tokens = tokens
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
715
        self.repository = repository
716
        self.queue = Queue.Queue()
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
717
        self.insert_thread = threading.Thread(target=self._inserter_thread)
718
        self.insert_thread.start()
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
719
720
    def do_chunk(self, body_stream_chunk):
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
721
        self.queue.put(body_stream_chunk)
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
722
723
    def _inserter_thread(self):
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
724
        try:
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
725
            src_format, stream = _byte_stream_to_stream(
726
                self.blocking_byte_stream())
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
727
            self.insert_result = self.repository._get_sink().insert_stream(
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
728
                stream, src_format, self.tokens)
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
729
            self.insert_ok = True
730
        except:
731
            self.insert_exception = sys.exc_info()
732
            self.insert_ok = False
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
733
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
734
    def blocking_byte_stream(self):
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
735
        while True:
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
736
            bytes = self.queue.get()
737
            if bytes is StopIteration:
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
738
                return
739
            else:
4060.1.4 by Robert Collins
Streaming fetch from remote servers.
740
                yield bytes
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
741
742
    def do_end(self):
743
        self.queue.put(StopIteration)
744
        if self.insert_thread is not None:
745
            self.insert_thread.join()
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
746
        if not self.insert_ok:
747
            exc_info = self.insert_exception
748
            raise exc_info[0], exc_info[1], exc_info[2]
749
        write_group_tokens, missing_keys = self.insert_result
750
        if write_group_tokens or missing_keys:
751
            # bzip needed? missing keys should typically be a small set.
752
            # Should this be a streaming body response ?
753
            missing_keys = sorted(missing_keys)
754
            bytes = bencode.bencode((write_group_tokens, missing_keys))
755
            self.repository.unlock()
756
            return SuccessfulSmartServerResponse(('missing-basis', bytes))
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
757
        else:
4032.3.7 by Robert Collins
Move write locking and write group responsibilities into the Sink objects themselves, allowing complete avoidance of unnecessary calls when the sink is a RemoteSink.
758
            self.repository.unlock()
759
            return SuccessfulSmartServerResponse(('ok', ))
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
760
761
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
762
class SmartServerRepositoryInsertStream_1_19(SmartServerRepositoryInsertStreamLocked):
4476.3.15 by Andrew Bennetts
Partially working fallback for pre-1.17 servers.
763
    """Insert a record stream from a RemoteSink into a repository.
764
765
    Same as SmartServerRepositoryInsertStreamLocked, except:
766
     - the lock token argument is optional
767
     - servers that implement this verb accept 'inventory-delta' records in the
768
       stream.
769
4476.3.82 by Andrew Bennetts
Mention another bug fix in NEWS, and update verb name, comments, and NEWS additions for landing on 1.19 rather than 1.18.
770
    New in 1.19.
4476.3.15 by Andrew Bennetts
Partially working fallback for pre-1.17 servers.
771
    """
772
773
    def do_repository_request(self, repository, resume_tokens, lock_token=None):
774
        """StreamSink.insert_stream for a remote repository."""
775
        SmartServerRepositoryInsertStreamLocked.do_repository_request(
776
            self, repository, resume_tokens, lock_token)
777
778
4144.3.1 by Andrew Bennetts
Add Repository.insert_stream_locked server-side implementation, plus tests for server-side _translate_error.
779
class SmartServerRepositoryInsertStream(SmartServerRepositoryInsertStreamLocked):
780
    """Insert a record stream from a RemoteSink into an unlocked repository.
781
782
    This is the same as SmartServerRepositoryInsertStreamLocked, except it
783
    takes no lock_tokens; i.e. it works with an unlocked (or lock-free, e.g.
784
    like pack format) repository.
785
786
    New in 1.13.
787
    """
788
789
    def do_repository_request(self, repository, resume_tokens):
790
        """StreamSink.insert_stream for a remote repository."""
791
        repository.lock_write()
792
        self.do_insert_stream_request(repository, resume_tokens)
793
794