]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/test/check_magicfolder_smoke.py
smoketest for magic-folder functionality
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / test / check_magicfolder_smoke.py
1 #!/usr/bin/env python
2
3 # this is a smoke-test using "./bin/tahoe" to:
4 #
5 # 1. create an introducer
6 # 2. create 5 storage nodes
7 # 3. create 2 client nodes (alice, bob)
8 # 4. Alice creates a magic-folder ("magik:")
9 # 5. Alice invites Bob
10 # 6. Bob joins
11 #
12 # After that, some basic tests are performed; see the "if True:"
13 # blocks to turn some on or off. Could benefit from some cleanups
14 # etc. but this seems useful out of the gate for quick testing.
15 #
16 # TO RUN:
17 # from top-level of your checkout (we use "./bin/tahoe"):
18 # python src/allmydata/test/check_magicfolder_smoke.py
19 #
20 # This will create "./smoke_magicfolder" (which is disposable) and
21 # contains all the Tahoe basedirs for the introducer, storage nodes,
22 # clients, and the clients' magic-folders. NOTE that if these
23 # directories already exist they will NOT be re-created. So kill the
24 # grid and then "rm -rf smoke_magicfolder" if you want to re-run the
25 # tests cleanly.
26 #
27 # Run the script with a single arg, "kill" to run "tahoe stop" on all
28 # the nodes.
29 #
30 # This will have "tahoe start" -ed all the nodes, so you can continue
31 # to play around after the script exits.
32
33 from __future__ import print_function
34
35 import sys
36 import time
37 import shutil
38 import subprocess
39 from os.path import join, abspath, curdir, exists
40 from os import mkdir, listdir, unlink
41
42 tahoe_base = abspath(curdir)
43 data_base = join(tahoe_base, 'smoke_magicfolder')
44 tahoe_bin = join(tahoe_base, 'bin', 'tahoe')
45
46 if not exists(data_base):
47     print("Creating", data_base)
48     mkdir(data_base)
49
50 if not exists(tahoe_bin):
51     raise RuntimeError("Can't find 'tahoe' binary at '{}'".format(tahoe_bin))
52
53 if 'kill' in sys.argv:
54     print("Killing the grid")
55     for d in listdir(data_base):
56         print("killing", d)
57         subprocess.call(
58             [
59                 tahoe_bin, 'stop', join(data_base, d),
60             ]
61         )
62     sys.exit(0)
63
64 if not exists(join(data_base, 'introducer')):
65     subprocess.check_call(
66         [
67             tahoe_bin, 'create-introducer', join(data_base, 'introducer'),
68         ]
69     )
70 with open(join(data_base, 'introducer', 'tahoe.cfg'), 'w') as f:
71     f.write('''
72 [node]
73 nickname = introducer0
74 web.port = 4560
75 ''')
76
77 subprocess.check_call(
78     [
79         tahoe_bin, 'start', join(data_base, 'introducer'),
80     ]
81 )
82
83 furl_fname = join(data_base, 'introducer', 'private', 'introducer.furl')
84 while not exists(furl_fname):
85     time.sleep(1)
86 furl = open(furl_fname, 'r').read()
87 print("FURL", furl)
88
89 for x in range(5):
90     data_dir = join(data_base, 'node%d' % x)
91     if not exists(data_dir):
92         subprocess.check_call(
93             [
94                 tahoe_bin, 'create-node',
95                 '--nickname', 'node{}'.format(x),
96                 '--introducer', furl,
97                 data_dir,
98             ]
99         )
100         with open(join(data_dir, 'tahoe.cfg'), 'w') as f:
101             f.write('''
102 [node]
103 nickname = node{node_id}
104 web.port =
105 web.static = public_html
106 tub.location = localhost:{tub_port}
107
108 [client]
109 # Which services should this client connect to?
110 introducer.furl = {furl}
111 shares.needed = 2
112 shares.happy = 3
113 shares.total = 4
114 '''.format(node_id=x, furl=furl, tub_port=(9900 + x)))
115     subprocess.check_call(
116         [
117             tahoe_bin, 'start', data_dir,
118         ]
119     )
120
121
122
123 # alice and bob clients
124 do_invites = False
125 node_id = 0
126 for name in ['alice', 'bob']:
127     data_dir = join(data_base, name)
128     magic_dir = join(data_base, '{}-magic'.format(name))
129     mkdir(magic_dir)
130     if not exists(data_dir):
131         do_invites = True
132         subprocess.check_call(
133             [
134                 tahoe_bin, 'create-node',
135                 '--no-storage',
136                 '--nickname', name,
137                 '--introducer', furl,
138                 data_dir,
139             ]
140         )
141         with open(join(data_dir, 'tahoe.cfg'), 'w') as f:
142             f.write('''
143 [node]
144 nickname = {name}
145 web.port = tcp:998{node_id}:interface=localhost
146 web.static = public_html
147
148 [client]
149 # Which services should this client connect to?
150 introducer.furl = {furl}
151 shares.needed = 2
152 shares.happy = 3
153 shares.total = 4
154 '''.format(name=name, node_id=node_id, furl=furl, magic_dir=magic_dir))
155     subprocess.check_call(
156         [
157             tahoe_bin, 'start', data_dir,
158         ]
159     )
160     node_id += 1
161
162 # okay, now we have alice + bob (alice, bob)
163 # now we have alice create a magic-folder, and invite bob to it
164
165 if do_invites:
166     data_dir = join(data_base, 'alice')
167     # alice creates her folder, invites bob
168     print("Alice creates a magic-folder")
169     subprocess.check_call(
170         [
171             tahoe_bin, 'magic-folder', 'create', '--basedir', data_dir, 'magik:', 'alice',
172             join(data_base, 'alice-magic'),
173         ]
174     )
175     print("Alice invites Bob")
176     invite = subprocess.check_output(
177         [
178             tahoe_bin, 'magic-folder', 'invite', '--basedir', data_dir, 'magik:', 'bob',
179         ]
180     )
181     print("  invite:", invite)
182
183     # now we let "bob"/bob join
184     print("Bob joins Alice's magic folder")
185     data_dir = join(data_base, 'bob')
186     subprocess.check_call(
187         [
188             tahoe_bin, 'magic-folder', 'join', '--basedir', data_dir, invite,
189             join(data_base, 'bob-magic'),
190         ]
191     )
192     print("Bob has joined.")
193
194     print("Restarting alice + bob clients")
195     subprocess.check_call(
196         [
197             tahoe_bin, 'restart', '--basedir', join(data_base, 'alice'),
198         ]
199     )
200     subprocess.check_call(
201         [
202             tahoe_bin, 'restart', '--basedir', join(data_base, 'bob'),
203         ]
204     )
205
206 if True:
207     for name in ['alice', 'bob']:
208         with open(join(data_base, name, 'private', 'magic_folder_dircap'), 'r') as f:
209             print("dircap {}: {}".format(name, f.read().strip()))
210
211 # give storage nodes a chance to connect properly? I'm not entirely
212 # sure what's up here, but I get "UnrecoverableFileError" on the
213 # first_file upload from Alice "very often" otherwise
214 print("waiting 3 seconds")
215 time.sleep(3)
216
217 if True:
218     # alice writes a file; bob should get it
219     alice_foo = join(data_base, 'alice-magic', 'first_file')
220     bob_foo = join(data_base, 'bob-magic', 'first_file')
221     with open(alice_foo, 'w') as f:
222         f.write("line one\n")
223
224     print("Waiting for:", bob_foo)
225     while True:
226         if exists(bob_foo):
227             print("  found", bob_foo)
228             with open(bob_foo, 'r') as f:
229                 if f.read() == "line one\n":
230                     break
231                 print("  file contents still mismatched")
232         time.sleep(1)
233
234 if True:
235     # bob writes a file; alice should get it
236     alice_bar = join(data_base, 'alice-magic', 'second_file')
237     bob_bar = join(data_base, 'bob-magic', 'second_file')
238     with open(bob_bar, 'w') as f:
239         f.write("line one\n")
240
241     print("Waiting for:", alice_bar)
242     while True:
243         if exists(bob_bar):
244             print("  found", bob_bar)
245             with open(bob_bar, 'r') as f:
246                 if f.read() == "line one\n":
247                     break
248                 print("  file contents still mismatched")
249         time.sleep(1)
250
251 if True:
252     # alice deletes 'first_file'
253     alice_foo = join(data_base, 'alice-magic', 'first_file')
254     bob_foo = join(data_base, 'bob-magic', 'first_file')
255     unlink(alice_foo)
256
257     print("Waiting for '%s' to disappear" % (bob_foo,))
258     while True:
259         if not exists(bob_foo):
260             print("  disappeared", bob_foo)
261             break
262         time.sleep(1)
263
264     # XXX this doesn't work; shouldn't a .tmp file appear on bob's side?
265     bob_tmp = bob_foo + '.tmp'
266     print("Waiting for '%s' to appear" % (bob_tmp,))
267     while True:
268         if exists(bob_tmp):
269             print("  appeared", bob_tmp)
270             break
271         time.sleep(1)
272
273 if True:
274     # bob writes new content to 'second_file'; alice should get it
275     # get it.
276     alice_foo = join(data_base, 'alice-magic', 'second_file')
277     bob_foo = join(data_base, 'bob-magic', 'second_file')
278     gold_content = "line one\nsecond line\n"
279
280     with open(bob_foo, 'w') as f:
281         f.write(gold_content)
282
283     print("Waiting for:", alice_foo)
284     while True:
285         if exists(alice_foo):
286             print("  found", alice_foo)
287             with open(alice_foo, 'r') as f:
288                 content = f.read()
289                 if content == gold_content:
290                     break
291                 print("  file contents still mismatched:\n")
292                 print(content)
293         time.sleep(1)
294
295 if True:
296     # bob creates a sub-directory and adds a file to it
297     alice_dir = join(data_base, 'alice-magic', 'subdir')
298     bob_dir = join(data_base, 'alice-magic', 'subdir')
299     gold_content = 'a file in a subdirectory\n'
300
301     mkdir(bob_dir)
302     with open(join(bob_dir, 'subfile'), 'w') as f:
303         f.write(gold_content)
304
305     print("Waiting for Bob's subdir '%s' to appear" % (bob_dir,))
306     while True:
307         if exists(bob_dir):
308             print("  found subdir")
309             if exists(join(bob_dir, 'subfile')):
310                 print("  found file")
311                 with open(join(bob_dir, 'subfile'), 'r') as f:
312                     if f.read() == gold_content:
313                         print("  contents match")
314                         break
315         time.sleep(0.1)
316
317 if True:
318     # bob deletes the whole subdir
319     alice_dir = join(data_base, 'alice-magic', 'subdir')
320     bob_dir = join(data_base, 'alice-magic', 'subdir')
321     shutil.rmtree(bob_dir)
322
323     print("Waiting for Alice's subdir '%s' to disappear" % (alice_dir,))
324     while True:
325         if not exists(alice_dir):
326             print("  it's gone")
327             break
328         time.sleep(0.1)
329
330 # XXX restore the file not working (but, unit-tests work; what's wrong with them?)
331 # NOTE: only not-works if it's alice restoring the file!
332 if True:
333     # restore 'first_file' but with different contents
334     print("re-writing 'first_file'")
335     assert not exists(join(data_base, 'bob-magic', 'first_file'))
336     assert not exists(join(data_base, 'alice-magic', 'first_file'))
337     alice_foo = join(data_base, 'alice-magic', 'first_file')
338     bob_foo = join(data_base, 'bob-magic', 'first_file')
339     if True:
340         # if we don't swap around, it works fine
341         alice_foo, bob_foo = bob_foo, alice_foo
342     gold_content = "see it again for the first time\n"
343
344     with open(bob_foo, 'w') as f:
345         f.write(gold_content)
346
347     print("Waiting for:", alice_foo)
348     while True:
349         if exists(alice_foo):
350             print("  found", alice_foo)
351             with open(alice_foo, 'r') as f:
352                 content = f.read()
353                 if content == gold_content:
354                     break
355                 print("  file contents still mismatched: %d bytes:\n" % (len(content),))
356                 print(content)
357         else:
358             print("   {} not there yet".format(alice_foo))
359         time.sleep(1)
360
361 # XXX test .backup (delete a file)
362
363 # port david's clock.advance stuff
364 # fix clock.advance()
365 # subdirectory
366 # file deletes
367 # conflicts