test_restore.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813
  1. import pytest
  2. from flexmock import flexmock
  3. import borgmatic.actions.restore as module
  4. def test_get_configured_data_source_matches_data_source_by_name():
  5. assert module.get_configured_data_source(
  6. config={
  7. 'other_databases': [{'name': 'other'}],
  8. 'postgresql_databases': [{'name': 'foo'}, {'name': 'bar'}],
  9. },
  10. archive_data_source_names={'postgresql_databases': ['other', 'foo', 'bar']},
  11. hook_name='postgresql_databases',
  12. data_source_name='bar',
  13. ) == ('postgresql_databases', {'name': 'bar'})
  14. def test_get_configured_data_source_matches_nothing_when_nothing_configured():
  15. assert module.get_configured_data_source(
  16. config={},
  17. archive_data_source_names={'postgresql_databases': ['foo']},
  18. hook_name='postgresql_databases',
  19. data_source_name='quux',
  20. ) == (None, None)
  21. def test_get_configured_data_source_matches_nothing_when_data_source_name_not_configured():
  22. assert module.get_configured_data_source(
  23. config={'postgresql_databases': [{'name': 'foo'}, {'name': 'bar'}]},
  24. archive_data_source_names={'postgresql_databases': ['foo']},
  25. hook_name='postgresql_databases',
  26. data_source_name='quux',
  27. ) == (None, None)
  28. def test_get_configured_data_source_matches_nothing_when_data_source_name_not_in_archive():
  29. assert module.get_configured_data_source(
  30. config={'postgresql_databases': [{'name': 'foo'}, {'name': 'bar'}]},
  31. archive_data_source_names={'postgresql_databases': ['bar']},
  32. hook_name='postgresql_databases',
  33. data_source_name='foo',
  34. ) == (None, None)
  35. def test_get_configured_data_source_matches_data_source_by_configuration_data_source_name():
  36. assert module.get_configured_data_source(
  37. config={'postgresql_databases': [{'name': 'all'}, {'name': 'bar'}]},
  38. archive_data_source_names={'postgresql_databases': ['foo']},
  39. hook_name='postgresql_databases',
  40. data_source_name='foo',
  41. configuration_data_source_name='all',
  42. ) == ('postgresql_databases', {'name': 'all'})
  43. def test_get_configured_data_source_with_unspecified_hook_matches_data_source_by_name():
  44. assert module.get_configured_data_source(
  45. config={
  46. 'other_databases': [{'name': 'other'}],
  47. 'postgresql_databases': [{'name': 'foo'}, {'name': 'bar'}],
  48. },
  49. archive_data_source_names={'postgresql_databases': ['other', 'foo', 'bar']},
  50. hook_name=module.UNSPECIFIED_HOOK,
  51. data_source_name='bar',
  52. ) == ('postgresql_databases', {'name': 'bar'})
  53. def test_strip_path_prefix_from_extracted_dump_destination_renames_first_matching_databases_subdirectory():
  54. flexmock(module.os).should_receive('walk').and_return(
  55. [
  56. ('/foo', flexmock(), flexmock()),
  57. ('/foo/bar', flexmock(), flexmock()),
  58. ('/foo/bar/postgresql_databases', flexmock(), flexmock()),
  59. ('/foo/bar/mariadb_databases', flexmock(), flexmock()),
  60. ]
  61. )
  62. flexmock(module.shutil).should_receive('move').with_args(
  63. '/foo/bar/postgresql_databases', '/run/user/0/borgmatic/postgresql_databases'
  64. ).once()
  65. flexmock(module.shutil).should_receive('move').with_args(
  66. '/foo/bar/mariadb_databases', '/run/user/0/borgmatic/mariadb_databases'
  67. ).never()
  68. module.strip_path_prefix_from_extracted_dump_destination('/foo', '/run/user/0/borgmatic')
  69. def test_restore_single_dump_extracts_and_restores_single_file_dump():
  70. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args(
  71. 'make_data_source_dump_patterns', object, object, object, object, object
  72. ).and_return({'postgresql': flexmock()})
  73. flexmock(module.tempfile).should_receive('mkdtemp').never()
  74. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  75. 'convert_glob_patterns_to_borg_pattern'
  76. ).and_return(flexmock())
  77. flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').and_return(
  78. flexmock()
  79. ).once()
  80. flexmock(module).should_receive('strip_path_prefix_from_extracted_dump_destination').never()
  81. flexmock(module.shutil).should_receive('rmtree').never()
  82. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').with_args(
  83. function_name='restore_data_source_dump',
  84. config=object,
  85. log_prefix=object,
  86. hook_name=object,
  87. data_source=object,
  88. dry_run=object,
  89. extract_process=object,
  90. connection_params=object,
  91. borgmatic_runtime_directory=object,
  92. ).once()
  93. module.restore_single_dump(
  94. repository={'path': 'test.borg'},
  95. config=flexmock(),
  96. local_borg_version=flexmock(),
  97. global_arguments=flexmock(dry_run=False),
  98. local_path=None,
  99. remote_path=None,
  100. archive_name=flexmock(),
  101. hook_name='postgresql',
  102. data_source={'name': 'test', 'format': 'plain'},
  103. connection_params=flexmock(),
  104. borgmatic_runtime_directory='/run/borgmatic',
  105. )
  106. def test_restore_single_dump_extracts_and_restores_directory_dump():
  107. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args(
  108. 'make_data_source_dump_patterns', object, object, object, object, object
  109. ).and_return({'postgresql': flexmock()})
  110. flexmock(module.tempfile).should_receive('mkdtemp').once().and_return(
  111. '/run/user/0/borgmatic/tmp1234'
  112. )
  113. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  114. 'convert_glob_patterns_to_borg_pattern'
  115. ).and_return(flexmock())
  116. flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').and_return(
  117. flexmock()
  118. ).once()
  119. flexmock(module).should_receive('strip_path_prefix_from_extracted_dump_destination').once()
  120. flexmock(module.shutil).should_receive('rmtree').once()
  121. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').with_args(
  122. function_name='restore_data_source_dump',
  123. config=object,
  124. log_prefix=object,
  125. hook_name=object,
  126. data_source=object,
  127. dry_run=object,
  128. extract_process=object,
  129. connection_params=object,
  130. borgmatic_runtime_directory='/run/borgmatic',
  131. ).once()
  132. module.restore_single_dump(
  133. repository={'path': 'test.borg'},
  134. config=flexmock(),
  135. local_borg_version=flexmock(),
  136. global_arguments=flexmock(dry_run=False),
  137. local_path=None,
  138. remote_path=None,
  139. archive_name=flexmock(),
  140. hook_name='postgresql',
  141. data_source={'name': 'test', 'format': 'directory'},
  142. connection_params=flexmock(),
  143. borgmatic_runtime_directory='/run/borgmatic',
  144. )
  145. def test_restore_single_dump_with_directory_dump_error_cleans_up_temporary_directory():
  146. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args(
  147. 'make_data_source_dump_patterns', object, object, object, object, object
  148. ).and_return({'postgresql': flexmock()})
  149. flexmock(module.tempfile).should_receive('mkdtemp').once().and_return(
  150. '/run/user/0/borgmatic/tmp1234'
  151. )
  152. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  153. 'convert_glob_patterns_to_borg_pattern'
  154. ).and_return(flexmock())
  155. flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').and_raise(
  156. ValueError
  157. ).once()
  158. flexmock(module).should_receive('strip_path_prefix_from_extracted_dump_destination').never()
  159. flexmock(module.shutil).should_receive('rmtree').once()
  160. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').with_args(
  161. function_name='restore_data_source_dump',
  162. config=object,
  163. log_prefix=object,
  164. hook_name=object,
  165. data_source=object,
  166. dry_run=object,
  167. extract_process=object,
  168. connection_params=object,
  169. borgmatic_runtime_directory='/run/user/0/borgmatic/tmp1234',
  170. ).never()
  171. with pytest.raises(ValueError):
  172. module.restore_single_dump(
  173. repository={'path': 'test.borg'},
  174. config=flexmock(),
  175. local_borg_version=flexmock(),
  176. global_arguments=flexmock(dry_run=False),
  177. local_path=None,
  178. remote_path=None,
  179. archive_name=flexmock(),
  180. hook_name='postgresql',
  181. data_source={'name': 'test', 'format': 'directory'},
  182. connection_params=flexmock(),
  183. borgmatic_runtime_directory='/run/borgmatic',
  184. )
  185. def test_restore_single_dump_with_directory_dump_and_dry_run_skips_directory_move_and_cleanup():
  186. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').with_args(
  187. 'make_data_source_dump_patterns', object, object, object, object, object
  188. ).and_return({'postgresql': flexmock()})
  189. flexmock(module.tempfile).should_receive('mkdtemp').once().and_return('/run/borgmatic/tmp1234')
  190. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  191. 'convert_glob_patterns_to_borg_pattern'
  192. ).and_return(flexmock())
  193. flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').and_return(
  194. flexmock()
  195. ).once()
  196. flexmock(module).should_receive('strip_path_prefix_from_extracted_dump_destination').never()
  197. flexmock(module.shutil).should_receive('rmtree').never()
  198. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hook').with_args(
  199. function_name='restore_data_source_dump',
  200. config=object,
  201. log_prefix=object,
  202. hook_name=object,
  203. data_source=object,
  204. dry_run=object,
  205. extract_process=object,
  206. connection_params=object,
  207. borgmatic_runtime_directory='/run/borgmatic',
  208. ).once()
  209. module.restore_single_dump(
  210. repository={'path': 'test.borg'},
  211. config=flexmock(),
  212. local_borg_version=flexmock(),
  213. global_arguments=flexmock(dry_run=True),
  214. local_path=None,
  215. remote_path=None,
  216. archive_name=flexmock(),
  217. hook_name='postgresql',
  218. data_source={'name': 'test', 'format': 'directory'},
  219. connection_params=flexmock(),
  220. borgmatic_runtime_directory='/run/borgmatic',
  221. )
  222. def test_collect_dumps_from_archive_parses_archive_paths():
  223. flexmock(module.borgmatic.config.paths).should_receive(
  224. 'get_borgmatic_source_directory'
  225. ).and_return('/root/.borgmatic')
  226. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  227. 'make_data_source_dump_path'
  228. ).and_return('')
  229. flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
  230. [
  231. 'borgmatic/postgresql_databases/localhost/foo',
  232. 'borgmatic/postgresql_databases/localhost/bar',
  233. 'borgmatic/mysql_databases/localhost/quux',
  234. ]
  235. )
  236. archive_data_source_names = module.collect_dumps_from_archive(
  237. repository={'path': 'repo'},
  238. archive='archive',
  239. config={},
  240. local_borg_version=flexmock(),
  241. global_arguments=flexmock(log_json=False),
  242. local_path=flexmock(),
  243. remote_path=flexmock(),
  244. borgmatic_runtime_directory='/run/borgmatic',
  245. )
  246. assert archive_data_source_names == {
  247. 'postgresql_databases': ['foo', 'bar'],
  248. 'mysql_databases': ['quux'],
  249. }
  250. def test_collect_dumps_from_archive_parses_archive_paths_with_different_base_directories():
  251. flexmock(module.borgmatic.config.paths).should_receive(
  252. 'get_borgmatic_source_directory'
  253. ).and_return('/root/.borgmatic')
  254. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  255. 'make_data_source_dump_path'
  256. ).and_return('')
  257. flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
  258. [
  259. 'borgmatic/postgresql_databases/localhost/foo',
  260. '.borgmatic/postgresql_databases/localhost/bar',
  261. '/root/.borgmatic/postgresql_databases/localhost/baz',
  262. '/var/run/0/borgmatic/mysql_databases/localhost/quux',
  263. ]
  264. )
  265. archive_data_source_names = module.collect_dumps_from_archive(
  266. repository={'path': 'repo'},
  267. archive='archive',
  268. config={},
  269. local_borg_version=flexmock(),
  270. global_arguments=flexmock(log_json=False),
  271. local_path=flexmock(),
  272. remote_path=flexmock(),
  273. borgmatic_runtime_directory='/run/borgmatic',
  274. )
  275. assert archive_data_source_names == {
  276. 'postgresql_databases': ['foo', 'bar', 'baz'],
  277. 'mysql_databases': ['quux'],
  278. }
  279. def test_collect_dumps_from_archive_parses_directory_format_archive_paths():
  280. flexmock(module.borgmatic.config.paths).should_receive(
  281. 'get_borgmatic_source_directory'
  282. ).and_return('/root/.borgmatic')
  283. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  284. 'make_data_source_dump_path'
  285. ).and_return('')
  286. flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
  287. [
  288. 'borgmatic/postgresql_databases/localhost/foo/table1',
  289. 'borgmatic/postgresql_databases/localhost/foo/table2',
  290. ]
  291. )
  292. archive_data_source_names = module.collect_dumps_from_archive(
  293. repository={'path': 'repo'},
  294. archive='archive',
  295. config={},
  296. local_borg_version=flexmock(),
  297. global_arguments=flexmock(log_json=False),
  298. local_path=flexmock(),
  299. remote_path=flexmock(),
  300. borgmatic_runtime_directory='/run/borgmatic',
  301. )
  302. assert archive_data_source_names == {
  303. 'postgresql_databases': ['foo'],
  304. }
  305. def test_collect_dumps_from_archive_skips_bad_archive_paths():
  306. flexmock(module.borgmatic.config.paths).should_receive(
  307. 'get_borgmatic_source_directory'
  308. ).and_return('/root/.borgmatic')
  309. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  310. 'make_data_source_dump_path'
  311. ).and_return('')
  312. flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
  313. [
  314. 'borgmatic/postgresql_databases/localhost/foo',
  315. 'borgmatic/invalid',
  316. 'invalid/as/well',
  317. '',
  318. ]
  319. )
  320. archive_data_source_names = module.collect_dumps_from_archive(
  321. repository={'path': 'repo'},
  322. archive='archive',
  323. config={},
  324. local_borg_version=flexmock(),
  325. global_arguments=flexmock(log_json=False),
  326. local_path=flexmock(),
  327. remote_path=flexmock(),
  328. borgmatic_runtime_directory='/run/borgmatic',
  329. )
  330. assert archive_data_source_names == {
  331. 'postgresql_databases': ['foo'],
  332. }
  333. def test_get_dumps_to_restore_passes_through_requested_names_found_in_archive():
  334. restore_names = module.get_dumps_to_restore(
  335. requested_data_source_names=['foo', 'bar'],
  336. archive_data_source_names={'postresql_databases': ['foo', 'bar', 'baz']},
  337. )
  338. assert restore_names == {module.UNSPECIFIED_HOOK: ['foo', 'bar']}
  339. def test_get_dumps_to_restore_raises_for_requested_names_missing_from_archive():
  340. with pytest.raises(ValueError):
  341. module.get_dumps_to_restore(
  342. requested_data_source_names=['foo', 'bar'],
  343. archive_data_source_names={'postresql_databases': ['foo']},
  344. )
  345. def test_get_dumps_to_restore_without_requested_names_finds_all_archive_data_sources():
  346. archive_data_source_names = {'postresql_databases': ['foo', 'bar']}
  347. restore_names = module.get_dumps_to_restore(
  348. requested_data_source_names=[],
  349. archive_data_source_names=archive_data_source_names,
  350. )
  351. assert restore_names == archive_data_source_names
  352. def test_get_dumps_to_restore_with_all_in_requested_names_finds_all_archive_data_sources():
  353. archive_data_source_names = {'postresql_databases': ['foo', 'bar']}
  354. restore_names = module.get_dumps_to_restore(
  355. requested_data_source_names=['all'],
  356. archive_data_source_names=archive_data_source_names,
  357. )
  358. assert restore_names == archive_data_source_names
  359. def test_get_dumps_to_restore_with_all_in_requested_names_plus_additional_requested_names_omits_duplicates():
  360. archive_data_source_names = {'postresql_databases': ['foo', 'bar']}
  361. restore_names = module.get_dumps_to_restore(
  362. requested_data_source_names=['all', 'foo', 'bar'],
  363. archive_data_source_names=archive_data_source_names,
  364. )
  365. assert restore_names == archive_data_source_names
  366. def test_get_dumps_to_restore_raises_for_all_in_requested_names_and_requested_named_missing_from_archives():
  367. with pytest.raises(ValueError):
  368. module.get_dumps_to_restore(
  369. requested_data_source_names=['all', 'foo', 'bar'],
  370. archive_data_source_names={'postresql_databases': ['foo']},
  371. )
  372. def test_ensure_requested_dumps_restored_with_all_data_sources_found_does_not_raise():
  373. module.ensure_requested_dumps_restored(
  374. restore_names={'postgresql_databases': ['foo']},
  375. remaining_restore_names={'postgresql_databases': ['bar']},
  376. found_names=['foo', 'bar'],
  377. )
  378. def test_ensure_requested_dumps_restored_with_no_data_sources_raises():
  379. with pytest.raises(ValueError):
  380. module.ensure_requested_dumps_restored(
  381. restore_names={'postgresql_databases': []},
  382. remaining_restore_names={},
  383. found_names=[],
  384. )
  385. def test_ensure_requested_dumps_restored_with_missing_data_sources_raises():
  386. with pytest.raises(ValueError):
  387. module.ensure_requested_dumps_restored(
  388. restore_names={'postgresql_databases': ['foo']},
  389. remaining_restore_names={'postgresql_databases': ['bar']},
  390. found_names=['foo'],
  391. )
  392. def test_run_restore_restores_each_data_source():
  393. restore_names = {
  394. 'postgresql_databases': ['foo', 'bar'],
  395. }
  396. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
  397. borgmatic_runtime_directory = flexmock()
  398. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  399. borgmatic_runtime_directory
  400. )
  401. flexmock(module.borgmatic.config.paths).should_receive(
  402. 'make_runtime_directory_glob'
  403. ).replace_with(lambda path: path)
  404. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
  405. flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
  406. flexmock()
  407. )
  408. flexmock(module).should_receive('collect_dumps_from_archive').and_return(flexmock())
  409. flexmock(module).should_receive('get_dumps_to_restore').and_return(restore_names)
  410. flexmock(module).should_receive('get_configured_data_source').and_return(
  411. ('postgresql_databases', {'name': 'foo'})
  412. ).and_return(('postgresql_databases', {'name': 'bar'}))
  413. flexmock(module).should_receive('restore_single_dump').with_args(
  414. repository=object,
  415. config=object,
  416. local_borg_version=object,
  417. global_arguments=object,
  418. local_path=object,
  419. remote_path=object,
  420. archive_name=object,
  421. hook_name='postgresql_databases',
  422. data_source={'name': 'foo', 'schemas': None},
  423. connection_params=object,
  424. borgmatic_runtime_directory=borgmatic_runtime_directory,
  425. ).once()
  426. flexmock(module).should_receive('restore_single_dump').with_args(
  427. repository=object,
  428. config=object,
  429. local_borg_version=object,
  430. global_arguments=object,
  431. local_path=object,
  432. remote_path=object,
  433. archive_name=object,
  434. hook_name='postgresql_databases',
  435. data_source={'name': 'bar', 'schemas': None},
  436. connection_params=object,
  437. borgmatic_runtime_directory=borgmatic_runtime_directory,
  438. ).once()
  439. flexmock(module).should_receive('ensure_requested_dumps_restored')
  440. module.run_restore(
  441. repository={'path': 'repo'},
  442. config=flexmock(),
  443. local_borg_version=flexmock(),
  444. restore_arguments=flexmock(
  445. repository='repo',
  446. archive='archive',
  447. data_sources=flexmock(),
  448. schemas=None,
  449. hostname=None,
  450. port=None,
  451. username=None,
  452. password=None,
  453. restore_path=None,
  454. ),
  455. global_arguments=flexmock(dry_run=False),
  456. local_path=flexmock(),
  457. remote_path=flexmock(),
  458. )
  459. def test_run_restore_bails_for_non_matching_repository():
  460. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(
  461. False
  462. )
  463. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  464. flexmock()
  465. )
  466. flexmock(module.borgmatic.config.paths).should_receive(
  467. 'make_runtime_directory_glob'
  468. ).replace_with(lambda path: path)
  469. flexmock(module.borgmatic.hooks.dispatch).should_receive(
  470. 'call_hooks_even_if_unconfigured'
  471. ).never()
  472. flexmock(module).should_receive('restore_single_dump').never()
  473. module.run_restore(
  474. repository={'path': 'repo'},
  475. config=flexmock(),
  476. local_borg_version=flexmock(),
  477. restore_arguments=flexmock(repository='repo', archive='archive', data_sources=flexmock()),
  478. global_arguments=flexmock(dry_run=False),
  479. local_path=flexmock(),
  480. remote_path=flexmock(),
  481. )
  482. def test_run_restore_restores_data_source_configured_with_all_name():
  483. restore_names = {
  484. 'postgresql_databases': ['foo', 'bar'],
  485. }
  486. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
  487. borgmatic_runtime_directory = flexmock()
  488. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  489. borgmatic_runtime_directory
  490. )
  491. flexmock(module.borgmatic.config.paths).should_receive(
  492. 'make_runtime_directory_glob'
  493. ).replace_with(lambda path: path)
  494. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
  495. flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
  496. flexmock()
  497. )
  498. flexmock(module).should_receive('collect_dumps_from_archive').and_return(flexmock())
  499. flexmock(module).should_receive('get_dumps_to_restore').and_return(restore_names)
  500. flexmock(module).should_receive('get_configured_data_source').with_args(
  501. config=object,
  502. archive_data_source_names=object,
  503. hook_name='postgresql_databases',
  504. data_source_name='foo',
  505. ).and_return(('postgresql_databases', {'name': 'foo'}))
  506. flexmock(module).should_receive('get_configured_data_source').with_args(
  507. config=object,
  508. archive_data_source_names=object,
  509. hook_name='postgresql_databases',
  510. data_source_name='bar',
  511. ).and_return((None, None))
  512. flexmock(module).should_receive('get_configured_data_source').with_args(
  513. config=object,
  514. archive_data_source_names=object,
  515. hook_name='postgresql_databases',
  516. data_source_name='bar',
  517. configuration_data_source_name='all',
  518. ).and_return(('postgresql_databases', {'name': 'bar'}))
  519. flexmock(module).should_receive('restore_single_dump').with_args(
  520. repository=object,
  521. config=object,
  522. local_borg_version=object,
  523. global_arguments=object,
  524. local_path=object,
  525. remote_path=object,
  526. archive_name=object,
  527. hook_name='postgresql_databases',
  528. data_source={'name': 'foo', 'schemas': None},
  529. connection_params=object,
  530. borgmatic_runtime_directory=borgmatic_runtime_directory,
  531. ).once()
  532. flexmock(module).should_receive('restore_single_dump').with_args(
  533. repository=object,
  534. config=object,
  535. local_borg_version=object,
  536. global_arguments=object,
  537. local_path=object,
  538. remote_path=object,
  539. archive_name=object,
  540. hook_name='postgresql_databases',
  541. data_source={'name': 'bar', 'schemas': None},
  542. connection_params=object,
  543. borgmatic_runtime_directory=borgmatic_runtime_directory,
  544. ).once()
  545. flexmock(module).should_receive('ensure_requested_dumps_restored')
  546. module.run_restore(
  547. repository={'path': 'repo'},
  548. config=flexmock(),
  549. local_borg_version=flexmock(),
  550. restore_arguments=flexmock(
  551. repository='repo',
  552. archive='archive',
  553. data_sources=flexmock(),
  554. schemas=None,
  555. hostname=None,
  556. port=None,
  557. username=None,
  558. password=None,
  559. restore_path=None,
  560. ),
  561. global_arguments=flexmock(dry_run=False),
  562. local_path=flexmock(),
  563. remote_path=flexmock(),
  564. )
  565. def test_run_restore_skips_missing_data_source():
  566. restore_names = {
  567. 'postgresql_databases': ['foo', 'bar'],
  568. }
  569. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
  570. borgmatic_runtime_directory = flexmock()
  571. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  572. borgmatic_runtime_directory
  573. )
  574. flexmock(module.borgmatic.config.paths).should_receive(
  575. 'make_runtime_directory_glob'
  576. ).replace_with(lambda path: path)
  577. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
  578. flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
  579. flexmock()
  580. )
  581. flexmock(module).should_receive('collect_dumps_from_archive').and_return(flexmock())
  582. flexmock(module).should_receive('get_dumps_to_restore').and_return(restore_names)
  583. flexmock(module).should_receive('get_configured_data_source').with_args(
  584. config=object,
  585. archive_data_source_names=object,
  586. hook_name='postgresql_databases',
  587. data_source_name='foo',
  588. ).and_return(('postgresql_databases', {'name': 'foo'}))
  589. flexmock(module).should_receive('get_configured_data_source').with_args(
  590. config=object,
  591. archive_data_source_names=object,
  592. hook_name='postgresql_databases',
  593. data_source_name='bar',
  594. ).and_return((None, None))
  595. flexmock(module).should_receive('get_configured_data_source').with_args(
  596. config=object,
  597. archive_data_source_names=object,
  598. hook_name='postgresql_databases',
  599. data_source_name='bar',
  600. configuration_data_source_name='all',
  601. ).and_return((None, None))
  602. flexmock(module).should_receive('restore_single_dump').with_args(
  603. repository=object,
  604. config=object,
  605. local_borg_version=object,
  606. global_arguments=object,
  607. local_path=object,
  608. remote_path=object,
  609. archive_name=object,
  610. hook_name='postgresql_databases',
  611. data_source={'name': 'foo', 'schemas': None},
  612. connection_params=object,
  613. borgmatic_runtime_directory=borgmatic_runtime_directory,
  614. ).once()
  615. flexmock(module).should_receive('restore_single_dump').with_args(
  616. repository=object,
  617. config=object,
  618. local_borg_version=object,
  619. global_arguments=object,
  620. local_path=object,
  621. remote_path=object,
  622. archive_name=object,
  623. hook_name='postgresql_databases',
  624. data_source={'name': 'bar', 'schemas': None},
  625. connection_params=object,
  626. borgmatic_runtime_directory=borgmatic_runtime_directory,
  627. ).never()
  628. flexmock(module).should_receive('ensure_requested_dumps_restored')
  629. module.run_restore(
  630. repository={'path': 'repo'},
  631. config=flexmock(),
  632. local_borg_version=flexmock(),
  633. restore_arguments=flexmock(
  634. repository='repo',
  635. archive='archive',
  636. data_sources=flexmock(),
  637. schemas=None,
  638. hostname=None,
  639. port=None,
  640. username=None,
  641. password=None,
  642. restore_path=None,
  643. ),
  644. global_arguments=flexmock(dry_run=False),
  645. local_path=flexmock(),
  646. remote_path=flexmock(),
  647. )
  648. def test_run_restore_restores_data_sources_from_different_hooks():
  649. restore_names = {
  650. 'postgresql_databases': ['foo'],
  651. 'mysql_databases': ['bar'],
  652. }
  653. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
  654. borgmatic_runtime_directory = flexmock()
  655. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  656. borgmatic_runtime_directory
  657. )
  658. flexmock(module.borgmatic.config.paths).should_receive(
  659. 'make_runtime_directory_glob'
  660. ).replace_with(lambda path: path)
  661. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
  662. flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
  663. flexmock()
  664. )
  665. flexmock(module).should_receive('collect_dumps_from_archive').and_return(flexmock())
  666. flexmock(module).should_receive('get_dumps_to_restore').and_return(restore_names)
  667. flexmock(module).should_receive('get_configured_data_source').with_args(
  668. config=object,
  669. archive_data_source_names=object,
  670. hook_name='postgresql_databases',
  671. data_source_name='foo',
  672. ).and_return(('postgresql_databases', {'name': 'foo'}))
  673. flexmock(module).should_receive('get_configured_data_source').with_args(
  674. config=object,
  675. archive_data_source_names=object,
  676. hook_name='mysql_databases',
  677. data_source_name='bar',
  678. ).and_return(('mysql_databases', {'name': 'bar'}))
  679. flexmock(module).should_receive('restore_single_dump').with_args(
  680. repository=object,
  681. config=object,
  682. local_borg_version=object,
  683. global_arguments=object,
  684. local_path=object,
  685. remote_path=object,
  686. archive_name=object,
  687. hook_name='postgresql_databases',
  688. data_source={'name': 'foo', 'schemas': None},
  689. connection_params=object,
  690. borgmatic_runtime_directory=borgmatic_runtime_directory,
  691. ).once()
  692. flexmock(module).should_receive('restore_single_dump').with_args(
  693. repository=object,
  694. config=object,
  695. local_borg_version=object,
  696. global_arguments=object,
  697. local_path=object,
  698. remote_path=object,
  699. archive_name=object,
  700. hook_name='mysql_databases',
  701. data_source={'name': 'bar', 'schemas': None},
  702. connection_params=object,
  703. borgmatic_runtime_directory=borgmatic_runtime_directory,
  704. ).once()
  705. flexmock(module).should_receive('ensure_requested_dumps_restored')
  706. module.run_restore(
  707. repository={'path': 'repo'},
  708. config=flexmock(),
  709. local_borg_version=flexmock(),
  710. restore_arguments=flexmock(
  711. repository='repo',
  712. archive='archive',
  713. data_sources=flexmock(),
  714. schemas=None,
  715. hostname=None,
  716. port=None,
  717. username=None,
  718. password=None,
  719. restore_path=None,
  720. ),
  721. global_arguments=flexmock(dry_run=False),
  722. local_path=flexmock(),
  723. remote_path=flexmock(),
  724. )