synapse 2.213.0__py311-none-any.whl → 2.215.0__py311-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of synapse might be problematic. Click here for more details.

Files changed (76) hide show
  1. synapse/cortex.py +37 -6
  2. synapse/daemon.py +6 -6
  3. synapse/exc.py +13 -1
  4. synapse/lib/aha.py +5 -0
  5. synapse/lib/ast.py +2 -6
  6. synapse/lib/boss.py +47 -2
  7. synapse/lib/cell.py +193 -3
  8. synapse/lib/certdir.py +44 -1
  9. synapse/lib/cmd.py +24 -0
  10. synapse/lib/coro.py +8 -2
  11. synapse/lib/drive.py +7 -2
  12. synapse/lib/jsonstor.py +4 -1
  13. synapse/lib/layer.py +3 -1
  14. synapse/lib/link.py +11 -3
  15. synapse/lib/schemas.py +1 -1
  16. synapse/lib/snap.py +76 -65
  17. synapse/lib/storm.py +2 -1
  18. synapse/lib/stormlib/imap.py +3 -2
  19. synapse/lib/stormlib/spooled.py +1 -0
  20. synapse/lib/task.py +1 -0
  21. synapse/lib/version.py +2 -2
  22. synapse/models/inet.py +5 -0
  23. synapse/models/infotech.py +45 -0
  24. synapse/tests/files/testpkg_build_docs/docs/bar.rst +15 -0
  25. synapse/tests/files/testpkg_build_docs/docs/foo.rst +4 -0
  26. synapse/tests/files/testpkg_build_docs/storm/commands/testcmd.storm +0 -0
  27. synapse/tests/files/testpkg_build_docs/storm/modules/apimod.storm +0 -0
  28. synapse/tests/files/testpkg_build_docs/storm/modules/testmod.storm +0 -0
  29. synapse/tests/files/testpkg_build_docs/storm/testcmd.storm +5 -0
  30. synapse/tests/files/testpkg_build_docs/testpkg.yaml +69 -0
  31. synapse/tests/test_cortex.py +20 -1
  32. synapse/tests/test_daemon.py +1 -1
  33. synapse/tests/test_exc.py +6 -0
  34. synapse/tests/test_lib_ast.py +69 -14
  35. synapse/tests/test_lib_boss.py +8 -0
  36. synapse/tests/test_lib_cell.py +104 -5
  37. synapse/tests/test_lib_certdir.py +8 -0
  38. synapse/tests/test_lib_coro.py +5 -0
  39. synapse/tests/test_lib_httpapi.py +10 -2
  40. synapse/tests/test_lib_jsonstor.py +45 -0
  41. synapse/tests/test_lib_layer.py +10 -0
  42. synapse/tests/test_lib_link.py +1 -1
  43. synapse/tests/test_lib_storm.py +121 -1
  44. synapse/tests/test_lib_stormlib_iters.py +1 -1
  45. synapse/tests/test_lib_stormlib_spooled.py +20 -0
  46. synapse/tests/test_lib_stormtypes.py +15 -0
  47. synapse/tests/test_lib_types.py +5 -1
  48. synapse/tests/test_model_inet.py +7 -0
  49. synapse/tests/test_model_infotech.py +31 -0
  50. synapse/tests/test_telepath.py +32 -5
  51. synapse/tests/test_tools_axon.py +304 -0
  52. synapse/tests/test_tools_cortex_layer.py +419 -0
  53. synapse/tests/test_tools_demote.py +114 -0
  54. synapse/tests/test_tools_pkgs_gendocs.py +100 -0
  55. synapse/tests/test_tools_shutdown.py +95 -0
  56. synapse/tests/test_utils.py +22 -1
  57. synapse/tests/utils.py +44 -29
  58. synapse/tools/aha/easycert.py +2 -0
  59. synapse/tools/aha/enroll.py +3 -0
  60. synapse/tools/axon/__init__.py +0 -0
  61. synapse/tools/axon/dump.py +155 -0
  62. synapse/tools/axon/load.py +89 -0
  63. synapse/tools/cortex/__init__.py +0 -0
  64. synapse/tools/cortex/layer/__init__.py +0 -0
  65. synapse/tools/cortex/layer/dump.py +184 -0
  66. synapse/tools/cortex/layer/load.py +129 -0
  67. synapse/tools/demote.py +52 -0
  68. synapse/tools/healthcheck.py +1 -1
  69. synapse/tools/pkgs/gendocs.py +176 -0
  70. synapse/tools/pkgs/pandoc_filter.py +79 -0
  71. synapse/tools/shutdown.py +52 -0
  72. {synapse-2.213.0.dist-info → synapse-2.215.0.dist-info}/METADATA +1 -1
  73. {synapse-2.213.0.dist-info → synapse-2.215.0.dist-info}/RECORD +76 -53
  74. {synapse-2.213.0.dist-info → synapse-2.215.0.dist-info}/WHEEL +0 -0
  75. {synapse-2.213.0.dist-info → synapse-2.215.0.dist-info}/licenses/LICENSE +0 -0
  76. {synapse-2.213.0.dist-info → synapse-2.215.0.dist-info}/top_level.txt +0 -0
