/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
1
# Copyright (C) 2006, 2007 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
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
22
import struct
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
23
import sys
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
24
import tarfile
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
25
import tempfile
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
26
import threading
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
27
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
28
from bzrlib import (
29
    errors,
30
    osutils,
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
31
    pack,
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
32
    )
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
33
from bzrlib.bzrdir import BzrDir
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
34
from bzrlib.smart.request import (
35
    FailedSmartServerResponse,
36
    SmartServerRequest,
37
    SuccessfulSmartServerResponse,
38
    )
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
39
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.
40
from bzrlib import revision as _mod_revision
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
41
from bzrlib.util import bencode
42
from bzrlib.versionedfile import NetworkRecordStream
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
43
44
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
45
class SmartServerRepositoryRequest(SmartServerRequest):
46
    """Common base class for Repository requests."""
47
48
    def do(self, path, *args):
49
        """Execute a repository request.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
50
2692.1.10 by Andrew Bennetts
More docstring polish
51
        All Repository requests take a path to the repository as their first
52
        argument.  The repository must be at the exact path given by the
53
        client - no searching is done.
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
54
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
55
        The actual logic is delegated to self.do_repository_request.
56
2692.1.10 by Andrew Bennetts
More docstring polish
57
        :param client_path: The path for the repository as received from the
58
            client.
59
        :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).
60
        """
2692.1.1 by Andrew Bennetts
Add translate_client_path method to SmartServerRequest.
61
        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).
62
        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.
63
        # Save the repository for use with do_body.
64
        self._repository = bzrdir.open_repository()
65
        return self.do_repository_request(self._repository, *args)
66
67
    def do_repository_request(self, repository, *args):
68
        """Override to provide an implementation for a verb."""
69
        # No-op for verbs that take bodies (None as a result indicates a body
70
        # is expected)
71
        return None
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
72
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.
73
    def recreate_search(self, repository, recipe_bytes):
74
        lines = recipe_bytes.split('\n')
75
        start_keys = set(lines[0].split(' '))
76
        exclude_keys = set(lines[1].split(' '))
77
        revision_count = int(lines[2])
78
        repository.lock_read()
79
        try:
80
            search = repository.get_graph()._make_breadth_first_searcher(
81
                start_keys)
82
            while True:
83
                try:
84
                    next_revs = search.next()
85
                except StopIteration:
86
                    break
87
                search.stop_searching_any(exclude_keys.intersection(next_revs))
88
            search_result = search.get_result()
89
            if search_result.get_recipe()[2] != revision_count:
90
                # we got back a different amount of data than expected, this
91
                # gets reported as NoSuchRevision, because less revisions
92
                # indicates missing revisions, and more should never happen as
93
                # the excludes list considers ghosts and ensures that ghost
94
                # filling races are not a problem.
95
                return (None, FailedSmartServerResponse(('NoSuchRevision',)))
96
            return (search, None)
97
        finally:
98
            repository.unlock()
99
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
100
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
101
class SmartServerRepositoryReadLocked(SmartServerRepositoryRequest):
102
    """Calls self.do_readlocked_repository_request."""
103
104
    def do_repository_request(self, repository, *args):
105
        """Read lock a repository for do_readlocked_repository_request."""
106
        repository.lock_read()
107
        try:
108
            return self.do_readlocked_repository_request(repository, *args)
109
        finally:
110
            repository.unlock()
111
112
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
113
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.
114
    """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
115
4035.2.1 by Andrew Bennetts
Fix unnecessary get_parent_map calls after insert_stream during push.
116
    no_extra_results = False
117
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
118
    def do_repository_request(self, repository, *revision_ids):
119
        """Get parent details for some revisions.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
120
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
121
        All the parents for revision_ids are returned. Additionally up to 64KB
122
        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.
123
        from revision_ids is returned. The verb takes a body containing the
124
        current search state, see do_body for details.
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
125
126
        :param repository: The repository to query in.
3172.5.8 by Robert Collins
Review feedback.
127
        :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.
128
        """
129
        self._revision_ids = revision_ids
130
        return None # Signal that we want a body.
131
132
    def do_body(self, body_bytes):
133
        """Process the current search state and perform the parent lookup.
