##// END OF EJS Templates
sshrepo: be more careful while reading data...
Alexis S. L. Carvalho -
r5978:7939c71f default
parent child Browse files
Show More
@@ -1,228 +1,239 b''
1 1 # sshrepo.py - ssh repository proxy class for mercurial
2 2 #
3 3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import *
9 9 from remoterepo import *
10 10 from i18n import _
11 11 import repo, os, re, stat, util
12 12
13 13 class sshrepository(remoterepository):
14 14 def __init__(self, ui, path, create=0):
15 15 self._url = path
16 16 self.ui = ui
17 17
18 18 m = re.match(r'^ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?$', path)
19 19 if not m:
20 20 self.raise_(repo.RepoError(_("couldn't parse location %s") % path))
21 21
22 22 self.user = m.group(2)
23 23 self.host = m.group(3)
24 24 self.port = m.group(5)
25 25 self.path = m.group(7) or "."
26 26
27 27 args = self.user and ("%s@%s" % (self.user, self.host)) or self.host
28 28 args = self.port and ("%s -p %s") % (args, self.port) or args
29 29
30 30 sshcmd = self.ui.config("ui", "ssh", "ssh")
31 31 remotecmd = self.ui.config("ui", "remotecmd", "hg")
32 32
33 33 if create:
34 34 cmd = '%s %s "%s init %s"'
35 35 cmd = cmd % (sshcmd, args, remotecmd, self.path)
36 36
37 37 ui.note('running %s\n' % cmd)
38 38 res = util.system(cmd)
39 39 if res != 0:
40 40 self.raise_(repo.RepoError(_("could not create remote repo")))
41 41
42 42 self.validate_repo(ui, sshcmd, args, remotecmd)
43 43
44 44 def url(self):
45 45 return self._url
46 46
47 47 def validate_repo(self, ui, sshcmd, args, remotecmd):
48 48 # cleanup up previous run
49 49 self.cleanup()
50 50
51 51 cmd = '%s %s "%s -R %s serve --stdio"'
52 52 cmd = cmd % (sshcmd, args, remotecmd, self.path)
53 53
54 54 cmd = util.quotecommand(cmd)
55 55 ui.note('running %s\n' % cmd)
56 56 self.pipeo, self.pipei, self.pipee = os.popen3(cmd, 'b')
57 57
58 58 # skip any noise generated by remote shell
59 59 self.do_cmd("hello")
60 60 r = self.do_cmd("between", pairs=("%s-%s" % ("0"*40, "0"*40)))
61 61 lines = ["", "dummy"]
62 62 max_noise = 500
63 63 while lines[-1] and max_noise:
64 64 l = r.readline()
65 65 self.readerr()
66 66 if lines[-1] == "1\n" and l == "\n":
67 67 break
68 68 if l:
69 69 ui.debug(_("remote: "), l)
70 70 lines.append(l)
71 71 max_noise -= 1
72 72 else:
73 73 self.raise_(repo.RepoError(_("no suitable response from remote hg")))
74 74
75 75 self.capabilities = util.set()
76 76 lines.reverse()
77 77 for l in lines:
78 78 if l.startswith("capabilities:"):
79 79 self.capabilities.update(l[:-1].split(":")[1].split())
80 80 break
81 81
82 82 def readerr(self):
83 83 while 1:
84 84 size = util.fstat(self.pipee).st_size
85 85 if size == 0: break
86 86 l = self.pipee.readline()
87 87 if not l: break
88 88 self.ui.status(_("remote: "), l)
89 89
90 90 def raise_(self, exception):
91 91 self.cleanup()
92 92 raise exception
93 93
94 94 def cleanup(self):
95 95 try:
96 96 self.pipeo.close()
97 97 self.pipei.close()
98 98 # read the error descriptor until EOF
99 99 for l in self.pipee:
100 100 self.ui.status(_("remote: "), l)
101 101 self.pipee.close()
102 102 except:
103 103 pass
104 104
105 105 __del__ = cleanup
106 106
107 107 def do_cmd(self, cmd, **args):
108 108 self.ui.debug(_("sending %s command\n") % cmd)
109 109 self.pipeo.write("%s\n" % cmd)
110 110 for k, v in args.items():
111 111 self.pipeo.write("%s %d\n" % (k, len(v)))
112 112 self.pipeo.write(v)
113 113 self.pipeo.flush()
114 114
115 115 return self.pipei
116 116
117 117 def call(self, cmd, **args):
118 r = self.do_cmd(cmd, **args)
119 l = r.readline()
118 self.do_cmd(cmd, **args)
119 return self._recv()
120
121 def _recv(self):
122 l = self.pipei.readline()
120 123 self.readerr()
121 124 try:
122 125 l = int(l)
123 126 except:
124 127 self.raise_(util.UnexpectedOutput(_("unexpected response:"), l))
125 return r.read(l)
128 return self.pipei.read(l)
129
130 def _send(self, data, flush=False):
131 self.pipeo.write("%d\n" % len(data))
132 if data:
133 self.pipeo.write(data)
134 if flush:
135 self.pipeo.flush()
136 self.readerr()
126 137
127 138 def lock(self):
128 139 self.call("lock")
129 140 return remotelock(self)
130 141
131 142 def unlock(self):
132 143 self.call("unlock")
133 144
134 145 def lookup(self, key):
135 146 self.requirecap('lookup', _('look up remote revision'))
136 147 d = self.call("lookup", key=key)
137 148 success, data = d[:-1].split(" ", 1)
138 149 if int(success):
139 150 return bin(data)
140 151 else:
141 152 self.raise_(repo.RepoError(data))
142 153
143 154 def heads(self):
144 155 d = self.call("heads")
145 156 try:
146 157 return map(bin, d[:-1].split(" "))
147 158 except:
148 159 self.raise_(util.UnexpectedOutput(_("unexpected response:"), d))
149 160
150 161 def branches(self, nodes):
151 162 n = " ".join(map(hex, nodes))
152 163 d = self.call("branches", nodes=n)
153 164 try:
154 165 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
155 166 return br
156 167 except:
157 168 self.raise_(util.UnexpectedOutput(_("unexpected response:"), d))
158 169
159 170 def between(self, pairs):
160 171 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
161 172 d = self.call("between", pairs=n)
162 173 try:
163 174 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
164 175 return p
165 176 except:
166 177 self.raise_(util.UnexpectedOutput(_("unexpected response:"), d))
167 178
168 179 def changegroup(self, nodes, kind):
169 180 n = " ".join(map(hex, nodes))
170 181 return self.do_cmd("changegroup", roots=n)
171 182
172 183 def changegroupsubset(self, bases, heads, kind):
173 184 self.requirecap('changegroupsubset', _('look up remote changes'))
174 185 bases = " ".join(map(hex, bases))
175 186 heads = " ".join(map(hex, heads))
176 187 return self.do_cmd("changegroupsubset", bases=bases, heads=heads)
177 188
178 189 def unbundle(self, cg, heads, source):
179 190 d = self.call("unbundle", heads=' '.join(map(hex, heads)))
180 191 if d:
181 192 # remote may send "unsynced changes"
182 193 self.raise_(repo.RepoError(_("push refused: %s") % d))
183 194
184 195 while 1:
185 196 d = cg.read(4096)
186 if not d: break
187 self.pipeo.write(str(len(d)) + '\n')
188 self.pipeo.write(d)
189 self.readerr()
197 if not d:
198 break
199 self._send(d)
190 200
191 self.pipeo.write('0\n')
192 self.pipeo.flush()
201 self._send("", flush=True)
193 202
194 self.readerr()
195 l = int(self.pipei.readline())
196 r = self.pipei.read(l)
203 r = self._recv()
197 204 if r:
198 205 # remote may send "unsynced changes"
199 206 self.raise_(hg.RepoError(_("push failed: %s") % r))
200 207
201 self.readerr()
202 l = int(self.pipei.readline())
203 r = self.pipei.read(l)
204 return int(r)
208 r = self._recv()
209 try:
210 return int(r)
211 except:
212 self.raise_(util.UnexpectedOutput(_("unexpected response:"), r))
205 213
206 214 def addchangegroup(self, cg, source, url):
207 215 d = self.call("addchangegroup")
208 216 if d:
209 217 self.raise_(repo.RepoError(_("push refused: %s") % d))
210 218 while 1:
211 219 d = cg.read(4096)
212 if not d: break
220 if not d:
221 break
213 222 self.pipeo.write(d)
214 223 self.readerr()
215 224
216 225 self.pipeo.flush()
217 226
218 227 self.readerr()
219 l = int(self.pipei.readline())
220 r = self.pipei.read(l)
228 r = self._recv()
221 229 if not r:
222 230 return 1
223 return int(r)
231 try:
232 return int(r)
233 except:
234 self.raise_(util.UnexpectedOutput(_("unexpected response:"), r))
224 235
225 236 def stream_out(self):
226 237 return self.do_cmd('stream_out')
227 238
228 239 instance = sshrepository
@@ -1,103 +1,111 b''
1 1 #!/bin/sh
2 2
3 3 cp "$TESTDIR"/printenv.py .
4 4
5 5 # This test tries to exercise the ssh functionality with a dummy script
6 6
7 7 cat <<EOF > dummyssh
8 8 import sys
9 9 import os
10 10
11 11 os.chdir(os.path.dirname(sys.argv[0]))
12 12 if sys.argv[1] != "user@dummy":
13 13 sys.exit(-1)
14 14
15 15 if not os.path.exists("dummyssh"):
16 16 sys.exit(-1)
17 17
18 18 os.environ["SSH_CLIENT"] = "127.0.0.1 1 2"
19 19
20 20 log = open("dummylog", "ab")
21 21 log.write("Got arguments")
22 22 for i, arg in enumerate(sys.argv[1:]):
23 23 log.write(" %d:%s" % (i+1, arg))
24 24 log.write("\n")
25 25 log.close()
26 26 r = os.system(sys.argv[2])
27 27 sys.exit(bool(r))
28 28 EOF
29 29
30 cat <<EOF > badhook
31 import sys
32 sys.stdout.write("KABOOM")
33 EOF
34
30 35 echo "# creating 'remote'"
31 36 hg init remote
32 37 cd remote
33 38 echo this > foo
34 39 echo this > fooO
35 40 hg ci -A -m "init" -d "1000000 0" foo fooO
36 41 echo '[server]' > .hg/hgrc
37 42 echo 'uncompressed = True' >> .hg/hgrc
38 43 echo '[hooks]' >> .hg/hgrc
39 44 echo 'changegroup = python ../printenv.py changegroup-in-remote 0 ../dummylog' >> .hg/hgrc
40 45
41 46 cd ..
42 47
43 48 echo "# repo not found error"
44 49 hg clone -e "python ./dummyssh" ssh://user@dummy/nonexistent local
45 50
46 51 echo "# clone remote via stream"
47 52 hg clone -e "python ./dummyssh" --uncompressed ssh://user@dummy/remote local-stream 2>&1 | \
48 53 sed -e 's/[0-9][0-9.]*/XXX/g' -e 's/[KM]\(B\/sec\)/X\1/'
49 54 cd local-stream
50 55 hg verify
51 56 cd ..
52 57
53 58 echo "# clone remote via pull"
54 59 hg clone -e "python ./dummyssh" ssh://user@dummy/remote local
55 60
56 61 echo "# verify"
57 62 cd local
58 63 hg verify
59 64
60 65 echo '[hooks]' >> .hg/hgrc
61 66 echo 'changegroup = python ../printenv.py changegroup-in-local 0 ../dummylog' >> .hg/hgrc
62 67
63 68 echo "# empty default pull"
64 69 hg paths
65 70 hg pull -e "python ../dummyssh"
66 71
67 72 echo "# local change"
68 73 echo bleah > foo
69 74 hg ci -m "add" -d "1000000 0"
70 75
71 76 echo "# updating rc"
72 77 echo "default-push = ssh://user@dummy/remote" >> .hg/hgrc
73 78 echo "[ui]" >> .hg/hgrc
74 79 echo "ssh = python ../dummyssh" >> .hg/hgrc
75 80
76 81 echo "# find outgoing"
77 82 hg out ssh://user@dummy/remote
78 83
79 84 echo "# find incoming on the remote side"
80 85 hg incoming -R ../remote -e "python ../dummyssh" ssh://user@dummy/local
81 86
82 87 echo "# push"
83 88 hg push
84 89
85 90 cd ../remote
86 91
87 92 echo "# check remote tip"
88 93 hg tip
89 94 hg verify
90 95 hg cat -r tip foo
91 96
92 97 echo z > z
93 98 hg ci -A -m z -d '1000001 0' z
99 # a bad, evil hook that prints to stdout
100 echo 'changegroup.stdout = python ../badhook' >> .hg/hgrc
94 101
95 102 cd ../local
96 103 echo r > r
97 104 hg ci -A -m z -d '1000002 0' r
98 105
99 echo "# push should succeed"
106 echo "# push should succeed even though it has an unexpected response"
100 107 hg push
108 hg -R ../remote heads
101 109
102 110 cd ..
103 111 cat dummylog
@@ -1,90 +1,105 b''
1 1 # creating 'remote'
2 2 # repo not found error
3 3 remote: abort: There is no Mercurial repository here (.hg not found)!
4 4 abort: no suitable response from remote hg!
5 5 # clone remote via stream
6 6 streaming all changes
7 7 XXX files to transfer, XXX bytes of data
8 8 transferred XXX bytes in XXX seconds (XXX XB/sec)
9 9 XXX files updated, XXX files merged, XXX files removed, XXX files unresolved
10 10 checking changesets
11 11 checking manifests
12 12 crosschecking files in changesets and manifests
13 13 checking files
14 14 2 files, 1 changesets, 2 total revisions
15 15 # clone remote via pull
16 16 requesting all changes
17 17 adding changesets
18 18 adding manifests
19 19 adding file changes
20 20 added 1 changesets with 2 changes to 2 files
21 21 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
22 22 # verify
23 23 checking changesets
24 24 checking manifests
25 25 crosschecking files in changesets and manifests
26 26 checking files
27 27 2 files, 1 changesets, 2 total revisions
28 28 # empty default pull
29 29 default = ssh://user@dummy/remote
30 30 pulling from ssh://user@dummy/remote
31 31 searching for changes
32 32 no changes found
33 33 # local change
34 34 # updating rc
35 35 # find outgoing
36 36 comparing with ssh://user@dummy/remote
37 37 searching for changes
38 38 changeset: 1:572896fe480d
39 39 tag: tip
40 40 user: test
41 41 date: Mon Jan 12 13:46:40 1970 +0000
42 42 summary: add
43 43
44 44 # find incoming on the remote side
45 45 comparing with ssh://user@dummy/local
46 46 searching for changes
47 47 changeset: 1:572896fe480d
48 48 tag: tip
49 49 user: test
50 50 date: Mon Jan 12 13:46:40 1970 +0000
51 51 summary: add
52 52
53 53 # push
54 54 pushing to ssh://user@dummy/remote
55 55 searching for changes
56 56 remote: adding changesets
57 57 remote: adding manifests
58 58 remote: adding file changes
59 59 remote: added 1 changesets with 1 changes to 1 files
60 60 # check remote tip
61 61 changeset: 1:572896fe480d
62 62 tag: tip
63 63 user: test
64 64 date: Mon Jan 12 13:46:40 1970 +0000
65 65 summary: add
66 66
67 67 checking changesets
68 68 checking manifests
69 69 crosschecking files in changesets and manifests
70 70 checking files
71 71 2 files, 2 changesets, 3 total revisions
72 72 bleah
73 # push should succeed
73 # push should succeed even though it has an unexpected response
74 74 pushing to ssh://user@dummy/remote
75 75 searching for changes
76 76 note: unsynced remote changes!
77 77 remote: adding changesets
78 78 remote: adding manifests
79 79 remote: adding file changes
80 80 remote: added 1 changesets with 1 changes to 1 files
81 abort: unexpected response:
82 'KABOOM1\n'
83 changeset: 3:ac7448082955
84 tag: tip
85 parent: 1:572896fe480d
86 user: test
87 date: Mon Jan 12 13:46:42 1970 +0000
88 summary: z
89
90 changeset: 2:187c6caa0d1e
91 parent: 0:e34318c26897
92 user: test
93 date: Mon Jan 12 13:46:41 1970 +0000
94 summary: z
95
81 96 Got arguments 1:user@dummy 2:hg -R nonexistent serve --stdio
82 97 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
83 98 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
84 99 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
85 100 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
86 101 Got arguments 1:user@dummy 2:hg -R local serve --stdio
87 102 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
88 103 changegroup-in-remote hook: HG_NODE=572896fe480d7581849806ee402175c49cb20037 HG_SOURCE=serve HG_URL=remote:ssh:127.0.0.1
89 104 Got arguments 1:user@dummy 2:hg -R remote serve --stdio
90 105 changegroup-in-remote hook: HG_NODE=ac7448082955a0b2ff5cb4512c1e061c779bbc79 HG_SOURCE=serve HG_URL=remote:ssh:127.0.0.1
General Comments 0
You need to be logged in to leave comments. Login now