~bzr-pqm/bzr/bzr.dev

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