134
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
135
        :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.
136
            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.
137
            format as Repository.get_revision_graph) which has been bz2
138
            compressed.
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
139
        """
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.
140
        repository = self._repository
141
        repository.lock_read()
142
        try:
143
            return self._do_repository_request(body_bytes)
144
        finally:
145
            repository.unlock()
146
147
    def _do_repository_request(self, body_bytes):
148
        repository = self._repository
149
        revision_ids = set(self._revision_ids)
150
        search, error = self.recreate_search(repository, body_bytes)
151
        if error is not None:
152
            return error
153
        # TODO might be nice to start up the search again; but thats not
154
        # written or tested yet.
155
        client_seen_revs = set(search.get_result().get_keys())
156
        # Always include the requested ids.
157
        client_seen_revs.difference_update(revision_ids)
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
158
        lines = []
159
        repo_graph = repository.get_graph()
160
        result = {}
161
        queried_revs = set()
162
        size_so_far = 0
163
        next_revs = revision_ids
164
        first_loop_done = False
165
        while next_revs:
166
            queried_revs.update(next_revs)
167
            parent_map = repo_graph.get_parent_map(next_revs)
168
            next_revs = set()
169
            for revision_id, parents in parent_map.iteritems():
170
                # adjust for the wire
171
                if parents == (_mod_revision.NULL_REVISION,):
172
                    parents = ()
173
                # prepare the next query
174
                next_revs.update(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.
175
                if revision_id not in client_seen_revs:
176
                    # Client does not have this revision, give it to it.
177
                    # add parents to the result
178
                    result[revision_id] = parents
179
                    # Approximate the serialized cost of this revision_id.
180
                    size_so_far += 2 + len(revision_id) + sum(map(len, parents))
181
            # get all the directly asked for parents, and then flesh out to
182
            # 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.
183
            # 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.
184
            # 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.
185
            if self.no_extra_results or (
186
                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.
187
                next_revs = set()
188
                break
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
189
            # don't query things we've already queried
190
            next_revs.difference_update(queried_revs)
191
            first_loop_done = True
192
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.
193
        # sorting trivially puts lexographically similar revision ids together.
194
        # Compression FTW.
195
        for revision, parents in sorted(result.items()):
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
196
            lines.append(' '.join((revision, ) + tuple(parents)))
197
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.
198
        return SuccessfulSmartServerResponse(
3211.5.2 by Robert Collins
Change RemoteRepository.get_parent_map to use bz2 not gzip for compression.
199
            ('ok', ), bz2.compress('\n'.join(lines)))
3172.5.6 by Robert Collins
Create new smart server verb Repository.get_parent_map.
200
201
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
202
class SmartServerRepositoryGetRevisionGraph(SmartServerRepositoryReadLocked):
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
203
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
204
    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)
205
        """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.
206
207
        Deprecated as of bzr 1.4, but supported for older clients.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
208
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
209
        :param repository: The repository to query in.
210
        :param revision_id: The utf8 encoded revision_id to get a graph from.
211
        :return: A smart server response where the body contains an utf8
212
            encoded flattened list of the revision graph.
213
        """
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
214
        if not revision_id:
215
            revision_id = None
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
216
217
        lines = []
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
218
        graph = repository.get_graph()
219
        if revision_id:
220
            search_ids = [revision_id]
221
        else:
222
            search_ids = repository.all_revision_ids()
223
        search = graph._make_breadth_first_searcher(search_ids)
224
        transitive_ids = set()
225
        map(transitive_ids.update, list(search))
226
        parent_map = graph.get_parent_map(transitive_ids)
3287.6.8 by Robert Collins
Reduce code duplication as per review.
227
        revision_graph = _strip_NULL_ghosts(parent_map)
3287.6.1 by Robert Collins
* ``VersionedFile.get_graph`` is deprecated, with no replacement method.
228
        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.
229
            # Note that we return an empty body, rather than omitting the body.
230
            # This way the client knows that it can always expect to find a body
231
            # 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.
232
            return FailedSmartServerResponse(('nosuchrevision', revision_id), '')
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
233
234
        for revision, parents in revision_graph.items():
2592.3.50 by Robert Collins
Merge bzr.dev.
235
            lines.append(' '.join((revision, ) + tuple(parents)))
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
236
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
237
        return SuccessfulSmartServerResponse(('ok', ), '\n'.join(lines))
2018.5.67 by Wouter van Heyst
Implement RemoteRepository.get_revision_graph (Wouter van Heyst, Robert Collins)
238
239
2018.5.56 by Robert Collins
Factor out code we expect to be common in SmartServerRequestHasRevision to SmartServerRepositoryRequest (Robert Collins, Vincent Ladeuil).
240
class SmartServerRequestHasRevision(SmartServerRepositoryRequest):
241
242
    def do_repository_request(self, repository, revision_id):
243
        """Return ok if a specific revision is in the repository at path.