@@ -2999,6 +2999,21 @@ class StormTypesTest(s_test.SynTest):
2999
2999
  self.eq((5, 'baz'), await core.callStorm('return($lib.queue.get(poptest).pop())'))
3000
3000
  self.none(await core.callStorm('return($lib.queue.get(poptest).pop())'))
3001
3001
 
3002
+ # Coverage for the Cortex queue:del nexus handler
3003
+ name = 'deleteme'
3004
+ iden = s_common.guid()
3005
+ await core.addCoreQueue(
3006
+ name,
3007
+ {'name': name,
3008
+ 'creator': core.auth.rootuser.iden,
3009
+ 'created': s_common.now(),
3010
+ 'iden': iden}
3011
+ )
3012
+ await core.auth.delAuthGate(f'queue:{name}')
3013
+ await core.delCoreQueue(name)
3014
+ with self.raises(s_exc.NoSuchName):
3015
+ await core.getCoreQueue(name)
3016
+
3002
3017
  async def test_storm_node_data(self):
3003
3018
 
3004
3019
  async with self.getTestCore() as core:
@@ -1,5 +1,6 @@
1
1
  import math
2
2
  import decimal
3
+ import datetime
3
4
  import synapse.exc as s_exc
4
5
  import synapse.common as s_common
5
6
  import synapse.datamodel as s_datamodel
@@ -1320,7 +1321,10 @@ class TypesTest(s_t_utils.SynTest):
1320
1321
  nodes = await core.nodes('test:str:tick=201401*')
1321
1322
  self.eq({node.ndef[1] for node in nodes}, {'a'})
1322
1323
 
1323
- nodes = await core.nodes('test:str:tick*range=("-4200 days", now)')
1324
+ utc = datetime.timezone.utc
1325
+ delta = datetime.datetime.now(tz=utc) - datetime.datetime(2014, 1, 1, tzinfo=utc)
1326
+ days = delta.days + 14
1327
+ nodes = await core.nodes(f'test:str:tick*range=("-{days} days", now)')
1324
1328
  self.eq({node.ndef[1] for node in nodes}, {'a', 'b', 'c', 'd'})
1325
1329
 
1326
1330
  opts = {'vars': {'tick': tick, 'tock': tock}}
@@ -3002,17 +3002,20 @@ class InetModelTest(s_t_utils.SynTest):
3002
3002
  :url="https://v.vtx.lk/slack"
3003
3003
  :name="Synapse users slack"
3004
3004
  :tenant={[ inet:service:tenant=({"id": "VS-31337"}) ]}
3005
+ :app={[ inet:service:app=({"id": "app00"}) ]}
3005
3006
  ]
