test_restore.py 33 KB

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