~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/smart/bzrdir.py

(jameinel) Allow 'bzr serve' to interpret SIGHUP as a graceful shutdown.
 (bug #795025) (John A Meinel)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006 Canonical Ltd
 
1
# Copyright (C) 2006-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
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
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Server-side bzrdir related request implmentations."""
18
18
 
19
19
 
20
 
from bzrlib import errors
21
 
from bzrlib.bzrdir import BzrDir, BzrDirFormat
 
20
from bzrlib import branch, errors, repository, urlutils
 
21
from bzrlib.bzrdir import (
 
22
    BzrDir,
 
23
    BzrDirFormat,
 
24
    BzrDirMetaFormat1,
 
25
    BzrProber,
 
26
    )
 
27
from bzrlib.controldir import (
 
28
    network_format_registry,
 
29
    )
22
30
from bzrlib.smart.request import (
23
31
    FailedSmartServerResponse,
24
32
    SmartServerRequest,
29
37
class SmartServerRequestOpenBzrDir(SmartServerRequest):
30
38
 
31
39
    def do(self, path):
32
 
        from bzrlib.bzrdir import BzrDirFormat
33
40
        try:
34
41
            t = self.transport_from_client_path(path)
35
42
        except errors.PathNotChild:
40
47
            # clients that don't anticipate errors from this method.
41
48
            answer = 'no'
42
49
        else:
43
 
            default_format = BzrDirFormat.get_default_format()
44
 
            real_bzrdir = default_format.open(t, _found=True)
 
50
            bzr_prober = BzrProber()
45
51
            try:
46
 
                real_bzrdir._format.probe_transport(t)
 
52
                bzr_prober.probe_transport(t)
47
53
            except (errors.NotBranchError, errors.UnknownFormatError):
48
54
                answer = 'no'
49
55
            else:
51
57
        return SuccessfulSmartServerResponse((answer,))
52
58
 
53
59
 
54
 
class SmartServerRequestFindRepository(SmartServerRequest):
 
60
class SmartServerRequestOpenBzrDir_2_1(SmartServerRequest):
 
61
 
 
62
    def do(self, path):
 
63
        """Is there a BzrDir present, and if so does it have a working tree?
 
64
 
 
65
        New in 2.1.
 
66
        """
 
67
        try:
 
68
            t = self.transport_from_client_path(path)
 
69
        except errors.PathNotChild:
 
70
            # The client is trying to ask about a path that they have no access
 
71
            # to.
 
72
            return SuccessfulSmartServerResponse(('no',))
 
73
        try:
 
74
            bd = BzrDir.open_from_transport(t)
 
75
        except errors.NotBranchError:
 
76
            answer = ('no',)
 
77
        else:
 
78
            answer = ('yes',)
 
79
            if bd.has_workingtree():
 
80
                answer += ('yes',)
 
81
            else:
 
82
                answer += ('no',)
 
83
        return SuccessfulSmartServerResponse(answer)
 
84
 
 
85
 
 
86
class SmartServerRequestBzrDir(SmartServerRequest):
 
87
 
 
88
    def do(self, path, *args):
 
89
        """Open a BzrDir at path, and return `self.do_bzrdir_request(*args)`."""
 
90
        try:
 
91
            self._bzrdir = BzrDir.open_from_transport(
 
92
                self.transport_from_client_path(path))
 
93
        except errors.NotBranchError, e:
 
94
            return FailedSmartServerResponse(('nobranch',))
 
95
        return self.do_bzrdir_request(*args)
55
96
 
56
97
    def _boolean_to_yes_no(self, a_boolean):
57
98
        if a_boolean:
59
100
        else:
60
101
            return 'no'
61
102
 
 
103
    def _format_to_capabilities(self, repo_format):
 
104
        rich_root = self._boolean_to_yes_no(repo_format.rich_root_data)
 
105
        tree_ref = self._boolean_to_yes_no(
 
106
            repo_format.supports_tree_reference)
 
107
        external_lookup = self._boolean_to_yes_no(
 
108
            repo_format.supports_external_lookups)
 
109
        return rich_root, tree_ref, external_lookup
 
110
 
 
111
    def _repo_relpath(self, current_transport, repository):
 
112
        """Get the relative path for repository from current_transport."""
 
113
        # the relpath of the bzrdir in the found repository gives us the
 
114
        # path segments to pop-out.
 
115
        relpath = repository.user_transport.relpath(
 
116
            current_transport.base)
 
117
        if len(relpath):
 
118
            segments = ['..'] * len(relpath.split('/'))
 
119
        else:
 
120
            segments = []
 
121
        return '/'.join(segments)
 
122
 
 
123
 
 
124
class SmartServerBzrDirRequestCloningMetaDir(SmartServerRequestBzrDir):
 
125
 
 
126
    def do_bzrdir_request(self, require_stacking):
 
127
        """Get the format that should be used when cloning from this dir.
 
128
 
 
129
        New in 1.13.
 
130
        
 
131
        :return: on success, a 3-tuple of network names for (control,
 
132
            repository, branch) directories, where '' signifies "not present".
 
133
            If this BzrDir contains a branch reference then this will fail with
 
134
            BranchReference; clients should resolve branch references before
 
135
            calling this RPC.
 
136
        """
 
137
        try:
 
138
            branch_ref = self._bzrdir.get_branch_reference()
 
139
        except errors.NotBranchError:
 
140
            branch_ref = None
 
141
        if branch_ref is not None:
 
142
            # The server shouldn't try to resolve references, and it quite
 
143
            # possibly can't reach them anyway.  The client needs to resolve
 
144
            # the branch reference to determine the cloning_metadir.
 
145
            return FailedSmartServerResponse(('BranchReference',))
 
146
        if require_stacking == "True":
 
147
            require_stacking = True
 
148
        else:
 
149
            require_stacking = False
 
150
        control_format = self._bzrdir.cloning_metadir(
 
151
            require_stacking=require_stacking)
 
152
        control_name = control_format.network_name()
 
153
        if not control_format.fixed_components:
 
154
            branch_name = ('branch',
 
155
                control_format.get_branch_format().network_name())
 
156
            repository_name = control_format.repository_format.network_name()
 
157
        else:
 
158
            # Only MetaDir has delegated formats today.
 
159
            branch_name = ('branch', '')
 
160
            repository_name = ''
 
161
        return SuccessfulSmartServerResponse((control_name, repository_name,
 
162
            branch_name))
 
163
 
 
164
 
 
165
class SmartServerRequestCreateBranch(SmartServerRequestBzrDir):
 
166
 
 
167
    def do(self, path, network_name):
 
168
        """Create a branch in the bzr dir at path.
 
169
 
 
170
        This operates precisely like 'bzrdir.create_branch'.
 
171
 
 
172
        If a bzrdir is not present, an exception is propogated
 
173
        rather than 'no branch' because these are different conditions (and
 
174
        this method should only be called after establishing that a bzr dir
 
175
        exists anyway).
 
176
 
 
177
        This is the initial version of this method introduced to the smart
 
178
        server for 1.13.
 
179
 
 
180
        :param path: The path to the bzrdir.
 
181
        :param network_name: The network name of the branch type to create.
 
182
        :return: ('ok', branch_format, repo_path, rich_root, tree_ref,
 
183
            external_lookup, repo_format)
 
184
        """
 
185
        bzrdir = BzrDir.open_from_transport(
 
186
            self.transport_from_client_path(path))
 
187
        format = branch.network_format_registry.get(network_name)
 
188
        bzrdir.branch_format = format
 
189
        result = format.initialize(bzrdir)
 
190
        rich_root, tree_ref, external_lookup = self._format_to_capabilities(
 
191
            result.repository._format)
 
192
        branch_format = result._format.network_name()
 
193
        repo_format = result.repository._format.network_name()
 
194
        repo_path = self._repo_relpath(bzrdir.root_transport,
 
195
            result.repository)
 
196
        # branch format, repo relpath, rich_root, tree_ref, external_lookup,
 
197
        # repo_network_name
 
198
        return SuccessfulSmartServerResponse(('ok', branch_format, repo_path,
 
199
            rich_root, tree_ref, external_lookup, repo_format))
 
200
 
 
201
 
 
202
class SmartServerRequestCreateRepository(SmartServerRequestBzrDir):
 
203
 
 
204
    def do(self, path, network_name, shared):
 
205
        """Create a repository in the bzr dir at path.
 
206
 
 
207
        This operates precisely like 'bzrdir.create_repository'.
 
208
 
 
209
        If a bzrdir is not present, an exception is propagated
 
210
        rather than 'no branch' because these are different conditions (and
 
211
        this method should only be called after establishing that a bzr dir
 
212
        exists anyway).
 
213
 
 
214
        This is the initial version of this method introduced to the smart
 
215
        server for 1.13.
 
216
 
 
217
        :param path: The path to the bzrdir.
 
218
        :param network_name: The network name of the repository type to create.
 
219
        :param shared: The value to pass create_repository for the shared
 
220
            parameter.
 
221
        :return: (ok, rich_root, tree_ref, external_lookup, network_name)
 
222
        """
 
223
        bzrdir = BzrDir.open_from_transport(
 
224
            self.transport_from_client_path(path))
 
225
        shared = shared == 'True'
 
226
        format = repository.network_format_registry.get(network_name)
 
227
        bzrdir.repository_format = format
 
228
        result = format.initialize(bzrdir, shared=shared)
 
229
        rich_root, tree_ref, external_lookup = self._format_to_capabilities(
 
230
            result._format)
 
231
        return SuccessfulSmartServerResponse(('ok', rich_root, tree_ref,
 
232
            external_lookup, result._format.network_name()))
 
233
 
 
234
 
 
235
class SmartServerRequestFindRepository(SmartServerRequestBzrDir):
 
236
 
62
237
    def _find(self, path):
63
238
        """try to find a repository from path upwards
64
 
        
 
239
 
65
240
        This operates precisely like 'bzrdir.find_repository'.
66
 
        
67
 
        :return: (relpath, rich_root, tree_ref, external_lookup) flags. All are
68
 
            strings, relpath is a / prefixed path, and the other three are
69
 
            either 'yes' or 'no'.
 
241
 
 
242
        :return: (relpath, rich_root, tree_ref, external_lookup, network_name).
 
243
            All are strings, relpath is a / prefixed path, the next three are
 
244
            either 'yes' or 'no', and the last is a repository format network
 
245
            name.
70
246
        :raises errors.NoRepositoryPresent: When there is no repository
71
247
            present.
72
248
        """
73
249
        bzrdir = BzrDir.open_from_transport(
74
250
            self.transport_from_client_path(path))
75
251
        repository = bzrdir.find_repository()
76
 
        # the relpath of the bzrdir in the found repository gives us the 
77
 
        # path segments to pop-out.
78
 
        relpath = repository.bzrdir.root_transport.relpath(
79
 
            bzrdir.root_transport.base)
80
 
        if len(relpath):
81
 
            segments = ['..'] * len(relpath.split('/'))
82
 
        else:
83
 
            segments = []
84
 
        rich_root = self._boolean_to_yes_no(repository.supports_rich_root())
85
 
        tree_ref = self._boolean_to_yes_no(
86
 
            repository._format.supports_tree_reference)
87
 
        external_lookup = self._boolean_to_yes_no(
88
 
            repository._format.supports_external_lookups)
89
 
        return '/'.join(segments), rich_root, tree_ref, external_lookup
 
252
        path = self._repo_relpath(bzrdir.root_transport, repository)
 
253
        rich_root, tree_ref, external_lookup = self._format_to_capabilities(
 
254
            repository._format)
 
255
        network_name = repository._format.network_name()
 
256
        return path, rich_root, tree_ref, external_lookup, network_name
90
257
 
91
258
 
92
259
class SmartServerRequestFindRepositoryV1(SmartServerRequestFindRepository):
93
260
 
94
261
    def do(self, path):
95
262
        """try to find a repository from path upwards
96
 
        
 
263
 
97
264
        This operates precisely like 'bzrdir.find_repository'.
98
 
        
99
 
        If a bzrdir is not present, an exception is propogated
 
265
 
 
266
        If a bzrdir is not present, an exception is propagated
100
267
        rather than 'no branch' because these are different conditions.
101
268
 
102
269
        This is the initial version of this method introduced with the smart
106
273
        :return: norepository or ok, relpath.
107
274
        """
108
275
        try:
109
 
            path, rich_root, tree_ref, external_lookup = self._find(path)
 
276
            path, rich_root, tree_ref, external_lookup, name = self._find(path)
110
277
            return SuccessfulSmartServerResponse(('ok', path, rich_root, tree_ref))
111
278
        except errors.NoRepositoryPresent:
112
279
            return FailedSmartServerResponse(('norepository', ))
116
283
 
117
284
    def do(self, path):
118
285
        """try to find a repository from path upwards
119
 
        
 
286
 
120
287
        This operates precisely like 'bzrdir.find_repository'.
121
 
        
122
 
        If a bzrdir is not present, an exception is propogated
 
288
 
 
289
        If a bzrdir is not present, an exception is propagated
123
290
        rather than 'no branch' because these are different conditions.
124
291
 
125
292
        This is the second edition of this method introduced in bzr 1.3, which
126
293
        returns information about the supports_external_lookups format
127
294
        attribute too.
128
295
 
129
 
        :return: norepository or ok, relpath.
 
296
        :return: norepository or ok, relpath, rich_root, tree_ref,
 
297
            external_lookup.
130
298
        """
131
299
        try:
132
 
            path, rich_root, tree_ref, external_lookup = self._find(path)
 
300
            path, rich_root, tree_ref, external_lookup, name = self._find(path)
133
301
            return SuccessfulSmartServerResponse(
134
302
                ('ok', path, rich_root, tree_ref, external_lookup))
135
303
        except errors.NoRepositoryPresent:
136
304
            return FailedSmartServerResponse(('norepository', ))
137
305
 
138
306
 
 
307
class SmartServerRequestFindRepositoryV3(SmartServerRequestFindRepository):
 
308
 
 
309
    def do(self, path):
 
310
        """try to find a repository from path upwards
 
311
 
 
312
        This operates precisely like 'bzrdir.find_repository'.
 
313
 
 
314
        If a bzrdir is not present, an exception is propogated
 
315
        rather than 'no branch' because these are different conditions.
 
316
 
 
317
        This is the third edition of this method introduced in bzr 1.13, which
 
318
        returns information about the network name of the repository format.
 
319
 
 
320
        :return: norepository or ok, relpath, rich_root, tree_ref,
 
321
            external_lookup, network_name.
 
322
        """
 
323
        try:
 
324
            path, rich_root, tree_ref, external_lookup, name = self._find(path)
 
325
            return SuccessfulSmartServerResponse(
 
326
                ('ok', path, rich_root, tree_ref, external_lookup, name))
 
327
        except errors.NoRepositoryPresent:
 
328
            return FailedSmartServerResponse(('norepository', ))
 
329
 
 
330
 
 
331
class SmartServerBzrDirRequestConfigFile(SmartServerRequestBzrDir):
 
332
 
 
333
    def do_bzrdir_request(self):
 
334
        """Get the configuration bytes for a config file in bzrdir.
 
335
        
 
336
        The body is not utf8 decoded - it is the literal bytestream from disk.
 
337
        """
 
338
        config = self._bzrdir._get_config()
 
339
        if config is None:
 
340
            content = ''
 
341
        else:
 
342
            content = config._get_config_file().read()
 
343
        return SuccessfulSmartServerResponse((), content)
 
344
 
 
345
 
139
346
class SmartServerRequestInitializeBzrDir(SmartServerRequest):
140
347
 
141
348
    def do(self, path):
149
356
        return SuccessfulSmartServerResponse(('ok', ))
150
357
 
151
358
 
152
 
class SmartServerRequestOpenBranch(SmartServerRequest):
153
 
 
154
 
    def do(self, path):
155
 
        """try to open a branch at path and return ok/nobranch.
156
 
        
157
 
        If a bzrdir is not present, an exception is propogated
158
 
        rather than 'no branch' because these are different conditions.
 
359
class SmartServerRequestBzrDirInitializeEx(SmartServerRequestBzrDir):
 
360
 
 
361
    def parse_NoneTrueFalse(self, arg):
 
362
        if not arg:
 
363
            return None
 
364
        if arg == 'False':
 
365
            return False
 
366
        if arg == 'True':
 
367
            return True
 
368
        raise AssertionError("invalid arg %r" % arg)
 
369
 
 
370
    def parse_NoneString(self, arg):
 
371
        return arg or None
 
372
 
 
373
    def _serialize_NoneTrueFalse(self, arg):
 
374
        if arg is False:
 
375
            return 'False'
 
376
        if not arg:
 
377
            return ''
 
378
        return 'True'
 
379
 
 
380
    def do(self, bzrdir_network_name, path, use_existing_dir, create_prefix,
 
381
        force_new_repo, stacked_on, stack_on_pwd, repo_format_name,
 
382
        make_working_trees, shared_repo):
 
383
        """Initialize a bzrdir at path as per
 
384
        BzrDirFormat.initialize_on_transport_ex.
 
385
 
 
386
        New in 1.16.  (Replaces BzrDirFormat.initialize_ex verb from 1.15).
 
387
 
 
388
        :return: return SuccessfulSmartServerResponse((repo_path, rich_root,
 
389
            tree_ref, external_lookup, repo_network_name,
 
390
            repo_bzrdir_network_name, bzrdir_format_network_name,
 
391
            NoneTrueFalse(stacking), final_stack, final_stack_pwd,
 
392
            repo_lock_token))
159
393
        """
160
 
        bzrdir = BzrDir.open_from_transport(
161
 
            self.transport_from_client_path(path))
 
394
        target_transport = self.transport_from_client_path(path)
 
395
        format = network_format_registry.get(bzrdir_network_name)
 
396
        use_existing_dir = self.parse_NoneTrueFalse(use_existing_dir)
 
397
        create_prefix = self.parse_NoneTrueFalse(create_prefix)
 
398
        force_new_repo = self.parse_NoneTrueFalse(force_new_repo)
 
399
        stacked_on = self.parse_NoneString(stacked_on)
 
400
        stack_on_pwd = self.parse_NoneString(stack_on_pwd)
 
401
        make_working_trees = self.parse_NoneTrueFalse(make_working_trees)
 
402
        shared_repo = self.parse_NoneTrueFalse(shared_repo)
 
403
        if stack_on_pwd == '.':
 
404
            stack_on_pwd = target_transport.base
 
405
        repo_format_name = self.parse_NoneString(repo_format_name)
 
406
        repo, bzrdir, stacking, repository_policy = \
 
407
            format.initialize_on_transport_ex(target_transport,
 
408
            use_existing_dir=use_existing_dir, create_prefix=create_prefix,
 
409
            force_new_repo=force_new_repo, stacked_on=stacked_on,
 
410
            stack_on_pwd=stack_on_pwd, repo_format_name=repo_format_name,
 
411
            make_working_trees=make_working_trees, shared_repo=shared_repo)
 
412
        if repo is None:
 
413
            repo_path = ''
 
414
            repo_name = ''
 
415
            rich_root = tree_ref = external_lookup = ''
 
416
            repo_bzrdir_name = ''
 
417
            final_stack = None
 
418
            final_stack_pwd = None
 
419
            repo_lock_token = ''
 
420
        else:
 
421
            repo_path = self._repo_relpath(bzrdir.root_transport, repo)
 
422
            if repo_path == '':
 
423
                repo_path = '.'
 
424
            rich_root, tree_ref, external_lookup = self._format_to_capabilities(
 
425
                repo._format)
 
426
            repo_name = repo._format.network_name()
 
427
            repo_bzrdir_name = repo.bzrdir._format.network_name()
 
428
            final_stack = repository_policy._stack_on
 
429
            final_stack_pwd = repository_policy._stack_on_pwd
 
430
            # It is returned locked, but we need to do the lock to get the lock
 
431
            # token.
 
432
            repo.unlock()
 
433
            repo_lock_token = repo.lock_write().repository_token or ''
 
434
            if repo_lock_token:
 
435
                repo.leave_lock_in_place()
 
436
            repo.unlock()
 
437
        final_stack = final_stack or ''
 
438
        final_stack_pwd = final_stack_pwd or ''
 
439
 
 
440
        # We want this to be relative to the bzrdir.
 
441
        if final_stack_pwd:
 
442
            final_stack_pwd = urlutils.relative_url(
 
443
                target_transport.base, final_stack_pwd)
 
444
 
 
445
        # Can't meaningfully return a root path.
 
446
        if final_stack.startswith('/'):
 
447
            client_path = self._root_client_path + final_stack[1:]
 
448
            final_stack = urlutils.relative_url(
 
449
                self._root_client_path, client_path)
 
450
            final_stack_pwd = '.'
 
451
 
 
452
        return SuccessfulSmartServerResponse((repo_path, rich_root, tree_ref,
 
453
            external_lookup, repo_name, repo_bzrdir_name,
 
454
            bzrdir._format.network_name(),
 
455
            self._serialize_NoneTrueFalse(stacking), final_stack,
 
456
            final_stack_pwd, repo_lock_token))
 
457
 
 
458
 
 
459
class SmartServerRequestOpenBranch(SmartServerRequestBzrDir):
 
460
 
 
461
    def do_bzrdir_request(self):
 
462
        """open a branch at path and return the branch reference or branch."""
162
463
        try:
163
 
            reference_url = bzrdir.get_branch_reference()
 
464
            reference_url = self._bzrdir.get_branch_reference()
164
465
            if reference_url is None:
165
466
                return SuccessfulSmartServerResponse(('ok', ''))
166
467
            else:
167
468
                return SuccessfulSmartServerResponse(('ok', reference_url))
168
 
        except errors.NotBranchError:
169
 
            return FailedSmartServerResponse(('nobranch', ))
 
469
        except errors.NotBranchError, e:
 
470
            return FailedSmartServerResponse(('nobranch',))
 
471
 
 
472
 
 
473
class SmartServerRequestOpenBranchV2(SmartServerRequestBzrDir):
 
474
 
 
475
    def do_bzrdir_request(self):
 
476
        """open a branch at path and return the reference or format."""
 
477
        try:
 
478
            reference_url = self._bzrdir.get_branch_reference()
 
479
            if reference_url is None:
 
480
                br = self._bzrdir.open_branch(ignore_fallbacks=True)
 
481
                format = br._format.network_name()
 
482
                return SuccessfulSmartServerResponse(('branch', format))
 
483
            else:
 
484
                return SuccessfulSmartServerResponse(('ref', reference_url))
 
485
        except errors.NotBranchError, e:
 
486
            return FailedSmartServerResponse(('nobranch',))
 
487
 
 
488
 
 
489
class SmartServerRequestOpenBranchV3(SmartServerRequestBzrDir):
 
490
 
 
491
    def do_bzrdir_request(self):
 
492
        """Open a branch at path and return the reference or format.
 
493
        
 
494
        This version introduced in 2.1.
 
495
 
 
496
        Differences to SmartServerRequestOpenBranchV2:
 
497
          * can return 2-element ('nobranch', extra), where 'extra' is a string
 
498
            with an explanation like 'location is a repository'.  Previously
 
499
            a 'nobranch' response would never have more than one element.
 
500
        """
 
501
        try:
 
502
            reference_url = self._bzrdir.get_branch_reference()
 
503
            if reference_url is None:
 
504
                br = self._bzrdir.open_branch(ignore_fallbacks=True)
 
505
                format = br._format.network_name()
 
506
                return SuccessfulSmartServerResponse(('branch', format))
 
507
            else:
 
508
                return SuccessfulSmartServerResponse(('ref', reference_url))
 
509
        except errors.NotBranchError, e:
 
510
            # Stringify the exception so that its .detail attribute will be
 
511
            # filled out.
 
512
            str(e)
 
513
            resp = ('nobranch',)
 
514
            detail = e.detail
 
515
            if detail:
 
516
                if detail.startswith(': '):
 
517
                    detail = detail[2:]
 
518
                resp += (detail,)
 
519
            return FailedSmartServerResponse(resp)
 
520