3006
3007
  '''
3007
3008
  nodes = await core.nodes(q)
3008
3009
  self.len(1, nodes)
3009
3010
  self.nn(nodes[0].get('tenant'))
3011
+ self.nn(nodes[0].get('app'))
3010
3012
  self.eq(nodes[0].ndef, ('inet:service:instance', s_common.guid(('vertex', 'slack'))))
3011
3013
  self.eq(nodes[0].get('id'), 'T2XK1223Y')
3012
3014
  self.eq(nodes[0].get('platform'), platform.ndef[1])
3013
3015
  self.eq(nodes[0].get('url'), 'https://v.vtx.lk/slack')
3014
3016
  self.eq(nodes[0].get('name'), 'synapse users slack')
3015
3017
  platinst = nodes[0]
3018
+ app00 = nodes[0].get('app')
3016
3019
 
3017
3020
  q = '''
3018
3021
  [
@@ -3023,6 +3026,7 @@ class InetModelTest(s_t_utils.SynTest):
3023
3026
  :email=blackout@vertex.link
3024
3027
  :profile={ gen.ps.contact.email vertex.employee blackout@vertex.link }
3025
3028
  :tenant={[ inet:service:tenant=({"id": "VS-31337"}) ]}
3029
+ :app={[ inet:service:app=({"id": "a001"}) ]}
3026
3030
  )
3027
3031
 
3028
3032
  (inet:service:account=(visi, account, vertex, slack)
@@ -3037,6 +3041,7 @@ class InetModelTest(s_t_utils.SynTest):
3037
3041
  self.len(2, accounts)
3038
3042
 
3039
3043
  self.nn(accounts[0].get('tenant'))
3044
+ self.nn(accounts[0].get('app'))
3040
3045
 
3041
3046
  profiles = await core.nodes('ps:contact')
3042
3047
  self.len(2, profiles)
@@ -3175,6 +3180,7 @@ class InetModelTest(s_t_utils.SynTest):
3175
3180
  :platform=$platiden
3176
3181
  :instance=$instiden
3177
3182
  :topic=' My Topic '
3183
+ :app={ inet:service:app:id=app00 }
3178
3184
  ]
