Mercurial > hg
comparison mercurial/debugcommands.py @ 36534:5faeabb07cf5
debugcommands: support for triggering push protocol
The mechanism for pushing to a remote is a bit more complicated
than other commands. On SSH, we wait for a positive reply from
the server before we start sending the bundle payload.
This commit adds a mechanism to the "command" action in
`hg debugwireproto` to trigger the "push protocol" and to
specify a file whose contents should be submitted as the command
payload.
With this new feature, we implement a handful of tests for the
"unbundle" command. We try to cover various server failures and
hook/output scenarios so protocol behavior is as comprehensively
tested as possible. Even with so much test output, we only cover
bundle1 with Python hooks. There's still a lot of test coverage
that needs to be implemented. But this is certainly a good start.
Because there are so many new tests, we split these tests into their
own test file.
In order to make output deterministic, we need to disable the
doublepipe primitive. We add an option to `hg debugwireproto`
to do that. Because something in the bowels of the peer does a
read of stderr, we still capture read I/O from stderr. So there
is test coverage of what the server emits.
The tests around I/O capture some wonkiness. For example,
interleaved ui.write() and ui.write_err() calls are emitted in
order. However, (presumably due to buffering), print() to
sys.stdout and sys.stderr aren't in order.
We currently only test bundle1 because bundle2 is substantially
harder to test because it is more complicated (the server responds
with a stream containing a bundle2 instead of a frame).
Differential Revision: https://phab.mercurial-scm.org/D2471
author | Gregory Szorc <gregory.szorc@gmail.com> |
---|---|
date | Mon, 26 Feb 2018 18:01:13 -0800 |
parents | 097ad1079192 |
children | 149fd142f498 |
comparison
equal
deleted
inserted
replaced
36533:1a36ef7df70a | 36534:5faeabb07cf5 |
---|---|
2566 | 2566 |
2567 @command('debugwireproto', | 2567 @command('debugwireproto', |
2568 [ | 2568 [ |
2569 ('', 'localssh', False, _('start an SSH server for this repo')), | 2569 ('', 'localssh', False, _('start an SSH server for this repo')), |
2570 ('', 'peer', '', _('construct a specific version of the peer')), | 2570 ('', 'peer', '', _('construct a specific version of the peer')), |
2571 ('', 'noreadstderr', False, _('do not read from stderr of the remote')), | |
2571 ] + cmdutil.remoteopts, | 2572 ] + cmdutil.remoteopts, |
2572 _('[REPO]'), | 2573 _('[REPO]'), |
2573 optionalrepo=True) | 2574 optionalrepo=True) |
2574 def debugwireproto(ui, repo, **opts): | 2575 def debugwireproto(ui, repo, **opts): |
2575 """send wire protocol commands to a server | 2576 """send wire protocol commands to a server |
2584 ``--peer`` can be used to bypass the handshake protocol and construct a | 2585 ``--peer`` can be used to bypass the handshake protocol and construct a |
2585 peer instance using the specified class type. Valid values are ``raw``, | 2586 peer instance using the specified class type. Valid values are ``raw``, |
2586 ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending raw data | 2587 ``ssh1``, and ``ssh2``. ``raw`` instances only allow sending raw data |
2587 payloads and don't support higher-level command actions. | 2588 payloads and don't support higher-level command actions. |
2588 | 2589 |
2590 ``--noreadstderr`` can be used to disable automatic reading from stderr | |
2591 of the peer (for SSH connections only). Disabling automatic reading of | |
2592 stderr is useful for making output more deterministic. | |
2593 | |
2589 Commands are issued via a mini language which is specified via stdin. | 2594 Commands are issued via a mini language which is specified via stdin. |
2590 The language consists of individual actions to perform. An action is | 2595 The language consists of individual actions to perform. An action is |
2591 defined by a block. A block is defined as a line with no leading | 2596 defined by a block. A block is defined as a line with no leading |
2592 space followed by 0 or more lines with leading space. Blocks are | 2597 space followed by 0 or more lines with leading space. Blocks are |
2593 effectively a high-level command with additional metadata. | 2598 effectively a high-level command with additional metadata. |
2626 command listkeys | 2631 command listkeys |
2627 namespace bookmarks | 2632 namespace bookmarks |
2628 | 2633 |
2629 Values are interpreted as Python b'' literals. This allows encoding | 2634 Values are interpreted as Python b'' literals. This allows encoding |
2630 special byte sequences via backslash escaping. | 2635 special byte sequences via backslash escaping. |
2636 | |
2637 The following arguments have special meaning: | |
2638 | |
2639 ``PUSHFILE`` | |
2640 When defined, the *push* mechanism of the peer will be used instead | |
2641 of the static request-response mechanism and the content of the | |
2642 file specified in the value of this argument will be sent as the | |
2643 command payload. | |
2644 | |
2645 This can be used to submit a local bundle file to the remote. | |
2631 | 2646 |
2632 batchbegin | 2647 batchbegin |
2633 ---------- | 2648 ---------- |
2634 | 2649 |
2635 Instruct the peer to begin a batched send. | 2650 Instruct the peer to begin a batched send. |
2710 logdata=True) | 2725 logdata=True) |
2711 | 2726 |
2712 # --localssh also implies the peer connection settings. | 2727 # --localssh also implies the peer connection settings. |
2713 | 2728 |
2714 url = 'ssh://localserver' | 2729 url = 'ssh://localserver' |
2730 autoreadstderr = not opts['noreadstderr'] | |
2715 | 2731 |
2716 if opts['peer'] == 'ssh1': | 2732 if opts['peer'] == 'ssh1': |
2717 ui.write(_('creating ssh peer for wire protocol version 1\n')) | 2733 ui.write(_('creating ssh peer for wire protocol version 1\n')) |
2718 peer = sshpeer.sshv1peer(ui, url, proc, stdin, stdout, stderr, | 2734 peer = sshpeer.sshv1peer(ui, url, proc, stdin, stdout, stderr, |
2719 None) | 2735 None, autoreadstderr=autoreadstderr) |
2720 elif opts['peer'] == 'ssh2': | 2736 elif opts['peer'] == 'ssh2': |
2721 ui.write(_('creating ssh peer for wire protocol version 2\n')) | 2737 ui.write(_('creating ssh peer for wire protocol version 2\n')) |
2722 peer = sshpeer.sshv2peer(ui, url, proc, stdin, stdout, stderr, | 2738 peer = sshpeer.sshv2peer(ui, url, proc, stdin, stdout, stderr, |
2723 None) | 2739 None, autoreadstderr=autoreadstderr) |
2724 elif opts['peer'] == 'raw': | 2740 elif opts['peer'] == 'raw': |
2725 ui.write(_('using raw connection to peer\n')) | 2741 ui.write(_('using raw connection to peer\n')) |
2726 peer = None | 2742 peer = None |
2727 else: | 2743 else: |
2728 ui.write(_('creating ssh peer from handshake results\n')) | 2744 ui.write(_('creating ssh peer from handshake results\n')) |
2729 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr) | 2745 peer = sshpeer.makepeer(ui, url, proc, stdin, stdout, stderr, |
2746 autoreadstderr=autoreadstderr) | |
2730 | 2747 |
2731 else: | 2748 else: |
2732 raise error.Abort(_('only --localssh is currently supported')) | 2749 raise error.Abort(_('only --localssh is currently supported')) |
2733 | 2750 |
2734 batchedcommands = None | 2751 batchedcommands = None |
2767 if batchedcommands is not None: | 2784 if batchedcommands is not None: |
2768 batchedcommands.append((command, args)) | 2785 batchedcommands.append((command, args)) |
2769 continue | 2786 continue |
2770 | 2787 |
2771 ui.status(_('sending %s command\n') % command) | 2788 ui.status(_('sending %s command\n') % command) |
2772 res = peer._call(command, **args) | 2789 |
2773 ui.status(_('response: %s\n') % util.escapedata(res)) | 2790 if 'PUSHFILE' in args: |
2791 with open(args['PUSHFILE'], r'rb') as fh: | |
2792 del args['PUSHFILE'] | |
2793 res, output = peer._callpush(command, fh, **args) | |
2794 ui.status(_('result: %s\n') % util.escapedata(res)) | |
2795 ui.status(_('remote output: %s\n') % | |
2796 util.escapedata(output)) | |
2797 else: | |
2798 res = peer._call(command, **args) | |
2799 ui.status(_('response: %s\n') % util.escapedata(res)) | |
2774 | 2800 |
2775 elif action == 'batchbegin': | 2801 elif action == 'batchbegin': |
2776 if batchedcommands is not None: | 2802 if batchedcommands is not None: |
2777 raise error.Abort(_('nested batchbegin not allowed')) | 2803 raise error.Abort(_('nested batchbegin not allowed')) |
2778 | 2804 |