# HG changeset patch # User Gregory Szorc # Date 2018-03-01 16:27:30 # Node ID bde0bd50f368cd9620003e7b0c89f468a13e657b # Parent 33c6f8f0388d0349df9fd5a43a2127cc13993db1 debugcommands: allow sending of simple commands with debugwireproto Previously, we only had support for low-level "raw" operations. A goal of `hg debugwireproto` is to allow easily performing higher-level primitives, such as sending a wire protocol command and reading its response. We implement a "command" action that does just this. Currently, we only support simple commands (those without payloads). We have basic support for sending command arguments. We don't yet support sending dictionary arguments. This will be implemented later. To prove it works, we add tests to test-ssh-proto.t that send some "listkeys" commands. Note: we don't observe/report os.read() events because these may not be deterministic. We instead observe/report the read() and readline() operations on the bufferedinputpipe. These *should* be deterministic. Differential Revision: https://phab.mercurial-scm.org/D2406 diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py --- a/mercurial/debugcommands.py +++ b/mercurial/debugcommands.py @@ -2614,6 +2614,21 @@ def debugwireproto(ui, repo, **opts): Behaves like ``raw`` except flushes output afterwards. + command + ----------- + + Send a request to run a named command, whose name follows the ``command`` + string. + + Arguments to the command are defined as lines in this block. The format of + each line is `` ``. e.g.:: + + command listkeys + namespace bookmarks + + Values are interpreted as Python b'' literals. This allows encoding + special byte sequences via backslash escaping. + close ----- @@ -2713,6 +2728,29 @@ def debugwireproto(ui, repo, **opts): stdin.flush() elif action == 'flush': stdin.flush() + elif action.startswith('command'): + if not peer: + raise error.Abort(_('cannot send commands unless peer instance ' + 'is available')) + + command = action.split(' ', 1)[1] + + args = {} + for line in lines: + # We need to allow empty values. + fields = line.lstrip().split(' ', 1) + if len(fields) == 1: + key = fields[0] + value = '' + else: + key, value = fields + + args[key] = util.unescapestr(value) + + ui.status(_('sending %s command\n') % command) + res = peer._call(command, **args) + ui.status(_('response: %s\n') % util.escapedata(res)) + elif action == 'close': peer.close() elif action == 'readavailable': diff --git a/tests/test-ssh-proto.t b/tests/test-ssh-proto.t --- a/tests/test-ssh-proto.t +++ b/tests/test-ssh-proto.t @@ -1,3 +1,23 @@ + $ cat > hgrc-sshv2 << EOF + > %include $HGRCPATH + > [experimental] + > sshpeer.advertise-v2 = true + > sshserver.support-v2 = true + > EOF + +Helper function to run protocol tests against multiple protocol versions. +This is easier than using #testcases because managing differences between +protocols with inline conditional output is hard to read. + + $ debugwireproto() { + > commands=`cat -` + > echo 'testing ssh1' + > echo "${commands}" | hg --verbose debugwireproto --localssh + > echo "" + > echo 'testing ssh2' + > echo "${commands}" | HGRCPATH=$TESTTMP/hgrc-sshv2 hg --verbose debugwireproto --localssh + > } + $ cat >> $HGRCPATH << EOF > [ui] > ssh = $PYTHON "$TESTDIR/dummyssh" @@ -1252,3 +1272,561 @@ Upgrade request must be followed by hell e> read(-1) -> 49: e> malformed handshake protocol: missing pairs 81\n e> -\n + + $ cd .. + +Test listkeys for listing namespaces + + $ hg init empty + $ cd empty + $ debugwireproto << EOF + > command listkeys + > namespace namespaces + > EOF + testing ssh1 + creating ssh peer from handshake results + i> write(104) -> None: + i> hello\n + i> between\n + i> pairs 81\n + i> 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 + i> flush() -> None + o> readline() -> 4: + o> 384\n + o> readline() -> 384: + o> capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN\n + o> readline() -> 2: + o> 1\n + o> readline() -> 1: + o> \n + sending listkeys command + i> write(9) -> None: + i> listkeys\n + i> write(13) -> None: + i> namespace 10\n + i> write(10) -> None: namespaces + i> flush() -> None + o> bufferedreadline() -> 3: + o> 30\n + o> bufferedread(30) -> 30: + o> bookmarks \n + o> namespaces \n + o> phases + response: bookmarks \nnamespaces \nphases + + testing ssh2 + creating ssh peer from handshake results + i> write(171) -> None: + i> upgrade * proto=exp-ssh-v2-0001\n (glob) + i> hello\n + i> between\n + i> pairs 81\n + i> 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 + i> flush() -> None + o> readline() -> 62: + o> upgraded * exp-ssh-v2-0001\n (glob) + o> readline() -> 4: + o> 383\n + o> read(383) -> 383: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + o> read(1) -> 1: + o> \n + sending listkeys command + i> write(9) -> None: + i> listkeys\n + i> write(13) -> None: + i> namespace 10\n + i> write(10) -> None: namespaces + i> flush() -> None + o> bufferedreadline() -> 3: + o> 30\n + o> bufferedread(30) -> 30: + o> bookmarks \n + o> namespaces \n + o> phases + response: bookmarks \nnamespaces \nphases + + $ cd .. + +Test listkeys for bookmarks + + $ hg init bookmarkrepo + $ cd bookmarkrepo + $ echo 0 > foo + $ hg add foo + $ hg -q commit -m initial + $ echo 1 > foo + $ hg commit -m second + +With no bookmarks set + + $ debugwireproto << EOF + > command listkeys + > namespace bookmarks + > EOF + testing ssh1 + creating ssh peer from handshake results + i> write(104) -> None: + i> hello\n + i> between\n + i> pairs 81\n + i> 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 + i> flush() -> None + o> readline() -> 4: + o> 384\n + o> readline() -> 384: + o> capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN\n + o> readline() -> 2: + o> 1\n + o> readline() -> 1: + o> \n + sending listkeys command + i> write(9) -> None: + i> listkeys\n + i> write(12) -> None: + i> namespace 9\n + i> write(9) -> None: bookmarks + i> flush() -> None + o> bufferedreadline() -> 2: + o> 0\n + response: + + testing ssh2 + creating ssh peer from handshake results + i> write(171) -> None: + i> upgrade * proto=exp-ssh-v2-0001\n (glob) + i> hello\n + i> between\n + i> pairs 81\n + i> 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 + i> flush() -> None + o> readline() -> 62: + o> upgraded * exp-ssh-v2-0001\n (glob) + o> readline() -> 4: + o> 383\n + o> read(383) -> 383: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + o> read(1) -> 1: + o> \n + sending listkeys command + i> write(9) -> None: + i> listkeys\n + i> write(12) -> None: + i> namespace 9\n + i> write(9) -> None: bookmarks + i> flush() -> None + o> bufferedreadline() -> 2: + o> 0\n + response: + +With a single bookmark set + + $ hg book -r 0 bookA + $ debugwireproto << EOF + > command listkeys + > namespace bookmarks + > EOF + testing ssh1 + creating ssh peer from handshake results + i> write(104) -> None: + i> hello\n + i> between\n + i> pairs 81\n + i> 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 + i> flush() -> None + o> readline() -> 4: + o> 384\n + o> readline() -> 384: + o> capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN\n + o> readline() -> 2: + o> 1\n + o> readline() -> 1: + o> \n + sending listkeys command + i> write(9) -> None: + i> listkeys\n + i> write(12) -> None: + i> namespace 9\n + i> write(9) -> None: bookmarks + i> flush() -> None + o> bufferedreadline() -> 3: + o> 46\n + o> bufferedread(46) -> 46: bookA 68986213bd4485ea51533535e3fc9e78007a711f + response: bookA 68986213bd4485ea51533535e3fc9e78007a711f + + testing ssh2 + creating ssh peer from handshake results + i> write(171) -> None: + i> upgrade * proto=exp-ssh-v2-0001\n (glob) + i> hello\n + i> between\n + i> pairs 81\n + i> 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 + i> flush() -> None + o> readline() -> 62: + o> upgraded * exp-ssh-v2-0001\n (glob) + o> readline() -> 4: + o> 383\n + o> read(383) -> 383: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + o> read(1) -> 1: + o> \n + sending listkeys command + i> write(9) -> None: + i> listkeys\n + i> write(12) -> None: + i> namespace 9\n + i> write(9) -> None: bookmarks + i> flush() -> None + o> bufferedreadline() -> 3: + o> 46\n + o> bufferedread(46) -> 46: bookA 68986213bd4485ea51533535e3fc9e78007a711f + response: bookA 68986213bd4485ea51533535e3fc9e78007a711f + +With multiple bookmarks set + + $ hg book -r 1 bookB + $ debugwireproto << EOF + > command listkeys + > namespace bookmarks + > EOF + testing ssh1 + creating ssh peer from handshake results + i> write(104) -> None: + i> hello\n + i> between\n + i> pairs 81\n + i> 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 + i> flush() -> None + o> readline() -> 4: + o> 384\n + o> readline() -> 384: + o> capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN\n + o> readline() -> 2: + o> 1\n + o> readline() -> 1: + o> \n + sending listkeys command + i> write(9) -> None: + i> listkeys\n + i> write(12) -> None: + i> namespace 9\n + i> write(9) -> None: bookmarks + i> flush() -> None + o> bufferedreadline() -> 3: + o> 93\n + o> bufferedread(93) -> 93: + o> bookA 68986213bd4485ea51533535e3fc9e78007a711f\n + o> bookB 1880f3755e2e52e3199e0ee5638128b08642f34d + response: bookA 68986213bd4485ea51533535e3fc9e78007a711f\nbookB 1880f3755e2e52e3199e0ee5638128b08642f34d + + testing ssh2 + creating ssh peer from handshake results + i> write(171) -> None: + i> upgrade * proto=exp-ssh-v2-0001\n (glob) + i> hello\n + i> between\n + i> pairs 81\n + i> 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 + i> flush() -> None + o> readline() -> 62: + o> upgraded * exp-ssh-v2-0001\n (glob) + o> readline() -> 4: + o> 383\n + o> read(383) -> 383: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + o> read(1) -> 1: + o> \n + sending listkeys command + i> write(9) -> None: + i> listkeys\n + i> write(12) -> None: + i> namespace 9\n + i> write(9) -> None: bookmarks + i> flush() -> None + o> bufferedreadline() -> 3: + o> 93\n + o> bufferedread(93) -> 93: + o> bookA 68986213bd4485ea51533535e3fc9e78007a711f\n + o> bookB 1880f3755e2e52e3199e0ee5638128b08642f34d + response: bookA 68986213bd4485ea51533535e3fc9e78007a711f\nbookB 1880f3755e2e52e3199e0ee5638128b08642f34d + + $ cd .. + +Test listkeys for phases + + $ hg init phasesrepo + $ cd phasesrepo + +Phases on empty repo + + $ debugwireproto << EOF + > command listkeys + > namespace phases + > EOF + testing ssh1 + creating ssh peer from handshake results + i> write(104) -> None: + i> hello\n + i> between\n + i> pairs 81\n + i> 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 + i> flush() -> None + o> readline() -> 4: + o> 384\n + o> readline() -> 384: + o> capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN\n + o> readline() -> 2: + o> 1\n + o> readline() -> 1: + o> \n + sending listkeys command + i> write(9) -> None: + i> listkeys\n + i> write(12) -> None: + i> namespace 6\n + i> write(6) -> None: phases + i> flush() -> None + o> bufferedreadline() -> 3: + o> 15\n + o> bufferedread(15) -> 15: publishing True + response: publishing True + + testing ssh2 + creating ssh peer from handshake results + i> write(171) -> None: + i> upgrade * proto=exp-ssh-v2-0001\n (glob) + i> hello\n + i> between\n + i> pairs 81\n + i> 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 + i> flush() -> None + o> readline() -> 62: + o> upgraded * exp-ssh-v2-0001\n (glob) + o> readline() -> 4: + o> 383\n + o> read(383) -> 383: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + o> read(1) -> 1: + o> \n + sending listkeys command + i> write(9) -> None: + i> listkeys\n + i> write(12) -> None: + i> namespace 6\n + i> write(6) -> None: phases + i> flush() -> None + o> bufferedreadline() -> 3: + o> 15\n + o> bufferedread(15) -> 15: publishing True + response: publishing True + +Create some commits + + $ echo 0 > foo + $ hg add foo + $ hg -q commit -m initial + $ hg phase --public + $ echo 1 > foo + $ hg commit -m 'head 1 commit 1' + $ echo 2 > foo + $ hg commit -m 'head 1 commit 2' + $ hg -q up 0 + $ echo 1a > foo + $ hg commit -m 'head 2 commit 1' + created new head + $ echo 2a > foo + $ hg commit -m 'head 2 commit 2' + +Two draft heads + + $ debugwireproto << EOF + > command listkeys + > namespace phases + > EOF + testing ssh1 + creating ssh peer from handshake results + i> write(104) -> None: + i> hello\n + i> between\n + i> pairs 81\n + i> 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 + i> flush() -> None + o> readline() -> 4: + o> 384\n + o> readline() -> 384: + o> capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN\n + o> readline() -> 2: + o> 1\n + o> readline() -> 1: + o> \n + sending listkeys command + i> write(9) -> None: + i> listkeys\n + i> write(12) -> None: + i> namespace 6\n + i> write(6) -> None: phases + i> flush() -> None + o> bufferedreadline() -> 4: + o> 101\n + o> bufferedread(101) -> 101: + o> 20b8a89289d80036e6c4e87c2083e3bea1586637 1\n + o> c4750011d906c18ea2f0527419cbc1a544435150 1\n + o> publishing True + response: 20b8a89289d80036e6c4e87c2083e3bea1586637 1\nc4750011d906c18ea2f0527419cbc1a544435150 1\npublishing True + + testing ssh2 + creating ssh peer from handshake results + i> write(171) -> None: + i> upgrade * proto=exp-ssh-v2-0001\n (glob) + i> hello\n + i> between\n + i> pairs 81\n + i> 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 + i> flush() -> None + o> readline() -> 62: + o> upgraded * exp-ssh-v2-0001\n (glob) + o> readline() -> 4: + o> 383\n + o> read(383) -> 383: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + o> read(1) -> 1: + o> \n + sending listkeys command + i> write(9) -> None: + i> listkeys\n + i> write(12) -> None: + i> namespace 6\n + i> write(6) -> None: phases + i> flush() -> None + o> bufferedreadline() -> 4: + o> 101\n + o> bufferedread(101) -> 101: + o> 20b8a89289d80036e6c4e87c2083e3bea1586637 1\n + o> c4750011d906c18ea2f0527419cbc1a544435150 1\n + o> publishing True + response: 20b8a89289d80036e6c4e87c2083e3bea1586637 1\nc4750011d906c18ea2f0527419cbc1a544435150 1\npublishing True + +Single draft head + + $ hg phase --public -r 2 + $ debugwireproto << EOF + > command listkeys + > namespace phases + > EOF + testing ssh1 + creating ssh peer from handshake results + i> write(104) -> None: + i> hello\n + i> between\n + i> pairs 81\n + i> 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 + i> flush() -> None + o> readline() -> 4: + o> 384\n + o> readline() -> 384: + o> capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN\n + o> readline() -> 2: + o> 1\n + o> readline() -> 1: + o> \n + sending listkeys command + i> write(9) -> None: + i> listkeys\n + i> write(12) -> None: + i> namespace 6\n + i> write(6) -> None: phases + i> flush() -> None + o> bufferedreadline() -> 3: + o> 58\n + o> bufferedread(58) -> 58: + o> c4750011d906c18ea2f0527419cbc1a544435150 1\n + o> publishing True + response: c4750011d906c18ea2f0527419cbc1a544435150 1\npublishing True + + testing ssh2 + creating ssh peer from handshake results + i> write(171) -> None: + i> upgrade * proto=exp-ssh-v2-0001\n (glob) + i> hello\n + i> between\n + i> pairs 81\n + i> 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 + i> flush() -> None + o> readline() -> 62: + o> upgraded * exp-ssh-v2-0001\n (glob) + o> readline() -> 4: + o> 383\n + o> read(383) -> 383: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + o> read(1) -> 1: + o> \n + sending listkeys command + i> write(9) -> None: + i> listkeys\n + i> write(12) -> None: + i> namespace 6\n + i> write(6) -> None: phases + i> flush() -> None + o> bufferedreadline() -> 3: + o> 58\n + o> bufferedread(58) -> 58: + o> c4750011d906c18ea2f0527419cbc1a544435150 1\n + o> publishing True + response: c4750011d906c18ea2f0527419cbc1a544435150 1\npublishing True + +All public heads + + $ hg phase --public -r 4 + $ debugwireproto << EOF + > command listkeys + > namespace phases + > EOF + testing ssh1 + creating ssh peer from handshake results + i> write(104) -> None: + i> hello\n + i> between\n + i> pairs 81\n + i> 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 + i> flush() -> None + o> readline() -> 4: + o> 384\n + o> readline() -> 384: + o> capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN\n + o> readline() -> 2: + o> 1\n + o> readline() -> 1: + o> \n + sending listkeys command + i> write(9) -> None: + i> listkeys\n + i> write(12) -> None: + i> namespace 6\n + i> write(6) -> None: phases + i> flush() -> None + o> bufferedreadline() -> 3: + o> 15\n + o> bufferedread(15) -> 15: publishing True + response: publishing True + + testing ssh2 + creating ssh peer from handshake results + i> write(171) -> None: + i> upgrade * proto=exp-ssh-v2-0001\n (glob) + i> hello\n + i> between\n + i> pairs 81\n + i> 0000000000000000000000000000000000000000-0000000000000000000000000000000000000000 + i> flush() -> None + o> readline() -> 62: + o> upgraded * exp-ssh-v2-0001\n (glob) + o> readline() -> 4: + o> 383\n + o> read(383) -> 383: capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch streamreqs=generaldelta,revlogv1 $USUAL_BUNDLE2_CAPS_SERVER$ unbundle=HG10GZ,HG10BZ,HG10UN + o> read(1) -> 1: + o> \n + sending listkeys command + i> write(9) -> None: + i> listkeys\n + i> write(12) -> None: + i> namespace 6\n + i> write(6) -> None: phases + i> flush() -> None + o> bufferedreadline() -> 3: + o> 15\n + o> bufferedread(15) -> 15: publishing True + response: publishing True