test_restore.py 52 KB

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