test_restore.py 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288
  1. import pytest
  2. from flexmock import flexmock
  3. import borgmatic.actions.restore as module
  4. @pytest.mark.parametrize(
  5. 'first_dump,second_dump,default_port,expected_result',
  6. (
  7. (
  8. module.Dump('postgresql_databases', 'foo'),
  9. module.Dump('postgresql_databases', 'foo'),
  10. None,
  11. True,
  12. ),
  13. (
  14. module.Dump('postgresql_databases', 'foo'),
  15. module.Dump('postgresql_databases', 'bar'),
  16. None,
  17. False,
  18. ),
  19. (
  20. module.Dump('postgresql_databases', 'foo'),
  21. module.Dump('mariadb_databases', 'foo'),
  22. None,
  23. False,
  24. ),
  25. (
  26. module.Dump('postgresql_databases', 'foo'),
  27. module.Dump(module.UNSPECIFIED, 'foo'),
  28. None,
  29. True,
  30. ),
  31. (
  32. module.Dump('postgresql_databases', 'foo'),
  33. module.Dump(module.UNSPECIFIED, 'bar'),
  34. None,
  35. False,
  36. ),
  37. (
  38. module.Dump('postgresql_databases', module.UNSPECIFIED),
  39. module.Dump('postgresql_databases', 'foo'),
  40. None,
  41. True,
  42. ),
  43. (
  44. module.Dump('postgresql_databases', module.UNSPECIFIED),
  45. module.Dump('mariadb_databases', 'foo'),
  46. None,
  47. False,
  48. ),
  49. (
  50. module.Dump('postgresql_databases', 'foo', 'myhost'),
  51. module.Dump('postgresql_databases', 'foo', 'myhost'),
  52. None,
  53. True,
  54. ),
  55. (
  56. module.Dump('postgresql_databases', 'foo', 'myhost'),
  57. module.Dump('postgresql_databases', 'foo', 'otherhost'),
  58. None,
  59. False,
  60. ),
  61. (
  62. module.Dump('postgresql_databases', 'foo', 'myhost'),
  63. module.Dump('postgresql_databases', 'foo', module.UNSPECIFIED),
  64. None,
  65. True,
  66. ),
  67. (
  68. module.Dump('postgresql_databases', 'foo', 'myhost'),
  69. module.Dump('postgresql_databases', 'bar', module.UNSPECIFIED),
  70. None,
  71. False,
  72. ),
  73. (
  74. module.Dump('postgresql_databases', 'foo', 'myhost', 1234),
  75. module.Dump('postgresql_databases', 'foo', 'myhost', 1234),
  76. None,
  77. True,
  78. ),
  79. (
  80. module.Dump('postgresql_databases', 'foo', 'myhost', 1234),
  81. module.Dump('postgresql_databases', 'foo', 'myhost', 4321),
  82. None,
  83. False,
  84. ),
  85. (
  86. module.Dump('postgresql_databases', 'foo', 'myhost', module.UNSPECIFIED),
  87. module.Dump('postgresql_databases', 'foo', 'myhost', 1234),
  88. None,
  89. True,
  90. ),
  91. (
  92. module.Dump('postgresql_databases', 'foo', 'myhost', module.UNSPECIFIED),
  93. module.Dump('postgresql_databases', 'foo', 'otherhost', 1234),
  94. None,
  95. False,
  96. ),
  97. (
  98. module.Dump(
  99. module.UNSPECIFIED, module.UNSPECIFIED, module.UNSPECIFIED, module.UNSPECIFIED
  100. ),
  101. module.Dump('postgresql_databases', 'foo', 'myhost', 1234),
  102. None,
  103. True,
  104. ),
  105. (
  106. module.Dump('postgresql_databases', 'foo', 'myhost', 5432),
  107. module.Dump('postgresql_databases', 'foo', 'myhost', None),
  108. 5432,
  109. True,
  110. ),
  111. (
  112. module.Dump('postgresql_databases', 'foo', 'myhost', None),
  113. module.Dump('postgresql_databases', 'foo', 'myhost', 5432),
  114. 5432,
  115. True,
  116. ),
  117. (
  118. module.Dump('postgresql_databases', 'foo', 'myhost', 5433),
  119. module.Dump('postgresql_databases', 'foo', 'myhost', None),
  120. 5432,
  121. False,
  122. ),
  123. ),
  124. )
  125. def test_dumps_match_compares_two_dumps_while_respecting_unspecified_values(
  126. first_dump, second_dump, default_port, expected_result
  127. ):
  128. assert module.dumps_match(first_dump, second_dump, default_port) == expected_result
  129. @pytest.mark.parametrize(
  130. 'dump,expected_result',
  131. (
  132. (
  133. module.Dump('postgresql_databases', 'foo'),
  134. 'foo@localhost (postgresql_databases)',
  135. ),
  136. (
  137. module.Dump(module.UNSPECIFIED, 'foo'),
  138. 'foo@localhost',
  139. ),
  140. (
  141. module.Dump('postgresql_databases', module.UNSPECIFIED),
  142. 'unspecified@localhost (postgresql_databases)',
  143. ),
  144. (
  145. module.Dump('postgresql_databases', 'foo', 'host'),
  146. 'foo@host (postgresql_databases)',
  147. ),
  148. (
  149. module.Dump('postgresql_databases', 'foo', module.UNSPECIFIED),
  150. 'foo (postgresql_databases)',
  151. ),
  152. (
  153. module.Dump('postgresql_databases', 'foo', 'host', 1234),
  154. 'foo@host:1234 (postgresql_databases)',
  155. ),
  156. (
  157. module.Dump('postgresql_databases', 'foo', module.UNSPECIFIED, 1234),
  158. 'foo@:1234 (postgresql_databases)',
  159. ),
  160. (
  161. module.Dump('postgresql_databases', 'foo', 'host', module.UNSPECIFIED),
  162. 'foo@host (postgresql_databases)',
  163. ),
  164. (
  165. module.Dump(
  166. module.UNSPECIFIED, module.UNSPECIFIED, module.UNSPECIFIED, module.UNSPECIFIED
  167. ),
  168. 'unspecified',
  169. ),
  170. ),
  171. )
  172. def test_render_dump_metadata_renders_dump_values_into_string(dump, expected_result):
  173. assert module.render_dump_metadata(dump) == expected_result
  174. def test_get_configured_data_source_matches_data_source_with_restore_dump():
  175. default_port = flexmock()
  176. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').and_return(default_port)
  177. flexmock(module).should_receive('dumps_match').and_return(False)
  178. flexmock(module).should_receive('dumps_match').with_args(
  179. module.Dump('postgresql_databases', 'bar'),
  180. module.Dump('postgresql_databases', 'bar'),
  181. default_port=default_port,
  182. ).and_return(True)
  183. assert module.get_configured_data_source(
  184. config={
  185. 'other_databases': [{'name': 'other'}],
  186. 'postgresql_databases': [{'name': 'foo'}, {'name': 'bar'}],
  187. },
  188. restore_dump=module.Dump('postgresql_databases', 'bar'),
  189. log_prefix='test',
  190. ) == {'name': 'bar'}
  191. def test_get_configured_data_source_matches_nothing_when_nothing_configured():
  192. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').and_return(flexmock())
  193. flexmock(module).should_receive('dumps_match').and_return(False)
  194. assert (
  195. module.get_configured_data_source(
  196. config={},
  197. restore_dump=module.Dump('postgresql_databases', 'quux'),
  198. log_prefix='test',
  199. )
  200. is None
  201. )
  202. def test_get_configured_data_source_matches_nothing_when_restore_dump_does_not_match_configuration():
  203. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').and_return(flexmock())
  204. flexmock(module).should_receive('dumps_match').and_return(False)
  205. assert (
  206. module.get_configured_data_source(
  207. config={
  208. 'postgresql_databases': [{'name': 'foo'}],
  209. },
  210. restore_dump=module.Dump('postgresql_databases', 'quux'),
  211. log_prefix='test',
  212. )
  213. is None
  214. )
  215. def test_get_configured_data_source_with_multiple_matching_data_sources_errors():
  216. default_port = flexmock()
  217. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').and_return(default_port)
  218. flexmock(module).should_receive('dumps_match').and_return(False)
  219. flexmock(module).should_receive('dumps_match').with_args(
  220. module.Dump('postgresql_databases', 'bar'),
  221. module.Dump('postgresql_databases', 'bar'),
  222. default_port=default_port,
  223. ).and_return(True)
  224. flexmock(module).should_receive('render_dump_metadata').and_return('test')
  225. with pytest.raises(ValueError):
  226. module.get_configured_data_source(
  227. config={
  228. 'other_databases': [{'name': 'other'}],
  229. 'postgresql_databases': [
  230. {'name': 'foo'},
  231. {'name': 'bar'},
  232. {'name': 'bar', 'format': 'directory'},
  233. ],
  234. },
  235. restore_dump=module.Dump('postgresql_databases', 'bar'),
  236. log_prefix='test',
  237. )
  238. def test_strip_path_prefix_from_extracted_dump_destination_renames_first_matching_databases_subdirectory():
  239. flexmock(module.os).should_receive('walk').and_return(
  240. [
  241. ('/foo', flexmock(), flexmock()),
  242. ('/foo/bar', flexmock(), flexmock()),
  243. ('/foo/bar/postgresql_databases', flexmock(), flexmock()),
  244. ('/foo/bar/mariadb_databases', flexmock(), flexmock()),
  245. ]
  246. )
  247. flexmock(module.shutil).should_receive('move').with_args(
  248. '/foo/bar/postgresql_databases', '/run/user/0/borgmatic/postgresql_databases'
  249. ).once()
  250. flexmock(module.shutil).should_receive('move').with_args(
  251. '/foo/bar/mariadb_databases', '/run/user/0/borgmatic/mariadb_databases'
  252. ).never()
  253. module.strip_path_prefix_from_extracted_dump_destination('/foo', '/run/user/0/borgmatic')
  254. def test_restore_single_dump_extracts_and_restores_single_file_dump():
  255. flexmock(module).should_receive('render_dump_metadata').and_return('test')
  256. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args(
  257. 'make_data_source_dump_patterns', object, object, object, object, object
  258. ).and_return({'postgresql': flexmock()})
  259. flexmock(module.tempfile).should_receive('mkdtemp').never()
  260. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  261. 'convert_glob_patterns_to_borg_pattern'
  262. ).and_return(flexmock())
  263. flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').and_return(
  264. flexmock()
  265. ).once()
  266. flexmock(module).should_receive('strip_path_prefix_from_extracted_dump_destination').never()
  267. flexmock(module.shutil).should_receive('rmtree').never()
  268. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').with_args(
  269. function_name='restore_data_source_dump',
  270. config=object,
  271. log_prefix=object,
  272. hook_name=object,
  273. data_source=object,
  274. dry_run=object,
  275. extract_process=object,
  276. connection_params=object,
  277. borgmatic_runtime_directory=object,
  278. ).once()
  279. module.restore_single_dump(
  280. repository={'path': 'test.borg'},
  281. config=flexmock(),
  282. local_borg_version=flexmock(),
  283. global_arguments=flexmock(dry_run=False),
  284. local_path=None,
  285. remote_path=None,
  286. archive_name=flexmock(),
  287. hook_name='postgresql',
  288. data_source={'name': 'test', 'format': 'plain'},
  289. connection_params=flexmock(),
  290. borgmatic_runtime_directory='/run/borgmatic',
  291. )
  292. def test_restore_single_dump_extracts_and_restores_directory_dump():
  293. flexmock(module).should_receive('render_dump_metadata').and_return('test')
  294. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args(
  295. 'make_data_source_dump_patterns', object, object, object, object, object
  296. ).and_return({'postgresql': flexmock()})
  297. flexmock(module.tempfile).should_receive('mkdtemp').once().and_return(
  298. '/run/user/0/borgmatic/tmp1234'
  299. )
  300. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  301. 'convert_glob_patterns_to_borg_pattern'
  302. ).and_return(flexmock())
  303. flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').and_return(
  304. flexmock()
  305. ).once()
  306. flexmock(module).should_receive('strip_path_prefix_from_extracted_dump_destination').once()
  307. flexmock(module.shutil).should_receive('rmtree').once()
  308. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').with_args(
  309. function_name='restore_data_source_dump',
  310. config=object,
  311. log_prefix=object,
  312. hook_name=object,
  313. data_source=object,
  314. dry_run=object,
  315. extract_process=object,
  316. connection_params=object,
  317. borgmatic_runtime_directory='/run/borgmatic',
  318. ).once()
  319. module.restore_single_dump(
  320. repository={'path': 'test.borg'},
  321. config=flexmock(),
  322. local_borg_version=flexmock(),
  323. global_arguments=flexmock(dry_run=False),
  324. local_path=None,
  325. remote_path=None,
  326. archive_name=flexmock(),
  327. hook_name='postgresql',
  328. data_source={'name': 'test', 'format': 'directory'},
  329. connection_params=flexmock(),
  330. borgmatic_runtime_directory='/run/borgmatic',
  331. )
  332. def test_restore_single_dump_with_directory_dump_error_cleans_up_temporary_directory():
  333. flexmock(module).should_receive('render_dump_metadata').and_return('test')
  334. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args(
  335. 'make_data_source_dump_patterns', object, object, object, object, object
  336. ).and_return({'postgresql': flexmock()})
  337. flexmock(module.tempfile).should_receive('mkdtemp').once().and_return(
  338. '/run/user/0/borgmatic/tmp1234'
  339. )
  340. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  341. 'convert_glob_patterns_to_borg_pattern'
  342. ).and_return(flexmock())
  343. flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').and_raise(
  344. ValueError
  345. ).once()
  346. flexmock(module).should_receive('strip_path_prefix_from_extracted_dump_destination').never()
  347. flexmock(module.shutil).should_receive('rmtree').once()
  348. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').with_args(
  349. function_name='restore_data_source_dump',
  350. config=object,
  351. log_prefix=object,
  352. hook_name=object,
  353. data_source=object,
  354. dry_run=object,
  355. extract_process=object,
  356. connection_params=object,
  357. borgmatic_runtime_directory='/run/user/0/borgmatic/tmp1234',
  358. ).never()
  359. with pytest.raises(ValueError):
  360. module.restore_single_dump(
  361. repository={'path': 'test.borg'},
  362. config=flexmock(),
  363. local_borg_version=flexmock(),
  364. global_arguments=flexmock(dry_run=False),
  365. local_path=None,
  366. remote_path=None,
  367. archive_name=flexmock(),
  368. hook_name='postgresql',
  369. data_source={'name': 'test', 'format': 'directory'},
  370. connection_params=flexmock(),
  371. borgmatic_runtime_directory='/run/borgmatic',
  372. )
  373. def test_restore_single_dump_with_directory_dump_and_dry_run_skips_directory_move_and_cleanup():
  374. flexmock(module).should_receive('render_dump_metadata').and_return('test')
  375. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args(
  376. 'make_data_source_dump_patterns', object, object, object, object, object
  377. ).and_return({'postgresql': flexmock()})
  378. flexmock(module.tempfile).should_receive('mkdtemp').once().and_return('/run/borgmatic/tmp1234')
  379. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  380. 'convert_glob_patterns_to_borg_pattern'
  381. ).and_return(flexmock())
  382. flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').and_return(
  383. flexmock()
  384. ).once()
  385. flexmock(module).should_receive('strip_path_prefix_from_extracted_dump_destination').never()
  386. flexmock(module.shutil).should_receive('rmtree').never()
  387. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').with_args(
  388. function_name='restore_data_source_dump',
  389. config=object,
  390. log_prefix=object,
  391. hook_name=object,
  392. data_source=object,
  393. dry_run=object,
  394. extract_process=object,
  395. connection_params=object,
  396. borgmatic_runtime_directory='/run/borgmatic',
  397. ).once()
  398. module.restore_single_dump(
  399. repository={'path': 'test.borg'},
  400. config=flexmock(),
  401. local_borg_version=flexmock(),
  402. global_arguments=flexmock(dry_run=True),
  403. local_path=None,
  404. remote_path=None,
  405. archive_name=flexmock(),
  406. hook_name='postgresql',
  407. data_source={'name': 'test', 'format': 'directory'},
  408. connection_params=flexmock(),
  409. borgmatic_runtime_directory='/run/borgmatic',
  410. )
  411. def test_collect_dumps_from_archive_parses_archive_paths():
  412. flexmock(module.borgmatic.config.paths).should_receive(
  413. 'get_borgmatic_source_directory'
  414. ).and_return('/root/.borgmatic')
  415. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  416. 'make_data_source_dump_path'
  417. ).and_return('')
  418. flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
  419. [
  420. 'borgmatic/postgresql_databases/localhost/foo',
  421. 'borgmatic/postgresql_databases/host:1234/bar',
  422. 'borgmatic/mysql_databases/localhost/quux',
  423. ]
  424. )
  425. archive_dumps = module.collect_dumps_from_archive(
  426. repository={'path': 'repo'},
  427. archive='archive',
  428. config={},
  429. local_borg_version=flexmock(),
  430. global_arguments=flexmock(log_json=False),
  431. local_path=flexmock(),
  432. remote_path=flexmock(),
  433. borgmatic_runtime_directory='/run/borgmatic',
  434. )
  435. assert archive_dumps == {
  436. module.Dump('postgresql_databases', 'foo'),
  437. module.Dump('postgresql_databases', 'bar', 'host', 1234),
  438. module.Dump('mysql_databases', 'quux'),
  439. }
  440. def test_collect_dumps_from_archive_parses_archive_paths_with_different_base_directories():
  441. flexmock(module.borgmatic.config.paths).should_receive(
  442. 'get_borgmatic_source_directory'
  443. ).and_return('/root/.borgmatic')
  444. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  445. 'make_data_source_dump_path'
  446. ).and_return('')
  447. flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
  448. [
  449. 'borgmatic/postgresql_databases/localhost/foo',
  450. '.borgmatic/postgresql_databases/localhost/bar',
  451. '/root/.borgmatic/postgresql_databases/localhost/baz',
  452. '/var/run/0/borgmatic/mysql_databases/localhost/quux',
  453. ]
  454. )
  455. archive_dumps = module.collect_dumps_from_archive(
  456. repository={'path': 'repo'},
  457. archive='archive',
  458. config={},
  459. local_borg_version=flexmock(),
  460. global_arguments=flexmock(log_json=False),
  461. local_path=flexmock(),
  462. remote_path=flexmock(),
  463. borgmatic_runtime_directory='/run/borgmatic',
  464. )
  465. assert archive_dumps == {
  466. module.Dump('postgresql_databases', 'foo'),
  467. module.Dump('postgresql_databases', 'bar'),
  468. module.Dump('postgresql_databases', 'baz'),
  469. module.Dump('mysql_databases', 'quux'),
  470. }
  471. def test_collect_dumps_from_archive_parses_directory_format_archive_paths():
  472. flexmock(module.borgmatic.config.paths).should_receive(
  473. 'get_borgmatic_source_directory'
  474. ).and_return('/root/.borgmatic')
  475. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  476. 'make_data_source_dump_path'
  477. ).and_return('')
  478. flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
  479. [
  480. 'borgmatic/postgresql_databases/localhost/foo/table1',
  481. 'borgmatic/postgresql_databases/localhost/foo/table2',
  482. ]
  483. )
  484. archive_dumps = module.collect_dumps_from_archive(
  485. repository={'path': 'repo'},
  486. archive='archive',
  487. config={},
  488. local_borg_version=flexmock(),
  489. global_arguments=flexmock(log_json=False),
  490. local_path=flexmock(),
  491. remote_path=flexmock(),
  492. borgmatic_runtime_directory='/run/borgmatic',
  493. )
  494. assert archive_dumps == {
  495. module.Dump('postgresql_databases', 'foo'),
  496. }
  497. def test_collect_dumps_from_archive_skips_bad_archive_paths_or_bad_path_components():
  498. flexmock(module.borgmatic.config.paths).should_receive(
  499. 'get_borgmatic_source_directory'
  500. ).and_return('/root/.borgmatic')
  501. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  502. 'make_data_source_dump_path'
  503. ).and_return('')
  504. flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
  505. [
  506. 'borgmatic/postgresql_databases/localhost/foo',
  507. 'borgmatic/postgresql_databases/localhost:abcd/bar',
  508. 'borgmatic/invalid',
  509. 'invalid/as/well',
  510. '',
  511. ]
  512. )
  513. archive_dumps = module.collect_dumps_from_archive(
  514. repository={'path': 'repo'},
  515. archive='archive',
  516. config={},
  517. local_borg_version=flexmock(),
  518. global_arguments=flexmock(log_json=False),
  519. local_path=flexmock(),
  520. remote_path=flexmock(),
  521. borgmatic_runtime_directory='/run/borgmatic',
  522. )
  523. assert archive_dumps == {
  524. module.Dump('postgresql_databases', 'foo'),
  525. module.Dump('postgresql_databases', 'bar'),
  526. }
  527. def test_get_dumps_to_restore_gets_requested_dumps_found_in_archive():
  528. dumps_from_archive = {
  529. module.Dump('postgresql_databases', 'foo'),
  530. module.Dump('postgresql_databases', 'bar'),
  531. module.Dump('postgresql_databases', 'baz'),
  532. }
  533. flexmock(module).should_receive('dumps_match').and_return(False)
  534. flexmock(module).should_receive('dumps_match').with_args(
  535. module.Dump(module.UNSPECIFIED, 'foo'),
  536. module.Dump('postgresql_databases', 'foo'),
  537. ).and_return(True)
  538. flexmock(module).should_receive('dumps_match').with_args(
  539. module.Dump(module.UNSPECIFIED, 'bar'),
  540. module.Dump('postgresql_databases', 'bar'),
  541. ).and_return(True)
  542. assert module.get_dumps_to_restore(
  543. restore_arguments=flexmock(
  544. hook=None,
  545. data_sources=['foo', 'bar'],
  546. original_hostname=None,
  547. original_port=None,
  548. ),
  549. dumps_from_archive=dumps_from_archive,
  550. ) == {
  551. module.Dump('postgresql_databases', 'foo'),
  552. module.Dump('postgresql_databases', 'bar'),
  553. }
  554. def test_get_dumps_to_restore_raises_for_requested_dumps_missing_from_archive():
  555. dumps_from_archive = {
  556. module.Dump('postgresql_databases', 'foo'),
  557. }
  558. flexmock(module).should_receive('dumps_match').and_return(False)
  559. flexmock(module).should_receive('render_dump_metadata').and_return('test')
  560. with pytest.raises(ValueError):
  561. module.get_dumps_to_restore(
  562. restore_arguments=flexmock(
  563. hook=None,
  564. data_sources=['foo', 'bar'],
  565. original_hostname=None,
  566. original_port=None,
  567. ),
  568. dumps_from_archive=dumps_from_archive,
  569. )
  570. def test_get_dumps_to_restore_without_requested_dumps_finds_all_archive_dumps():
  571. dumps_from_archive = {
  572. module.Dump('postgresql_databases', 'foo'),
  573. module.Dump('postgresql_databases', 'bar'),
  574. }
  575. flexmock(module).should_receive('dumps_match').and_return(False)
  576. assert (
  577. module.get_dumps_to_restore(
  578. restore_arguments=flexmock(
  579. hook=None,
  580. data_sources=[],
  581. original_hostname=None,
  582. original_port=None,
  583. ),
  584. dumps_from_archive=dumps_from_archive,
  585. )
  586. == dumps_from_archive
  587. )
  588. def test_get_dumps_to_restore_with_all_in_requested_dumps_finds_all_archive_dumps():
  589. dumps_from_archive = {
  590. module.Dump('postgresql_databases', 'foo'),
  591. module.Dump('postgresql_databases', 'bar'),
  592. }
  593. flexmock(module).should_receive('dumps_match').and_return(False)
  594. flexmock(module).should_receive('dumps_match').with_args(
  595. module.Dump(module.UNSPECIFIED, 'foo'),
  596. module.Dump('postgresql_databases', 'foo'),
  597. ).and_return(True)
  598. flexmock(module).should_receive('dumps_match').with_args(
  599. module.Dump(module.UNSPECIFIED, 'bar'),
  600. module.Dump('postgresql_databases', 'bar'),
  601. ).and_return(True)
  602. assert (
  603. module.get_dumps_to_restore(
  604. restore_arguments=flexmock(
  605. hook=None,
  606. data_sources=['all'],
  607. original_hostname=None,
  608. original_port=None,
  609. ),
  610. dumps_from_archive=dumps_from_archive,
  611. )
  612. == dumps_from_archive
  613. )
  614. def test_get_dumps_to_restore_with_all_in_requested_dumps_plus_additional_requested_dumps_omits_duplicates():
  615. dumps_from_archive = {
  616. module.Dump('postgresql_databases', 'foo'),
  617. module.Dump('postgresql_databases', 'bar'),
  618. }
  619. flexmock(module).should_receive('dumps_match').and_return(False)
  620. flexmock(module).should_receive('dumps_match').with_args(
  621. module.Dump(module.UNSPECIFIED, 'foo'),
  622. module.Dump('postgresql_databases', 'foo'),
  623. ).and_return(True)
  624. flexmock(module).should_receive('dumps_match').with_args(
  625. module.Dump(module.UNSPECIFIED, 'bar'),
  626. module.Dump('postgresql_databases', 'bar'),
  627. ).and_return(True)
  628. assert (
  629. module.get_dumps_to_restore(
  630. restore_arguments=flexmock(
  631. hook=None,
  632. data_sources=['all', 'foo', 'bar'],
  633. original_hostname=None,
  634. original_port=None,
  635. ),
  636. dumps_from_archive=dumps_from_archive,
  637. )
  638. == dumps_from_archive
  639. )
  640. def test_get_dumps_to_restore_raises_for_multiple_matching_dumps_in_archive():
  641. flexmock(module).should_receive('dumps_match').and_return(False)
  642. flexmock(module).should_receive('dumps_match').with_args(
  643. module.Dump(module.UNSPECIFIED, 'foo'),
  644. module.Dump('postgresql_databases', 'foo'),
  645. ).and_return(True)
  646. flexmock(module).should_receive('dumps_match').with_args(
  647. module.Dump(module.UNSPECIFIED, 'foo'),
  648. module.Dump('mariadb_databases', 'foo'),
  649. ).and_return(True)
  650. flexmock(module).should_receive('render_dump_metadata').and_return('test')
  651. with pytest.raises(ValueError):
  652. module.get_dumps_to_restore(
  653. restore_arguments=flexmock(
  654. hook=None,
  655. data_sources=['foo'],
  656. original_hostname=None,
  657. original_port=None,
  658. ),
  659. dumps_from_archive={
  660. module.Dump('postgresql_databases', 'foo'),
  661. module.Dump('mariadb_databases', 'foo'),
  662. },
  663. )
  664. def test_get_dumps_to_restore_raises_for_all_in_requested_dumps_and_requested_dumps_missing_from_archive():
  665. flexmock(module).should_receive('dumps_match').and_return(False)
  666. flexmock(module).should_receive('dumps_match').with_args(
  667. module.Dump(module.UNSPECIFIED, 'foo'),
  668. module.Dump('postgresql_databases', 'foo'),
  669. ).and_return(True)
  670. flexmock(module).should_receive('render_dump_metadata').and_return('test')
  671. with pytest.raises(ValueError):
  672. module.get_dumps_to_restore(
  673. restore_arguments=flexmock(
  674. hook=None,
  675. data_sources=['all', 'foo', 'bar'],
  676. original_hostname=None,
  677. original_port=None,
  678. ),
  679. dumps_from_archive={module.Dump('postresql_databases', 'foo')},
  680. )
  681. def test_get_dumps_to_restore_with_requested_hook_name_filters_dumps_found_in_archive():
  682. dumps_from_archive = {
  683. module.Dump('mariadb_databases', 'foo'),
  684. module.Dump('postgresql_databases', 'foo'),
  685. module.Dump('sqlite_databases', 'bar'),
  686. }
  687. flexmock(module).should_receive('dumps_match').and_return(False)
  688. flexmock(module).should_receive('dumps_match').with_args(
  689. module.Dump('postgresql_databases', 'foo'),
  690. module.Dump('postgresql_databases', 'foo'),
  691. ).and_return(True)
  692. assert module.get_dumps_to_restore(
  693. restore_arguments=flexmock(
  694. hook='postgresql_databases',
  695. data_sources=['foo'],
  696. original_hostname=None,
  697. original_port=None,
  698. ),
  699. dumps_from_archive=dumps_from_archive,
  700. ) == {
  701. module.Dump('postgresql_databases', 'foo'),
  702. }
  703. def test_get_dumps_to_restore_with_requested_shortened_hook_name_filters_dumps_found_in_archive():
  704. dumps_from_archive = {
  705. module.Dump('mariadb_databases', 'foo'),
  706. module.Dump('postgresql_databases', 'foo'),
  707. module.Dump('sqlite_databases', 'bar'),
  708. }
  709. flexmock(module).should_receive('dumps_match').and_return(False)
  710. flexmock(module).should_receive('dumps_match').with_args(
  711. module.Dump('postgresql_databases', 'foo'),
  712. module.Dump('postgresql_databases', 'foo'),
  713. ).and_return(True)
  714. assert module.get_dumps_to_restore(
  715. restore_arguments=flexmock(
  716. hook='postgresql',
  717. data_sources=['foo'],
  718. original_hostname=None,
  719. original_port=None,
  720. ),
  721. dumps_from_archive=dumps_from_archive,
  722. ) == {
  723. module.Dump('postgresql_databases', 'foo'),
  724. }
  725. def test_get_dumps_to_restore_with_requested_hostname_filters_dumps_found_in_archive():
  726. dumps_from_archive = {
  727. module.Dump('postgresql_databases', 'foo'),
  728. module.Dump('postgresql_databases', 'foo', 'host'),
  729. module.Dump('postgresql_databases', 'bar'),
  730. }
  731. flexmock(module).should_receive('dumps_match').and_return(False)
  732. flexmock(module).should_receive('dumps_match').with_args(
  733. module.Dump('postgresql_databases', 'foo', 'host'),
  734. module.Dump('postgresql_databases', 'foo', 'host'),
  735. ).and_return(True)
  736. assert module.get_dumps_to_restore(
  737. restore_arguments=flexmock(
  738. hook='postgresql_databases',
  739. data_sources=['foo'],
  740. original_hostname='host',
  741. original_port=None,
  742. ),
  743. dumps_from_archive=dumps_from_archive,
  744. ) == {
  745. module.Dump('postgresql_databases', 'foo', 'host'),
  746. }
  747. def test_get_dumps_to_restore_with_requested_port_filters_dumps_found_in_archive():
  748. dumps_from_archive = {
  749. module.Dump('postgresql_databases', 'foo', 'host'),
  750. module.Dump('postgresql_databases', 'foo', 'host', 1234),
  751. module.Dump('postgresql_databases', 'bar'),
  752. }
  753. flexmock(module).should_receive('dumps_match').and_return(False)
  754. flexmock(module).should_receive('dumps_match').with_args(
  755. module.Dump('postgresql_databases', 'foo', 'host', 1234),
  756. module.Dump('postgresql_databases', 'foo', 'host', 1234),
  757. ).and_return(True)
  758. assert module.get_dumps_to_restore(
  759. restore_arguments=flexmock(
  760. hook='postgresql_databases',
  761. data_sources=['foo'],
  762. original_hostname='host',
  763. original_port=1234,
  764. ),
  765. dumps_from_archive=dumps_from_archive,
  766. ) == {
  767. module.Dump('postgresql_databases', 'foo', 'host', 1234),
  768. }
  769. def test_ensure_requested_dumps_restored_with_all_dumps_restored_does_not_raise():
  770. module.ensure_requested_dumps_restored(
  771. dumps_to_restore={
  772. module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  773. module.Dump(hook_name='postgresql_databases', data_source_name='bar'),
  774. },
  775. dumps_actually_restored={
  776. module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  777. module.Dump(hook_name='postgresql_databases', data_source_name='bar'),
  778. },
  779. )
  780. def test_ensure_requested_dumps_restored_with_no_dumps_raises():
  781. with pytest.raises(ValueError):
  782. module.ensure_requested_dumps_restored(
  783. dumps_to_restore={},
  784. dumps_actually_restored={},
  785. )
  786. def test_ensure_requested_dumps_restored_with_missing_dumps_raises():
  787. flexmock(module).should_receive('render_dump_metadata').and_return('test')
  788. with pytest.raises(ValueError):
  789. module.ensure_requested_dumps_restored(
  790. dumps_to_restore={
  791. module.Dump(hook_name='postgresql_databases', data_source_name='foo')
  792. },
  793. dumps_actually_restored={
  794. module.Dump(hook_name='postgresql_databases', data_source_name='bar')
  795. },
  796. )
  797. def test_run_restore_restores_each_data_source():
  798. dumps_to_restore = {
  799. module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  800. module.Dump(hook_name='postgresql_databases', data_source_name='bar'),
  801. }
  802. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
  803. borgmatic_runtime_directory = flexmock()
  804. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  805. borgmatic_runtime_directory
  806. )
  807. flexmock(module.borgmatic.config.paths).should_receive(
  808. 'make_runtime_directory_glob'
  809. ).replace_with(lambda path: path)
  810. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
  811. flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
  812. flexmock()
  813. )
  814. flexmock(module).should_receive('collect_dumps_from_archive').and_return(flexmock())
  815. flexmock(module).should_receive('get_dumps_to_restore').and_return(dumps_to_restore)
  816. flexmock(module).should_receive('get_configured_data_source').and_return(
  817. {'name': 'foo'}
  818. ).and_return({'name': 'bar'})
  819. flexmock(module).should_receive('restore_single_dump').with_args(
  820. repository=object,
  821. config=object,
  822. local_borg_version=object,
  823. global_arguments=object,
  824. local_path=object,
  825. remote_path=object,
  826. archive_name=object,
  827. hook_name='postgresql_databases',
  828. data_source={'name': 'foo', 'schemas': None},
  829. connection_params=object,
  830. borgmatic_runtime_directory=borgmatic_runtime_directory,
  831. ).once()
  832. flexmock(module).should_receive('restore_single_dump').with_args(
  833. repository=object,
  834. config=object,
  835. local_borg_version=object,
  836. global_arguments=object,
  837. local_path=object,
  838. remote_path=object,
  839. archive_name=object,
  840. hook_name='postgresql_databases',
  841. data_source={'name': 'bar', 'schemas': None},
  842. connection_params=object,
  843. borgmatic_runtime_directory=borgmatic_runtime_directory,
  844. ).once()
  845. flexmock(module).should_receive('ensure_requested_dumps_restored')
  846. module.run_restore(
  847. repository={'path': 'repo'},
  848. config=flexmock(),
  849. local_borg_version=flexmock(),
  850. restore_arguments=flexmock(
  851. repository='repo',
  852. archive='archive',
  853. data_sources=flexmock(),
  854. schemas=None,
  855. hostname=None,
  856. port=None,
  857. username=None,
  858. password=None,
  859. restore_path=None,
  860. ),
  861. global_arguments=flexmock(dry_run=False),
  862. local_path=flexmock(),
  863. remote_path=flexmock(),
  864. )
  865. def test_run_restore_bails_for_non_matching_repository():
  866. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(
  867. False
  868. )
  869. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  870. flexmock()
  871. )
  872. flexmock(module.borgmatic.config.paths).should_receive(
  873. 'make_runtime_directory_glob'
  874. ).replace_with(lambda path: path)
  875. flexmock(module.borgmatic.hooks.dispatch).should_receive(
  876. 'call_hooks_even_if_unconfigured'
  877. ).never()
  878. flexmock(module).should_receive('restore_single_dump').never()
  879. module.run_restore(
  880. repository={'path': 'repo'},
  881. config=flexmock(),
  882. local_borg_version=flexmock(),
  883. restore_arguments=flexmock(repository='repo', archive='archive', data_sources=flexmock()),
  884. global_arguments=flexmock(dry_run=False),
  885. local_path=flexmock(),
  886. remote_path=flexmock(),
  887. )
  888. def test_run_restore_restores_data_source_by_falling_back_to_all_name():
  889. dumps_to_restore = {
  890. module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  891. }
  892. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
  893. borgmatic_runtime_directory = flexmock()
  894. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  895. borgmatic_runtime_directory
  896. )
  897. flexmock(module.borgmatic.config.paths).should_receive(
  898. 'make_runtime_directory_glob'
  899. ).replace_with(lambda path: path)
  900. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
  901. flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
  902. flexmock()
  903. )
  904. flexmock(module).should_receive('collect_dumps_from_archive').and_return(flexmock())
  905. flexmock(module).should_receive('get_dumps_to_restore').and_return(dumps_to_restore)
  906. flexmock(module).should_receive('get_configured_data_source').and_return(
  907. {'name': 'foo'}
  908. ).and_return({'name': 'all'})
  909. flexmock(module).should_receive('restore_single_dump').with_args(
  910. repository=object,
  911. config=object,
  912. local_borg_version=object,
  913. global_arguments=object,
  914. local_path=object,
  915. remote_path=object,
  916. archive_name=object,
  917. hook_name='postgresql_databases',
  918. data_source={'name': 'foo', 'schemas': None},
  919. connection_params=object,
  920. borgmatic_runtime_directory=borgmatic_runtime_directory,
  921. ).once()
  922. flexmock(module).should_receive('ensure_requested_dumps_restored')
  923. module.run_restore(
  924. repository={'path': 'repo'},
  925. config=flexmock(),
  926. local_borg_version=flexmock(),
  927. restore_arguments=flexmock(
  928. repository='repo',
  929. archive='archive',
  930. data_sources=flexmock(),
  931. schemas=None,
  932. hostname=None,
  933. port=None,
  934. username=None,
  935. password=None,
  936. restore_path=None,
  937. ),
  938. global_arguments=flexmock(dry_run=False),
  939. local_path=flexmock(),
  940. remote_path=flexmock(),
  941. )
  942. def test_run_restore_restores_data_source_configured_with_all_name():
  943. dumps_to_restore = {
  944. module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  945. module.Dump(hook_name='postgresql_databases', data_source_name='bar'),
  946. }
  947. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
  948. borgmatic_runtime_directory = flexmock()
  949. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  950. borgmatic_runtime_directory
  951. )
  952. flexmock(module.borgmatic.config.paths).should_receive(
  953. 'make_runtime_directory_glob'
  954. ).replace_with(lambda path: path)
  955. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
  956. flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
  957. flexmock()
  958. )
  959. flexmock(module).should_receive('collect_dumps_from_archive').and_return(flexmock())
  960. flexmock(module).should_receive('get_dumps_to_restore').and_return(dumps_to_restore)
  961. flexmock(module).should_receive('get_configured_data_source').with_args(
  962. config=object,
  963. restore_dump=module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  964. log_prefix=object,
  965. ).and_return({'name': 'foo'})
  966. flexmock(module).should_receive('get_configured_data_source').with_args(
  967. config=object,
  968. restore_dump=module.Dump(hook_name='postgresql_databases', data_source_name='bar'),
  969. log_prefix=object,
  970. ).and_return(None)
  971. flexmock(module).should_receive('get_configured_data_source').with_args(
  972. config=object,
  973. restore_dump=module.Dump(hook_name='postgresql_databases', data_source_name='all'),
  974. log_prefix=object,
  975. ).and_return({'name': 'bar'})
  976. flexmock(module).should_receive('restore_single_dump').with_args(
  977. repository=object,
  978. config=object,
  979. local_borg_version=object,
  980. global_arguments=object,
  981. local_path=object,
  982. remote_path=object,
  983. archive_name=object,
  984. hook_name='postgresql_databases',
  985. data_source={'name': 'foo', 'schemas': None},
  986. connection_params=object,
  987. borgmatic_runtime_directory=borgmatic_runtime_directory,
  988. ).once()
  989. flexmock(module).should_receive('restore_single_dump').with_args(
  990. repository=object,
  991. config=object,
  992. local_borg_version=object,
  993. global_arguments=object,
  994. local_path=object,
  995. remote_path=object,
  996. archive_name=object,
  997. hook_name='postgresql_databases',
  998. data_source={'name': 'bar', 'schemas': None},
  999. connection_params=object,
  1000. borgmatic_runtime_directory=borgmatic_runtime_directory,
  1001. ).once()
  1002. flexmock(module).should_receive('ensure_requested_dumps_restored')
  1003. module.run_restore(
  1004. repository={'path': 'repo'},
  1005. config=flexmock(),
  1006. local_borg_version=flexmock(),
  1007. restore_arguments=flexmock(
  1008. repository='repo',
  1009. archive='archive',
  1010. data_sources=flexmock(),
  1011. schemas=None,
  1012. hostname=None,
  1013. port=None,
  1014. username=None,
  1015. password=None,
  1016. restore_path=None,
  1017. ),
  1018. global_arguments=flexmock(dry_run=False),
  1019. local_path=flexmock(),
  1020. remote_path=flexmock(),
  1021. )
  1022. def test_run_restore_skips_missing_data_source():
  1023. dumps_to_restore = {
  1024. module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  1025. module.Dump(hook_name='postgresql_databases', data_source_name='bar'),
  1026. }
  1027. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
  1028. borgmatic_runtime_directory = flexmock()
  1029. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  1030. borgmatic_runtime_directory
  1031. )
  1032. flexmock(module.borgmatic.config.paths).should_receive(
  1033. 'make_runtime_directory_glob'
  1034. ).replace_with(lambda path: path)
  1035. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
  1036. flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
  1037. flexmock()
  1038. )
  1039. flexmock(module).should_receive('collect_dumps_from_archive').and_return(flexmock())
  1040. flexmock(module).should_receive('get_dumps_to_restore').and_return(dumps_to_restore)
  1041. flexmock(module).should_receive('get_configured_data_source').with_args(
  1042. config=object,
  1043. restore_dump=module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  1044. log_prefix=object,
  1045. ).and_return({'name': 'foo'})
  1046. flexmock(module).should_receive('get_configured_data_source').with_args(
  1047. config=object,
  1048. restore_dump=module.Dump(hook_name='postgresql_databases', data_source_name='bar'),
  1049. log_prefix=object,
  1050. ).and_return(None)
  1051. flexmock(module).should_receive('get_configured_data_source').with_args(
  1052. config=object,
  1053. restore_dump=module.Dump(hook_name='postgresql_databases', data_source_name='all'),
  1054. log_prefix=object,
  1055. ).and_return(None)
  1056. flexmock(module).should_receive('restore_single_dump').with_args(
  1057. repository=object,
  1058. config=object,
  1059. local_borg_version=object,
  1060. global_arguments=object,
  1061. local_path=object,
  1062. remote_path=object,
  1063. archive_name=object,
  1064. hook_name='postgresql_databases',
  1065. data_source={'name': 'foo', 'schemas': None},
  1066. connection_params=object,
  1067. borgmatic_runtime_directory=borgmatic_runtime_directory,
  1068. ).once()
  1069. flexmock(module).should_receive('restore_single_dump').with_args(
  1070. repository=object,
  1071. config=object,
  1072. local_borg_version=object,
  1073. global_arguments=object,
  1074. local_path=object,
  1075. remote_path=object,
  1076. archive_name=object,
  1077. hook_name='postgresql_databases',
  1078. data_source={'name': 'bar', 'schemas': None},
  1079. connection_params=object,
  1080. borgmatic_runtime_directory=borgmatic_runtime_directory,
  1081. ).never()
  1082. flexmock(module).should_receive('ensure_requested_dumps_restored')
  1083. module.run_restore(
  1084. repository={'path': 'repo'},
  1085. config=flexmock(),
  1086. local_borg_version=flexmock(),
  1087. restore_arguments=flexmock(
  1088. repository='repo',
  1089. archive='archive',
  1090. data_sources=flexmock(),
  1091. schemas=None,
  1092. hostname=None,
  1093. port=None,
  1094. username=None,
  1095. password=None,
  1096. restore_path=None,
  1097. ),
  1098. global_arguments=flexmock(dry_run=False),
  1099. local_path=flexmock(),
  1100. remote_path=flexmock(),
  1101. )
  1102. def test_run_restore_restores_data_sources_from_different_hooks():
  1103. dumps_to_restore = {
  1104. module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  1105. module.Dump(hook_name='mysql_databases', data_source_name='foo'),
  1106. }
  1107. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
  1108. borgmatic_runtime_directory = flexmock()
  1109. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  1110. borgmatic_runtime_directory
  1111. )
  1112. flexmock(module.borgmatic.config.paths).should_receive(
  1113. 'make_runtime_directory_glob'
  1114. ).replace_with(lambda path: path)
  1115. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
  1116. flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
  1117. flexmock()
  1118. )
  1119. flexmock(module).should_receive('collect_dumps_from_archive').and_return(flexmock())
  1120. flexmock(module).should_receive('get_dumps_to_restore').and_return(dumps_to_restore)
  1121. flexmock(module).should_receive('get_configured_data_source').with_args(
  1122. config=object,
  1123. restore_dump=module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  1124. log_prefix=object,
  1125. ).and_return({'name': 'foo'})
  1126. flexmock(module).should_receive('get_configured_data_source').with_args(
  1127. config=object,
  1128. restore_dump=module.Dump(hook_name='mysql_databases', data_source_name='foo'),
  1129. log_prefix=object,
  1130. ).and_return({'name': 'bar'})
  1131. flexmock(module).should_receive('restore_single_dump').with_args(
  1132. repository=object,
  1133. config=object,
  1134. local_borg_version=object,
  1135. global_arguments=object,
  1136. local_path=object,
  1137. remote_path=object,
  1138. archive_name=object,
  1139. hook_name='postgresql_databases',
  1140. data_source={'name': 'foo', 'schemas': None},
  1141. connection_params=object,
  1142. borgmatic_runtime_directory=borgmatic_runtime_directory,
  1143. ).once()
  1144. flexmock(module).should_receive('restore_single_dump').with_args(
  1145. repository=object,
  1146. config=object,
  1147. local_borg_version=object,
  1148. global_arguments=object,
  1149. local_path=object,
  1150. remote_path=object,
  1151. archive_name=object,
  1152. hook_name='mysql_databases',
  1153. data_source={'name': 'bar', 'schemas': None},
  1154. connection_params=object,
  1155. borgmatic_runtime_directory=borgmatic_runtime_directory,
  1156. ).once()
  1157. flexmock(module).should_receive('ensure_requested_dumps_restored')
  1158. module.run_restore(
  1159. repository={'path': 'repo'},
  1160. config=flexmock(),
  1161. local_borg_version=flexmock(),
  1162. restore_arguments=flexmock(
  1163. repository='repo',
  1164. archive='archive',
  1165. data_sources=flexmock(),
  1166. schemas=None,
  1167. hostname=None,
  1168. port=None,
  1169. username=None,
  1170. password=None,
  1171. restore_path=None,
  1172. ),
  1173. global_arguments=flexmock(dry_run=False),
  1174. local_path=flexmock(),
  1175. remote_path=flexmock(),
  1176. )