244
245
        :param repository: The repository to query in.
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
246
        :param revision_id: The utf8 encoded revision_id to lookup.
247
        :return: A smart server response of ('ok', ) if the revision is
248
            present.
249
        """
2018.5.83 by Andrew Bennetts
Fix some test failures caused by the switch from unicode to UTF-8-encoded strs for revision IDs.
250
        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.
251
            return SuccessfulSmartServerResponse(('yes', ))
2018.5.40 by Robert Collins
Implement a remote Repository.has_revision method.
252
        else:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
253
            return SuccessfulSmartServerResponse(('no', ))
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
254
255
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
256
class SmartServerRepositoryGatherStats(SmartServerRepositoryRequest):
257
258
    def do_repository_request(self, repository, revid, committers):
259
        """Return the result of repository.gather_stats().
260
261
        :param repository: The repository to query in.
262
        :param revid: utf8 encoded rev id or an empty string to indicate None
263
        :param committers: 'yes' or 'no'.
264
265
        :return: A SmartServerResponse ('ok',), a encoded body looking like
266
              committers: 1
267
              firstrev: 1234.230 0
268
              latestrev: 345.700 3600
269
              revisions: 2
270
271
              But containing only fields returned by the gather_stats() call
272
        """
273
        if revid == '':
274
            decoded_revision_id = None
275
        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.
276
            decoded_revision_id = revid
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
277
        if committers == 'yes':
278
            decoded_committers = True
279
        else:
280
            decoded_committers = None
281
        stats = repository.gather_stats(decoded_revision_id, decoded_committers)
282
283
        body = ''
284
        if stats.has_key('committers'):
285
            body += 'committers: %d\n' % stats['committers']
286
        if stats.has_key('firstrev'):
287
            body += 'firstrev: %.3f %d\n' % stats['firstrev']
288
        if stats.has_key('latestrev'):
289
             body += 'latestrev: %.3f %d\n' % stats['latestrev']
290
        if stats.has_key('revisions'):
291
            body += 'revisions: %d\n' % stats['revisions']
292
        if stats.has_key('size'):
293
            body += 'size: %d\n' % stats['size']
294
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
295
        return SuccessfulSmartServerResponse(('ok', ), body)
2018.10.2 by v.ladeuil+lp at free
gather_stats server side and request registration
296
297
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
298
class SmartServerRepositoryIsShared(SmartServerRepositoryRequest):
299
300
    def do_repository_request(self, repository):
301
        """Return the result of repository.is_shared().
302
303
        :param repository: The repository to query in.
304
        :return: A smart server response of ('yes', ) if the repository is
305
            shared, and ('no', ) if it is not.
