test_restore.py 30 KB

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