3179
3185
  '''
3180
3186
  opts = {'vars': {
@@ -3185,6 +3191,7 @@ class InetModelTest(s_t_utils.SynTest):
3185
3191
  nodes = await core.nodes(q, opts=opts)
3186
3192
  self.len(1, nodes)
3187
3193
  self.eq(nodes[0].ndef, ('inet:service:channel', s_common.guid(('general', 'channel', 'vertex', 'slack'))))
3194
+ self.eq(nodes[0].get('app'), app00)
3188
3195
  self.eq(nodes[0].get('name'), 'general')
3189
3196
  self.eq(nodes[0].get('topic'), 'my topic')
3190
3197
  self.eq(nodes[0].get('period'), (1420070400000, 9223372036854775807))
@@ -2438,3 +2438,34 @@ class InfotechModelTest(s_t_utils.SynTest):
2438
2438
  self.eq(2, nodes[0].get('assets:vulns:preexisting'))
2439
2439
 
2440
2440
  self.len(1, await core.nodes('it:sec:metrics -> ou:org +:name=vertex'))
2441
+
2442
+ async def test_infotech_windows(self):
2443
+
2444
+ async with self.getTestCore() as core:
2445
+
2446
+ nodes = await core.nodes('''
2447
+ [ it:os:windows:service=*
2448
+ :name=Woot
2449
+ :host=*
2450
+ :type=(0x20)
2451
+ :start=(0x20)
2452
+ :errorcontrol=(0x20)
2453
+ :displayname="Foo Bar Baz"
2454
+ :imagepath=c:/windows/system32/woot.exe
2455
+ :description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
2456
+ ]
2457
+ ''')
2458
+
2459
+ self.len(1, nodes)
2460
+ self.eq(nodes[0].get('name'), 'woot')
2461
+ self.eq(nodes[0].get('type'), 0x20)
2462
+ self.eq(nodes[0].get('start'), 0x20)
2463
+ self.eq(nodes[0].get('errorcontrol'), 0x20)
2464
+ self.eq(nodes[0].get('displayname'), 'foo bar baz')
2465
+ self.eq(nodes[0].get('imagepath'), 'c:/windows/system32/woot.exe')
2466
+ self.eq(nodes[0].get('description'), 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.')
2467
+
2468
+ self.len(1, await core.nodes('it:os:windows:service -> it:host'))
2469
+ self.len(1, await core.nodes('it:os:windows:service -> file:path'))
2470
+
2471
+ self.len(1, await core.nodes('[ it:exec:proc=* :windows:service={ it:os:windows:service } ] -> it:os:windows:service'))
@@ -110,7 +110,7 @@ class Foo:
110
110
 
111
111
  def raze(self):
112
112
  # test that SynErr makes it through
113
- raise s_exc.NoSuchMeth(name='haha')
113
+ raise s_exc.SynErr(mesg='hehe', key='valu')
114
114
 
115
115
  async def corovalu(self, x, y):
116
116
  return x * 2 + y
@@ -305,11 +305,24 @@ class TeleTest(s_t_utils.SynTest):
305
305
  genr = prox.agenrboom()
306
306
  await self.asyncraises(s_exc.SynErr, genr.list())
307
307
 
308
- await self.asyncraises(s_exc.NoSuchMeth, prox.raze())
308
+ with self.raises(s_exc.SynErr) as cm:
309
+ await prox.raze()
310
+ self.eq(cm.exception.get('mesg'), 'hehe')
311
+ self.eq(cm.exception.get('key'), 'valu')
309
312
 
310
- await self.asyncraises(s_exc.NoSuchMeth, prox.fake())
313
+ with self.raises(s_exc.NoSuchMeth) as cm:
314
+ await prox.fake()
315
+ self.eq(cm.exception.get('mesg'), 'Foo has no method: fake.')
316
+ self.eq(cm.exception.get('name'), 'fake')
311
317
 
312
- await self.asyncraises(s_exc.SynErr, prox.boom())
318
+ with self.raises(s_exc.NoSuchMeth) as cm:
319
+ await prox._fake()
320
+ self.eq(cm.exception.get('mesg'), 'Foo has no method: _fake.')
321
+ self.eq(cm.exception.get('name'), '_fake')
322
+
323
+ with self.raises(s_exc.NotMsgpackSafe) as cm:
324
+ await prox.boom()
325
+ self.isin("can not serialize 'Boom' object", cm.exception.get('mesg'))
313
326
 
314
327
  # Fini'ing a daemon fini's proxies connected to it.
315
328
  self.true(await s_coro.event_wait(evt, 2))
@@ -395,10 +408,12 @@ class TeleTest(s_t_utils.SynTest):
395
408
  genr = prox.corogenr(3)
396
409
  self.eq((0, 1, 2), await s_t_utils.alist(genr))
397
410
 
398
- await self.asyncraises(s_exc.NoSuchMeth, prox.raze())
411
+ await self.asyncraises(s_exc.SynErr, prox.raze())
399
412
 
400
413
  await self.asyncraises(s_exc.NoSuchMeth, prox.fake())
401
414
 
415
+ await self.asyncraises(s_exc.NoSuchMeth, prox._fake())
416
+
402
417
  await self.asyncraises(s_exc.SynErr, prox.boom())
403
418
 
404
419
  # Fini'ing a daemon fini's proxies connected to it.
@@ -834,6 +849,18 @@ class TeleTest(s_t_utils.SynTest):
834
849
  {'family': 'unix',
835
850
  'addr': sockpath})
836
851
 
852
+ async def test_url_bad_cell_path(self):
853
+ with self.getTestDir() as dirn:
854
+ # Touch the sock path
855
+ s_common.genfile(dirn, 'newp', 'sock').close()
856
+ cell_url = f'cell://{dirn}/newp'
857
+ with self.raises(s_exc.LinkErr) as cm:
858
+ await s_telepath.openurl(cell_url)
859
+ self.isin('Cell path is not listening', cm.exception.get('mesg'))
860
+ with self.raises(s_exc.NoSuchPath) as cm:
861
+ await s_telepath.openurl(cell_url)
862
+ self.isin('Cell path does not exist', cm.exception.get('mesg'))
863
+
837
864
  async def test_ipv6(self):
838
865
  if s_common.envbool('CIRCLECI'):
839
866
  self.skip('ipv6 listener is not supported in circleci')
@@ -0,0 +1,304 @@
1
+ import io
2
+ import os
3
+ import tarfile
4
+
5
+ from unittest import mock
6
+
7
+ import synapse.common as s_common
8
+
9
+ import synapse.tests.utils as s_t_utils
10
+
11
+ import synapse.tools.axon.dump as axon_dump
12
+ import synapse.tools.axon.load as axon_load
13
+
14
+ class AxonToolsTest(s_t_utils.SynTest):
15
+
16
+ async def test_axon_dump_and_load_url_handling(self):
17
+ with self.getTestDir() as testdir:
18
+ argv = ['--url', 'cell:///definitelynotarealpath/axon', '--offset', '0', testdir]
19
+ outp = self.getTestOutp()
20
+ self.eq(1, await axon_dump.main(argv, outp=outp))
21
+ outp.expect('ERROR')
22
+ outp.expect('Cell path does not exist')
23
+
24
+ with self.getTestDir() as testdir:
25
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
26
+ tarfile = os.path.join(testdir, 'dummy.0-1.tar.gz')
27
+ with open(tarfile, 'wb') as fd:
28
+ fd.write(b'dummy')
29
+ url = f'cell://baduser:badpass@/{axon.dirn}'
30
+ argv = ['--url', url, tarfile]
31
+ outp = self.getTestOutp()
32
+ self.eq(1, await axon_load.main(argv, outp=outp))
33
+ outp.expect('No such user')
34
+
35
+ with self.getTestDir() as testdir:
36
+ tarfile = os.path.join(testdir, 'dummy.0-1.tar.gz')
37
+ with open(tarfile, 'wb') as fd:
38
+ fd.write(b'dummy')
39
+ argv = ['--url', 'cell:///definitelynotarealpath/axon', tarfile]
40
+ outp = self.getTestOutp()
41
+ self.eq(1, await axon_load.main(argv, outp=outp))
42
+ outp.expect('ERROR')
43
+ outp.expect('Cell path does not exist')
44
+
45
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
46
+ dumpdir = os.path.join(testdir, 'dumpdir')
47
+ os.makedirs(dumpdir)
48
+ url = f'cell://baduser:badpass@/{axon.dirn}'
49
+ argv = ['--url', url, '--offset', '0', dumpdir]
50
+ outp = self.getTestOutp()
51
+ self.eq(1, await axon_dump.main(argv, outp=outp))
52
+ outp.expect('No such user')
53
+
54
+ async def test_axon_dump_and_load(self):
55
+ with self.getTestDir() as testdir:
56
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon0')) as axon0:
57
+ blobs = [b'foo', b'bar', b'baz' * 1000]
58
+ sha2s = []
59
+ for blob in blobs:
60
+ size, sha2 = await axon0.put(blob)
61
+ sha2s.append((size, sha2, blob))
62
+ dumpdir = os.path.join(testdir, 'dumpdir')
63
+ os.makedirs(dumpdir)
64
+ argv = [
65
+ '--url', f'cell:///{axon0.dirn}',
66
+ '--offset', '0',
67
+ dumpdir,
68
+ ]
69
+ self.eq(0, await axon_dump.main(argv))
70
+ files = os.listdir(dumpdir)
71
+ self.true(files, 'No dump file created')
72
+
73
+ tarfiles = sorted([os.path.join(dumpdir, f) for f in files if f.endswith('.tar.gz')])
74
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon1')) as axon1:
75
+ argv = ['--url', f'cell:///{axon1.dirn}'] + tarfiles
76
+ self.eq(0, await axon_load.main(argv))
77
+ for size, sha2, blob in sha2s:
78
+ self.true(await axon1.has(sha2))
79
+ out = b''
80
+ async for byts in axon1.get(sha2):
81
+ out += byts
82
+ self.eq(out, blob)
83
+
84
+ async def test_dump_blob_size_mismatch(self):
85
+ with self.getTestDir() as testdir:
86
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
87
+ size, sha2 = await axon.put(b'hello world')
88
+ dumpdir = os.path.join(testdir, 'dumpdir')
89
+ os.makedirs(dumpdir)
90
+ orig_get = axon.get
91
+ async def bad_get(sha2arg, **kwargs):
92
+ if sha2arg == sha2:
93
+ yield b'hello' # corrupt it
94
+ else:
95
+ async for byts in orig_get(sha2arg):
96
+ yield byts
97
+ axon.get = bad_get
98
+ argv = ['--url', f'cell:///{axon.dirn}', '--offset', '0', dumpdir]
99
+ outp = self.getTestOutp()
100
+ self.eq(1, await axon_dump.main(argv, outp=outp))
101
+ outp.expect('Blob size mismatch')
102
+
103
+ async def test_dump_not_axon_cell(self):
104
+ with self.getTestDir() as testdir:
105
+ async with self.getTestCore() as core:
106
+ curl = core.getLocalUrl()
107
+ argv = ['--url', curl, testdir]
108
+ outp = self.getTestOutp()
109
+ self.eq(1, await axon_dump.main(argv, outp=outp))
110
+ outp.expect('only works on axon')
111
+
112
+ async def test_dump_outdir_is_not_directory(self):
113
+ with self.getTestDir() as testdir:
114
+ outpath = os.path.join(testdir, 'notadir')
115
+ with open(outpath, 'w') as fd:
116
+ fd.write('not a directory')
117
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
118
+ argv = ['--url', f'cell:///{axon.dirn}', '--offset', '0', outpath]
119
+ outp = self.getTestOutp()
120
+ self.eq(1, await axon_dump.main(argv, outp=outp))
121
+ outp.expect('exists but is not a directory')
122
+
123
+ async def test_dump_rotate_size_and_load(self):
124
+ with self.getTestDir() as testdir:
125
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
126
+ blobs = [os.urandom(1024) for _ in range(10)]
127
+ sha2s = []
128
+ for blob in blobs:
129
+ size, sha2 = await axon.put(blob)
130
+ sha2s.append((size, sha2, blob))
131
+ dumpdir = os.path.join(testdir, 'dumpdir')
132
+ os.makedirs(dumpdir)
133
+ argv = ['--url', f'cell:///{axon.dirn}', '--offset', '0', '--rotate-size', '2048', dumpdir]
134
+ outp = self.getTestOutp()
135
+ self.eq(0, await axon_dump.main(argv, outp=outp))
136
+
137
+ tarfiles = sorted([os.path.join(dumpdir, f) for f in os.listdir(dumpdir) if f.endswith('.tar.gz')])
138
+ self.true(len(tarfiles) > 1)
139
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon2')) as axon2:
140
+ outp = self.getTestOutp()
141
+ argv = ['--url', f'cell:///{axon2.dirn}'] + tarfiles
142
+ self.eq(0, await axon_load.main(argv, outp=outp))
143
+ for size, sha2, blob in sha2s:
144
+ self.true(await axon2.has(sha2))
145
+ out = b''
146
+ async for byts in axon2.get(sha2):
147
+ out += byts
148
+ self.eq(out, blob)
149
+
150
+ async def test_dump_tempfile_rollover(self):
151
+ with self.getTestDir() as testdir:
152
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
153
+ n = 128
154
+ blobs = [os.urandom(n) for n in range(n)]
155
+ sha2s = []
156
+ for blob in blobs:
157
+ size, sha2 = await axon.put(blob)
158
+ sha2s.append((size, sha2, blob))
159
+ dumpdir = os.path.join(testdir, 'dumpdir')
160
+ os.makedirs(dumpdir)
161
+ argv = ['--url', f'cell:///{axon.dirn}', dumpdir]
162
+ outp = self.getTestOutp()
163
+
164
+ # Patch the SppoledTemporaryFile rollover size while dumping files.
165
+ with mock.patch('synapse.tools.axon.dump.MAX_SPOOL_SIZE', n / 2):
166
+ self.eq(0, await axon_dump.main(argv, outp=outp))
167
+
168
+ tarfiles = sorted([os.path.join(dumpdir, f) for f in os.listdir(dumpdir) if f.endswith('.tar.gz')])
169
+ self.len(1, tarfiles)
170
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon2')) as axon2:
171
+ outp = self.getTestOutp()
172
+ argv = ['--url', f'cell:///{axon2.dirn}'] + tarfiles
173
+ self.eq(0, await axon_load.main(argv, outp=outp))
174
+ for size, sha2, blob in sha2s:
175
+ self.true(await axon2.has(sha2))
176
+ out = b''
177
+ async for byts in axon2.get(sha2):
178
+ out += byts
179
+ self.eq(out, blob)
180
+
181
+ async def test_dump_statefile(self):
182
+ with self.getTestDir() as testdir:
183
+ async with self.getTestAxon(dirn=testdir) as axon:
184
+ blob_data = b'blobdata'
185
+ await axon.put(blob_data)
186
+ outdir = os.path.join(testdir, 'out')
187
+ os.makedirs(outdir)
188
+ statefile = os.path.join(testdir, 'state.yaml')
189
+
190
+ argv = ['--url', axon.getLocalUrl(), '--statefile', statefile, outdir]
191
+ outp = self.getTestOutp()
192
+ self.eq(0, await axon_dump.main(argv, outp=outp))
193
+ self.true(os.path.isfile(statefile))
194
+ state = s_common.yamlload(statefile)
195
+ self.isin('offset:next', state)
196
+
197
+ s_common.yamlsave({'offset:next': 123}, statefile)
198
+ outp = self.getTestOutp()
199
+ self.eq(0, await axon_dump.main(argv, outp=outp))
200
+ self.true(os.path.isfile(statefile))
201
+ state = s_common.yamlload(statefile)
202
+ self.isin('offset:next', state)
203
+ self.ne(state['offset:next'], 123)
204
+
205
+ statedir = os.path.join(testdir, 'statedir')
206
+ os.makedirs(statedir)
207
+ argv = ['--url', axon.getLocalUrl(), '--statefile', statedir, outdir]
208
+ outp = self.getTestOutp()
209
+ self.eq(0, await axon_dump.main(argv, outp=outp))
210
+ cellinfo = await axon.getCellInfo()
211
+ celliden = cellinfo['cell']['iden']
212
+ statefile_path = os.path.join(statedir, f'{celliden}.yaml')
213
+ self.true(os.path.isfile(statefile_path))
214
+ state = s_common.yamlload(statefile_path)
215
+ self.isin('offset:next', state)
216
+
217
+ async def test_load_blobs_path_does_not_exist(self):
218
+ with self.getTestDir() as testdir:
219
+ missing = os.path.join(testdir, 'doesnotexist')
220
+ argv = ['--url', 'cell:///definitelynotarealpath/axon', missing]
221
+ outp = self.getTestOutp()
222
+ self.eq(1, await axon_load.main(argv, outp=outp))
223
+ outp.expect('ERROR: Cell path does not exist')
224
+
225
+ async def test_load_continue_and_invalid_blob(self):
226
+ with self.getTestDir() as testdir:
227
+ tarpath = os.path.join(testdir, 'test1.tar.gz')
228
+ with tarfile.open(tarpath, 'w:gz') as tar:
229
+ info = tarfile.TarInfo('notablob.txt')
230
+ data = b'not a blob'
231
+ info.size = len(data)
232
+ tar.addfile(info, io.BytesIO(data))
233
+ info2 = tarfile.TarInfo('invalidblobname.blob')
234
+ data2 = b'data'
235
+ info2.size = len(data2)
236
+ tar.addfile(info2, io.BytesIO(data2))
237
+
238
+ orig_uhex = s_common.uhex
239
+ def fake_uhex(val):
240
+ if val == 'invalidblobname':
241
+ raise Exception('bad hex')
242
+ return orig_uhex(val)
243
+ s_common.uhex = fake_uhex
244
+
245
+ outp = self.getTestOutp()
246
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
247
+ argv = ['--url', axon.getLocalUrl(), tarpath]
248
+ self.eq(0, await axon_load.main(argv, outp=outp))
249
+ s_common.uhex = orig_uhex
250
+ outp.expect('Skipping invalid blob filename')
251
+
252
+ async def test_load_failed_to_extract(self):
253
+ with self.getTestDir() as testdir:
254
+ tarpath = os.path.join(testdir, 'test3.tar.gz')
255
+ with tarfile.open(tarpath, 'w:gz') as tar:
256
+ info = tarfile.TarInfo('deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef.blob')
257
+ info.type = tarfile.DIRTYPE
258
+ tar.addfile(info)
259
+ outp = self.getTestOutp()
260
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
261
+ argv = ['--url', axon.getLocalUrl(), tarpath]
262
+ self.eq(0, await axon_load.main(argv, outp=outp))
263
+ outp.expect('Failed to extract')
264
+
265
+ async def test_load_not_axon_cell(self):
266
+ with self.getTestDir() as testdir:
267
+ async with self.getTestCore() as core:
268
+ curl = core.getLocalUrl()
269
+ dumpdir = os.path.join(testdir, 'dumpdir')
270
+ os.makedirs(dumpdir)
271
+ argv = ['--url', curl, dumpdir]
272
+ outp = self.getTestOutp()
273
+ self.eq(1, await axon_load.main(argv, outp=outp))
274
+ outp.expect('only works on axon')
275
+
276
+ async def test_load_oserror_extracting_blob(self):
277
+ with self.getTestDir() as testdir:
278
+ tarpath = os.path.join(testdir, 'test_oserror.tar.gz')
279
+ with tarfile.open(tarpath, 'w:gz') as tar:
280
+ info = tarfile.TarInfo('deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef.blob')
281
+ data = b'12345'
282
+ info.size = len(data)
283
+ tar.addfile(info, io.BytesIO(data))
284
+ outp = self.getTestOutp()
285
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
286
+ with mock.patch('tarfile.TarFile.extractfile', side_effect=OSError("simulated extraction error")):
287
+ argv = ['--url', axon.getLocalUrl(), tarpath]
288
+ self.eq(0, await axon_load.main(argv, outp=outp))
289
+ outp.expect('WARNING: Error extracting')
290
+
291
+ async def test_load_skipping_existing_blob(self):
292
+ with self.getTestDir() as testdir:
293
+ async with self.getTestAxon(dirn=os.path.join(testdir, 'axon')) as axon:
294
+ blob = b'foo'
295
+ size, sha2 = await axon.put(blob)
296
+ tarpath = os.path.join(testdir, 'test2.tar.gz')
297
+ with tarfile.open(tarpath, 'w:gz') as tar:
298
+ info = tarfile.TarInfo(f'{s_common.ehex(sha2)}.blob')
299
+ info.size = len(blob)
300
+ tar.addfile(info, io.BytesIO(blob))
301
+ outp = self.getTestOutp()
302
+ argv = ['--url', axon.getLocalUrl(), tarpath]
303
+ self.eq(0, await axon_load.main(argv, outp=outp))
304
+ outp.expect('Skipping existing blob')