306
        """
307
        if repository.is_shared():
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
308
            return SuccessfulSmartServerResponse(('yes', ))
2018.5.57 by Robert Collins
Implement RemoteRepository.is_shared (Robert Collins, Vincent Ladeuil).
309
        else:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
310
            return SuccessfulSmartServerResponse(('no', ))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
311
312
313
class SmartServerRepositoryLockWrite(SmartServerRepositoryRequest):
314
2018.5.79 by Andrew Bennetts
Implement RemoteBranch.lock_write/unlock as smart operations.
315
    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
316
        # XXX: this probably should not have a token.
317
        if token == '':
318
            token = None
319
        try:
320
            token = repository.lock_write(token=token)
321
        except errors.LockContention, e:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
322
            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.
323
        except errors.UnlockableTransport:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
324
            return FailedSmartServerResponse(('UnlockableTransport',))
2872.5.3 by Martin Pool
Pass back LockFailed from smart server lock methods
325
        except errors.LockFailed, e:
326
            return FailedSmartServerResponse(('LockFailed',
327
                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.
328
        if token is not None:
329
            repository.leave_lock_in_place()
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
330
        repository.unlock()
331
        if token is None:
332
            token = ''
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
333
        return SuccessfulSmartServerResponse(('ok', token))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
334
335
336
class SmartServerRepositoryUnlock(SmartServerRepositoryRequest):
337
338
    def do_repository_request(self, repository, token):
339
        try:
340
            repository.lock_write(token=token)
341
        except errors.TokenMismatch, e:
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
342
            return FailedSmartServerResponse(('TokenMismatch',))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
343
        repository.dont_leave_lock_in_place()
344
        repository.unlock()
2432.4.5 by Robert Collins
Make using SuccessfulSmartServerResponse and FailedSmartServerResponse mandatory rather than optional in smart server logic.
345
        return SuccessfulSmartServerResponse(('ok',))
2018.5.78 by Andrew Bennetts
Implement RemoteRepository.lock_write/unlock to expect and send tokens over the
346
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
347
4017.3.4 by Robert Collins
Create a verb for Repository.set_make_working_trees.
348
class SmartServerRepositorySetMakeWorkingTrees(SmartServerRepositoryRequest):
349
350
    def do_repository_request(self, repository, str_bool_new_value):
351
        if str_bool_new_value == 'True':
352
            new_value = True
353
        else:
354
            new_value = False
355
        repository.set_make_working_trees(new_value)
356
        return SuccessfulSmartServerResponse(('ok',))
357
358
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
359
class SmartServerRepositoryTarball(SmartServerRepositoryRequest):
2018.18.11 by Martin Pool
merge hpss changes
360
    """Get the raw repository files as a tarball.
361
362
    The returned tarball contains a .bzr control directory which in turn
363
    contains a repository.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
364
365
    This takes one parameter, compression, which currently must be
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
366
    "", "gz", or "bz2".
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
367
368
    This is used to implement the Repository.copy_content_into operation.
2018.18.1 by Martin Pool
Add stub Repository.tarball smart method
369
    """
370
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
371
    def do_repository_request(self, repository, compression):
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
372
        tmp_dirname, tmp_repo = self._copy_to_tempdir(repository)
2018.18.5 by Martin Pool
Repository.tarball locks repository while running for consistency
373
        try:
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
374
            controldir_name = tmp_dirname + '/.bzr'
375
            return self._tarfile_response(controldir_name, compression)
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
376
        finally:
377
            osutils.rmtree(tmp_dirname)
378
379
    def _copy_to_tempdir(self, from_repo):
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
380
        tmp_dirname = osutils.mkdtemp(prefix='tmpbzrclone')
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
381
        tmp_bzrdir = from_repo.bzrdir._format.initialize(tmp_dirname)
382
        tmp_repo = from_repo._format.initialize(tmp_bzrdir)
383
        from_repo.copy_content_into(tmp_repo)
384
        return tmp_dirname, tmp_repo
385
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
386
    def _tarfile_response(self, tmp_dirname, compression):
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
387
        temp = tempfile.NamedTemporaryFile()
388
        try:
2557.1.1 by Martin Pool
[BUG 119330] Fix tempfile permissions error in smart server tar bundling (under windows) (Martin_)
389
            self._tarball_of_dir(tmp_dirname, compression, temp.file)
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
390
            # all finished; write the tempfile out to the network
391
            temp.seek(0)
2420.2.2 by Andrew Bennetts
Merge tarball branch that's already with PQM, resolving conflicts.
392
            return SuccessfulSmartServerResponse(('ok',), temp.read())
3638.3.2 by Vincent Ladeuil
Fix all calls to tempfile.mkdtemp to osutils.mkdtemp.
393
            # FIXME: Don't read the whole thing into memory here; rather stream
394
            # it out from the file onto the network. mbp 20070411
2018.18.2 by Martin Pool
smart method Repository.tarball actually returns the tarball
395
        finally:
396
            temp.close()
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
397
2557.1.1 by Martin Pool
[BUG 119330] Fix tempfile permissions error in smart server tar bundling (under windows) (Martin_)
398
    def _tarball_of_dir(self, dirname, compression, ofile):
2571.2.2 by Ian Clatworthy
use basename as poolie recommended
399
        filename = os.path.basename(ofile.name)
400
        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
401
            mode='w|' + compression)
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
402
        try:
403
            # The tarball module only accepts ascii names, and (i guess)
404
            # packs them with their 8bit names.  We know all the files
405
            # within the repository have ASCII names so the should be safe
406
            # to pack in.
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
407
            dirname = dirname.encode(sys.getfilesystemencoding())
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
408
            # python's tarball module includes the whole path by default so
409
            # override it
3376.2.4 by Martin Pool
Remove every assert statement from bzrlib!
410
            if not dirname.endswith('.bzr'):
411
                raise ValueError(dirname)
2018.18.10 by Martin Pool
copy_content_into from Remote repositories by using temporary directories on both ends.
412
            tarball.add(dirname, '.bzr') # recursive by default
2018.18.9 by Martin Pool
remote Repository.tarball builds a temporary directory and tars that
413
        finally:
414
            tarball.close()
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
415
416
417
class SmartServerRepositoryInsertStream(SmartServerRepositoryRequest):
418
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
419
    def do_repository_request(self, repository, resume_tokens):
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
420
        """StreamSink.insert_stream for a remote repository."""
421
        repository.lock_write()
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
422
        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.
423
        self.tokens = tokens
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
424
        self.repository = repository
425
        self.stream_decoder = pack.ContainerPushParser()
426
        self.src_format = None
427
        self.queue = Queue.Queue()
428
        self.insert_thread = None
429
430
    def do_chunk(self, body_stream_chunk):
431
        self.stream_decoder.accept_bytes(body_stream_chunk)
432
        for record in self.stream_decoder.read_pending_records():
433
            record_names, record_bytes = record
434
            if self.src_format is None:
435
                src_format_name = record_bytes
436
                src_format = network_format_registry.get(src_format_name)
437
                self.src_format = src_format
438
                self.insert_thread = threading.Thread(target=self._inserter_thread)
439
                self.insert_thread.start()
440
            else:
441
                record_name, = record_names
442
                substream_type = record_name[0]
443
                stream = NetworkRecordStream([record_bytes])
444
                for record in stream.read():
445
                    self.queue.put((substream_type, [record]))
446
447
    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.
448
        try:
449
            self.insert_result = self.repository._get_sink().insert_stream(
450
                self.blocking_read_stream(), self.src_format, self.tokens)
451
            self.insert_ok = True
452
        except:
453
            self.insert_exception = sys.exc_info()
454
            self.insert_ok = False
4022.1.6 by Robert Collins
Cherrypick and polish the RemoteSink for streaming push.
455
456
    def blocking_read_stream(self):
457
        while True:
458
            item = self.queue.get()
459
            if item is StopIteration:
460
                return
461
            else:
462
                yield item
463
464
    def do_end(self):
465
        self.queue.put(StopIteration)
466
        if self.insert_thread is not None:
467
            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.
468
        if not self.insert_ok:
469
            exc_info = self.insert_exception
470
            raise exc_info[0], exc_info[1], exc_info[2]
471
        write_group_tokens, missing_keys = self.insert_result
472
        if write_group_tokens or missing_keys:
473
            # bzip needed? missing keys should typically be a small set.
474
            # Should this be a streaming body response ?
475
            missing_keys = sorted(missing_keys)
476
            bytes = bencode.bencode((write_group_tokens, missing_keys))
477
            self.repository.unlock()
478
            return SuccessfulSmartServerResponse(('missing-basis', bytes))
4029.2.1 by Robert Collins
Support streaming push to stacked branches.
479
        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.
480
            self.repository.unlock()
481
            return SuccessfulSmartServerResponse(('ok', ))