test_restore.py 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299
  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_parses_archive_paths():
  431. flexmock(module.borgmatic.config.paths).should_receive(
  432. 'get_borgmatic_source_directory',
  433. ).and_return('/root/.borgmatic')
  434. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  435. 'make_data_source_dump_path',
  436. ).and_return('')
  437. flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
  438. [
  439. 'borgmatic/postgresql_databases/localhost/foo',
  440. 'borgmatic/postgresql_databases/host:1234/bar',
  441. 'borgmatic/mysql_databases/localhost/quux',
  442. ],
  443. )
  444. archive_dumps = module.collect_dumps_from_archive(
  445. repository={'path': 'repo'},
  446. archive='archive',
  447. config={},
  448. local_borg_version=flexmock(),
  449. global_arguments=flexmock(log_json=False),
  450. local_path=flexmock(),
  451. remote_path=flexmock(),
  452. borgmatic_runtime_directory='/run/borgmatic',
  453. )
  454. assert archive_dumps == {
  455. module.Dump('postgresql_databases', 'foo'),
  456. module.Dump('postgresql_databases', 'bar', 'host', 1234),
  457. module.Dump('mysql_databases', 'quux'),
  458. }
  459. def test_collect_dumps_from_archive_parses_archive_paths_with_different_base_directories():
  460. flexmock(module.borgmatic.config.paths).should_receive(
  461. 'get_borgmatic_source_directory',
  462. ).and_return('/root/.borgmatic')
  463. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  464. 'make_data_source_dump_path',
  465. ).and_return('')
  466. flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
  467. [
  468. 'borgmatic/postgresql_databases/localhost/foo',
  469. '.borgmatic/postgresql_databases/localhost/bar',
  470. '/root/.borgmatic/postgresql_databases/localhost/baz',
  471. '/var/run/0/borgmatic/mysql_databases/localhost/quux',
  472. ],
  473. )
  474. archive_dumps = module.collect_dumps_from_archive(
  475. repository={'path': 'repo'},
  476. archive='archive',
  477. config={},
  478. local_borg_version=flexmock(),
  479. global_arguments=flexmock(log_json=False),
  480. local_path=flexmock(),
  481. remote_path=flexmock(),
  482. borgmatic_runtime_directory='/run/borgmatic',
  483. )
  484. assert archive_dumps == {
  485. module.Dump('postgresql_databases', 'foo'),
  486. module.Dump('postgresql_databases', 'bar'),
  487. module.Dump('postgresql_databases', 'baz'),
  488. module.Dump('mysql_databases', 'quux'),
  489. }
  490. def test_collect_dumps_from_archive_parses_directory_format_archive_paths():
  491. flexmock(module.borgmatic.config.paths).should_receive(
  492. 'get_borgmatic_source_directory',
  493. ).and_return('/root/.borgmatic')
  494. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  495. 'make_data_source_dump_path',
  496. ).and_return('')
  497. flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
  498. [
  499. 'borgmatic/postgresql_databases/localhost/foo/table1',
  500. 'borgmatic/postgresql_databases/localhost/foo/table2',
  501. ],
  502. )
  503. archive_dumps = module.collect_dumps_from_archive(
  504. repository={'path': 'repo'},
  505. archive='archive',
  506. config={},
  507. local_borg_version=flexmock(),
  508. global_arguments=flexmock(log_json=False),
  509. local_path=flexmock(),
  510. remote_path=flexmock(),
  511. borgmatic_runtime_directory='/run/borgmatic',
  512. )
  513. assert archive_dumps == {
  514. module.Dump('postgresql_databases', 'foo'),
  515. }
  516. def test_collect_dumps_from_archive_skips_bad_archive_paths_or_bad_path_components():
  517. flexmock(module.borgmatic.config.paths).should_receive(
  518. 'get_borgmatic_source_directory',
  519. ).and_return('/root/.borgmatic')
  520. flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
  521. 'make_data_source_dump_path',
  522. ).and_return('')
  523. flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
  524. [
  525. 'borgmatic/postgresql_databases/localhost/foo',
  526. 'borgmatic/postgresql_databases/localhost:abcd/bar',
  527. 'borgmatic/invalid',
  528. 'invalid/as/well',
  529. '',
  530. ],
  531. )
  532. archive_dumps = module.collect_dumps_from_archive(
  533. repository={'path': 'repo'},
  534. archive='archive',
  535. config={},
  536. local_borg_version=flexmock(),
  537. global_arguments=flexmock(log_json=False),
  538. local_path=flexmock(),
  539. remote_path=flexmock(),
  540. borgmatic_runtime_directory='/run/borgmatic',
  541. )
  542. assert archive_dumps == {
  543. module.Dump('postgresql_databases', 'foo'),
  544. module.Dump('postgresql_databases', 'bar'),
  545. }
  546. def test_get_dumps_to_restore_gets_requested_dumps_found_in_archive():
  547. dumps_from_archive = {
  548. module.Dump('postgresql_databases', 'foo'),
  549. module.Dump('postgresql_databases', 'bar'),
  550. module.Dump('postgresql_databases', 'baz'),
  551. }
  552. flexmock(module).should_receive('dumps_match').and_return(False)
  553. flexmock(module).should_receive('dumps_match').with_args(
  554. module.Dump(module.UNSPECIFIED, 'foo', hostname=module.UNSPECIFIED),
  555. module.Dump('postgresql_databases', 'foo'),
  556. ).and_return(True)
  557. flexmock(module).should_receive('dumps_match').with_args(
  558. module.Dump(module.UNSPECIFIED, 'bar', hostname=module.UNSPECIFIED),
  559. module.Dump('postgresql_databases', 'bar'),
  560. ).and_return(True)
  561. assert module.get_dumps_to_restore(
  562. restore_arguments=flexmock(
  563. hook=None,
  564. data_sources=['foo', 'bar'],
  565. original_hostname=None,
  566. original_port=None,
  567. ),
  568. dumps_from_archive=dumps_from_archive,
  569. ) == {
  570. module.Dump('postgresql_databases', 'foo'),
  571. module.Dump('postgresql_databases', 'bar'),
  572. }
  573. def test_get_dumps_to_restore_raises_for_requested_dumps_missing_from_archive():
  574. dumps_from_archive = {
  575. module.Dump('postgresql_databases', 'foo'),
  576. }
  577. flexmock(module).should_receive('dumps_match').and_return(False)
  578. flexmock(module).should_receive('render_dump_metadata').and_return('test')
  579. with pytest.raises(ValueError):
  580. module.get_dumps_to_restore(
  581. restore_arguments=flexmock(
  582. hook=None,
  583. data_sources=['foo', 'bar'],
  584. original_hostname=None,
  585. original_port=None,
  586. ),
  587. dumps_from_archive=dumps_from_archive,
  588. )
  589. def test_get_dumps_to_restore_without_requested_dumps_finds_all_archive_dumps():
  590. dumps_from_archive = {
  591. module.Dump('postgresql_databases', 'foo'),
  592. module.Dump('postgresql_databases', 'bar'),
  593. }
  594. flexmock(module).should_receive('dumps_match').and_return(False)
  595. assert (
  596. module.get_dumps_to_restore(
  597. restore_arguments=flexmock(
  598. hook=None,
  599. data_sources=[],
  600. original_hostname=None,
  601. original_port=None,
  602. ),
  603. dumps_from_archive=dumps_from_archive,
  604. )
  605. == dumps_from_archive
  606. )
  607. def test_get_dumps_to_restore_with_all_in_requested_dumps_finds_all_archive_dumps():
  608. dumps_from_archive = {
  609. module.Dump('postgresql_databases', 'foo'),
  610. module.Dump('postgresql_databases', 'bar'),
  611. }
  612. flexmock(module).should_receive('dumps_match').and_return(False)
  613. flexmock(module).should_receive('dumps_match').with_args(
  614. module.Dump(module.UNSPECIFIED, 'foo', hostname=module.UNSPECIFIED),
  615. module.Dump('postgresql_databases', 'foo'),
  616. ).and_return(True)
  617. flexmock(module).should_receive('dumps_match').with_args(
  618. module.Dump(module.UNSPECIFIED, 'bar', hostname=module.UNSPECIFIED),
  619. module.Dump('postgresql_databases', 'bar'),
  620. ).and_return(True)
  621. assert (
  622. module.get_dumps_to_restore(
  623. restore_arguments=flexmock(
  624. hook=None,
  625. data_sources=['all'],
  626. original_hostname=None,
  627. original_port=None,
  628. ),
  629. dumps_from_archive=dumps_from_archive,
  630. )
  631. == dumps_from_archive
  632. )
  633. def test_get_dumps_to_restore_with_all_in_requested_dumps_plus_additional_requested_dumps_omits_duplicates():
  634. dumps_from_archive = {
  635. module.Dump('postgresql_databases', 'foo'),
  636. module.Dump('postgresql_databases', 'bar'),
  637. }
  638. flexmock(module).should_receive('dumps_match').and_return(False)
  639. flexmock(module).should_receive('dumps_match').with_args(
  640. module.Dump(module.UNSPECIFIED, 'foo', hostname=module.UNSPECIFIED),
  641. module.Dump('postgresql_databases', 'foo'),
  642. ).and_return(True)
  643. flexmock(module).should_receive('dumps_match').with_args(
  644. module.Dump(module.UNSPECIFIED, 'bar', hostname=module.UNSPECIFIED),
  645. module.Dump('postgresql_databases', 'bar'),
  646. ).and_return(True)
  647. assert (
  648. module.get_dumps_to_restore(
  649. restore_arguments=flexmock(
  650. hook=None,
  651. data_sources=['all', 'foo', 'bar'],
  652. original_hostname=None,
  653. original_port=None,
  654. ),
  655. dumps_from_archive=dumps_from_archive,
  656. )
  657. == dumps_from_archive
  658. )
  659. def test_get_dumps_to_restore_raises_for_multiple_matching_dumps_in_archive():
  660. flexmock(module).should_receive('dumps_match').and_return(False)
  661. flexmock(module).should_receive('dumps_match').with_args(
  662. module.Dump(module.UNSPECIFIED, 'foo', hostname=module.UNSPECIFIED),
  663. module.Dump('postgresql_databases', 'foo'),
  664. ).and_return(True)
  665. flexmock(module).should_receive('dumps_match').with_args(
  666. module.Dump(module.UNSPECIFIED, 'foo', hostname=module.UNSPECIFIED),
  667. module.Dump('mariadb_databases', 'foo'),
  668. ).and_return(True)
  669. flexmock(module).should_receive('render_dump_metadata').and_return('test')
  670. with pytest.raises(ValueError):
  671. module.get_dumps_to_restore(
  672. restore_arguments=flexmock(
  673. hook=None,
  674. data_sources=['foo'],
  675. original_hostname=None,
  676. original_port=None,
  677. ),
  678. dumps_from_archive={
  679. module.Dump('postgresql_databases', 'foo'),
  680. module.Dump('mariadb_databases', 'foo'),
  681. },
  682. )
  683. def test_get_dumps_to_restore_raises_for_all_in_requested_dumps_and_requested_dumps_missing_from_archive():
  684. flexmock(module).should_receive('dumps_match').and_return(False)
  685. flexmock(module).should_receive('dumps_match').with_args(
  686. module.Dump(module.UNSPECIFIED, 'foo', hostname=module.UNSPECIFIED),
  687. module.Dump('postgresql_databases', 'foo'),
  688. ).and_return(True)
  689. flexmock(module).should_receive('render_dump_metadata').and_return('test')
  690. with pytest.raises(ValueError):
  691. module.get_dumps_to_restore(
  692. restore_arguments=flexmock(
  693. hook=None,
  694. data_sources=['all', 'foo', 'bar'],
  695. original_hostname=None,
  696. original_port=None,
  697. ),
  698. dumps_from_archive={module.Dump('postresql_databases', 'foo')},
  699. )
  700. def test_get_dumps_to_restore_with_requested_hook_name_filters_dumps_found_in_archive():
  701. dumps_from_archive = {
  702. module.Dump('mariadb_databases', 'foo'),
  703. module.Dump('postgresql_databases', 'foo'),
  704. module.Dump('sqlite_databases', 'bar'),
  705. }
  706. flexmock(module).should_receive('dumps_match').and_return(False)
  707. flexmock(module).should_receive('dumps_match').with_args(
  708. module.Dump('postgresql_databases', 'foo', hostname=module.UNSPECIFIED),
  709. module.Dump('postgresql_databases', 'foo'),
  710. ).and_return(True)
  711. assert module.get_dumps_to_restore(
  712. restore_arguments=flexmock(
  713. hook='postgresql_databases',
  714. data_sources=['foo'],
  715. original_hostname=None,
  716. original_port=None,
  717. ),
  718. dumps_from_archive=dumps_from_archive,
  719. ) == {
  720. module.Dump('postgresql_databases', 'foo'),
  721. }
  722. def test_get_dumps_to_restore_with_requested_shortened_hook_name_filters_dumps_found_in_archive():
  723. dumps_from_archive = {
  724. module.Dump('mariadb_databases', 'foo'),
  725. module.Dump('postgresql_databases', 'foo'),
  726. module.Dump('sqlite_databases', 'bar'),
  727. }
  728. flexmock(module).should_receive('dumps_match').and_return(False)
  729. flexmock(module).should_receive('dumps_match').with_args(
  730. module.Dump('postgresql_databases', 'foo', hostname=module.UNSPECIFIED),
  731. module.Dump('postgresql_databases', 'foo'),
  732. ).and_return(True)
  733. assert module.get_dumps_to_restore(
  734. restore_arguments=flexmock(
  735. hook='postgresql',
  736. data_sources=['foo'],
  737. original_hostname=None,
  738. original_port=None,
  739. ),
  740. dumps_from_archive=dumps_from_archive,
  741. ) == {
  742. module.Dump('postgresql_databases', 'foo'),
  743. }
  744. def test_get_dumps_to_restore_with_requested_hostname_filters_dumps_found_in_archive():
  745. dumps_from_archive = {
  746. module.Dump('postgresql_databases', 'foo'),
  747. module.Dump('postgresql_databases', 'foo', 'host'),
  748. module.Dump('postgresql_databases', 'bar'),
  749. }
  750. flexmock(module).should_receive('dumps_match').and_return(False)
  751. flexmock(module).should_receive('dumps_match').with_args(
  752. module.Dump('postgresql_databases', 'foo', 'host'),
  753. module.Dump('postgresql_databases', 'foo', 'host'),
  754. ).and_return(True)
  755. assert module.get_dumps_to_restore(
  756. restore_arguments=flexmock(
  757. hook='postgresql_databases',
  758. data_sources=['foo'],
  759. original_hostname='host',
  760. original_port=None,
  761. ),
  762. dumps_from_archive=dumps_from_archive,
  763. ) == {
  764. module.Dump('postgresql_databases', 'foo', 'host'),
  765. }
  766. def test_get_dumps_to_restore_with_requested_port_filters_dumps_found_in_archive():
  767. dumps_from_archive = {
  768. module.Dump('postgresql_databases', 'foo', 'host'),
  769. module.Dump('postgresql_databases', 'foo', 'host', 1234),
  770. module.Dump('postgresql_databases', 'bar'),
  771. }
  772. flexmock(module).should_receive('dumps_match').and_return(False)
  773. flexmock(module).should_receive('dumps_match').with_args(
  774. module.Dump('postgresql_databases', 'foo', 'host', 1234),
  775. module.Dump('postgresql_databases', 'foo', 'host', 1234),
  776. ).and_return(True)
  777. assert module.get_dumps_to_restore(
  778. restore_arguments=flexmock(
  779. hook='postgresql_databases',
  780. data_sources=['foo'],
  781. original_hostname='host',
  782. original_port=1234,
  783. ),
  784. dumps_from_archive=dumps_from_archive,
  785. ) == {
  786. module.Dump('postgresql_databases', 'foo', 'host', 1234),
  787. }
  788. def test_ensure_requested_dumps_restored_with_all_dumps_restored_does_not_raise():
  789. module.ensure_requested_dumps_restored(
  790. dumps_to_restore={
  791. module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  792. module.Dump(hook_name='postgresql_databases', data_source_name='bar'),
  793. },
  794. dumps_actually_restored={
  795. module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  796. module.Dump(hook_name='postgresql_databases', data_source_name='bar'),
  797. },
  798. )
  799. def test_ensure_requested_dumps_restored_with_no_dumps_raises():
  800. with pytest.raises(ValueError):
  801. module.ensure_requested_dumps_restored(
  802. dumps_to_restore={},
  803. dumps_actually_restored={},
  804. )
  805. def test_ensure_requested_dumps_restored_with_missing_dumps_raises():
  806. flexmock(module).should_receive('render_dump_metadata').and_return('test')
  807. with pytest.raises(ValueError):
  808. module.ensure_requested_dumps_restored(
  809. dumps_to_restore={
  810. module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  811. },
  812. dumps_actually_restored={
  813. module.Dump(hook_name='postgresql_databases', data_source_name='bar'),
  814. },
  815. )
  816. def test_run_restore_restores_each_data_source():
  817. dumps_to_restore = {
  818. module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  819. module.Dump(hook_name='postgresql_databases', data_source_name='bar'),
  820. }
  821. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
  822. borgmatic_runtime_directory = flexmock()
  823. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  824. borgmatic_runtime_directory,
  825. )
  826. flexmock(module.borgmatic.config.paths).should_receive(
  827. 'make_runtime_directory_glob',
  828. ).replace_with(lambda path: path)
  829. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
  830. flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
  831. flexmock(),
  832. )
  833. flexmock(module).should_receive('collect_dumps_from_archive').and_return(flexmock())
  834. flexmock(module).should_receive('get_dumps_to_restore').and_return(dumps_to_restore)
  835. flexmock(module).should_receive('get_configured_data_source').and_return(
  836. {'name': 'foo'},
  837. ).and_return({'name': 'bar'})
  838. flexmock(module).should_receive('restore_single_dump').with_args(
  839. repository=object,
  840. config=object,
  841. local_borg_version=object,
  842. global_arguments=object,
  843. local_path=object,
  844. remote_path=object,
  845. archive_name=object,
  846. hook_name='postgresql_databases',
  847. data_source={'name': 'foo', 'schemas': None},
  848. connection_params=object,
  849. borgmatic_runtime_directory=borgmatic_runtime_directory,
  850. ).once()
  851. flexmock(module).should_receive('restore_single_dump').with_args(
  852. repository=object,
  853. config=object,
  854. local_borg_version=object,
  855. global_arguments=object,
  856. local_path=object,
  857. remote_path=object,
  858. archive_name=object,
  859. hook_name='postgresql_databases',
  860. data_source={'name': 'bar', 'schemas': None},
  861. connection_params=object,
  862. borgmatic_runtime_directory=borgmatic_runtime_directory,
  863. ).once()
  864. flexmock(module).should_receive('ensure_requested_dumps_restored')
  865. module.run_restore(
  866. repository={'path': 'repo'},
  867. config=flexmock(),
  868. local_borg_version=flexmock(),
  869. restore_arguments=flexmock(
  870. repository='repo',
  871. archive='archive',
  872. data_sources=flexmock(),
  873. schemas=None,
  874. hostname=None,
  875. port=None,
  876. username=None,
  877. password=None,
  878. restore_path=None,
  879. ),
  880. global_arguments=flexmock(dry_run=False),
  881. local_path=flexmock(),
  882. remote_path=flexmock(),
  883. )
  884. def test_run_restore_bails_for_non_matching_repository():
  885. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(
  886. False,
  887. )
  888. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  889. flexmock(),
  890. )
  891. flexmock(module.borgmatic.config.paths).should_receive(
  892. 'make_runtime_directory_glob',
  893. ).replace_with(lambda path: path)
  894. flexmock(module.borgmatic.hooks.dispatch).should_receive(
  895. 'call_hooks_even_if_unconfigured',
  896. ).never()
  897. flexmock(module).should_receive('restore_single_dump').never()
  898. module.run_restore(
  899. repository={'path': 'repo'},
  900. config=flexmock(),
  901. local_borg_version=flexmock(),
  902. restore_arguments=flexmock(repository='repo', archive='archive', data_sources=flexmock()),
  903. global_arguments=flexmock(dry_run=False),
  904. local_path=flexmock(),
  905. remote_path=flexmock(),
  906. )
  907. def test_run_restore_restores_data_source_by_falling_back_to_all_name():
  908. dumps_to_restore = {
  909. module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  910. }
  911. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
  912. borgmatic_runtime_directory = flexmock()
  913. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  914. borgmatic_runtime_directory,
  915. )
  916. flexmock(module.borgmatic.config.paths).should_receive(
  917. 'make_runtime_directory_glob',
  918. ).replace_with(lambda path: path)
  919. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
  920. flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
  921. flexmock(),
  922. )
  923. flexmock(module).should_receive('collect_dumps_from_archive').and_return(flexmock())
  924. flexmock(module).should_receive('get_dumps_to_restore').and_return(dumps_to_restore)
  925. flexmock(module).should_receive('get_configured_data_source').and_return(
  926. {'name': 'foo'},
  927. ).and_return({'name': 'all'})
  928. flexmock(module).should_receive('restore_single_dump').with_args(
  929. repository=object,
  930. config=object,
  931. local_borg_version=object,
  932. global_arguments=object,
  933. local_path=object,
  934. remote_path=object,
  935. archive_name=object,
  936. hook_name='postgresql_databases',
  937. data_source={'name': 'foo', 'schemas': None},
  938. connection_params=object,
  939. borgmatic_runtime_directory=borgmatic_runtime_directory,
  940. ).once()
  941. flexmock(module).should_receive('ensure_requested_dumps_restored')
  942. module.run_restore(
  943. repository={'path': 'repo'},
  944. config=flexmock(),
  945. local_borg_version=flexmock(),
  946. restore_arguments=flexmock(
  947. repository='repo',
  948. archive='archive',
  949. data_sources=flexmock(),
  950. schemas=None,
  951. hostname=None,
  952. port=None,
  953. username=None,
  954. password=None,
  955. restore_path=None,
  956. ),
  957. global_arguments=flexmock(dry_run=False),
  958. local_path=flexmock(),
  959. remote_path=flexmock(),
  960. )
  961. def test_run_restore_restores_data_source_configured_with_all_name():
  962. dumps_to_restore = {
  963. module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  964. module.Dump(hook_name='postgresql_databases', data_source_name='bar'),
  965. }
  966. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
  967. borgmatic_runtime_directory = flexmock()
  968. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  969. borgmatic_runtime_directory,
  970. )
  971. flexmock(module.borgmatic.config.paths).should_receive(
  972. 'make_runtime_directory_glob',
  973. ).replace_with(lambda path: path)
  974. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
  975. flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
  976. flexmock(),
  977. )
  978. flexmock(module).should_receive('collect_dumps_from_archive').and_return(flexmock())
  979. flexmock(module).should_receive('get_dumps_to_restore').and_return(dumps_to_restore)
  980. flexmock(module).should_receive('get_configured_data_source').with_args(
  981. config=object,
  982. restore_dump=module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  983. ).and_return({'name': 'foo'})
  984. flexmock(module).should_receive('get_configured_data_source').with_args(
  985. config=object,
  986. restore_dump=module.Dump(hook_name='postgresql_databases', data_source_name='bar'),
  987. ).and_return(None)
  988. flexmock(module).should_receive('get_configured_data_source').with_args(
  989. config=object,
  990. restore_dump=module.Dump(hook_name='postgresql_databases', data_source_name='all'),
  991. ).and_return({'name': 'bar'})
  992. flexmock(module).should_receive('restore_single_dump').with_args(
  993. repository=object,
  994. config=object,
  995. local_borg_version=object,
  996. global_arguments=object,
  997. local_path=object,
  998. remote_path=object,
  999. archive_name=object,
  1000. hook_name='postgresql_databases',
  1001. data_source={'name': 'foo', 'schemas': None},
  1002. connection_params=object,
  1003. borgmatic_runtime_directory=borgmatic_runtime_directory,
  1004. ).once()
  1005. flexmock(module).should_receive('restore_single_dump').with_args(
  1006. repository=object,
  1007. config=object,
  1008. local_borg_version=object,
  1009. global_arguments=object,
  1010. local_path=object,
  1011. remote_path=object,
  1012. archive_name=object,
  1013. hook_name='postgresql_databases',
  1014. data_source={'name': 'bar', 'schemas': None},
  1015. connection_params=object,
  1016. borgmatic_runtime_directory=borgmatic_runtime_directory,
  1017. ).once()
  1018. flexmock(module).should_receive('ensure_requested_dumps_restored')
  1019. module.run_restore(
  1020. repository={'path': 'repo'},
  1021. config=flexmock(),
  1022. local_borg_version=flexmock(),
  1023. restore_arguments=flexmock(
  1024. repository='repo',
  1025. archive='archive',
  1026. data_sources=flexmock(),
  1027. schemas=None,
  1028. hostname=None,
  1029. port=None,
  1030. username=None,
  1031. password=None,
  1032. restore_path=None,
  1033. ),
  1034. global_arguments=flexmock(dry_run=False),
  1035. local_path=flexmock(),
  1036. remote_path=flexmock(),
  1037. )
  1038. def test_run_restore_skips_missing_data_source():
  1039. dumps_to_restore = {
  1040. module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  1041. module.Dump(hook_name='postgresql_databases', data_source_name='bar'),
  1042. }
  1043. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
  1044. borgmatic_runtime_directory = flexmock()
  1045. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  1046. borgmatic_runtime_directory,
  1047. )
  1048. flexmock(module.borgmatic.config.paths).should_receive(
  1049. 'make_runtime_directory_glob',
  1050. ).replace_with(lambda path: path)
  1051. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
  1052. flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
  1053. flexmock(),
  1054. )
  1055. flexmock(module).should_receive('collect_dumps_from_archive').and_return(flexmock())
  1056. flexmock(module).should_receive('get_dumps_to_restore').and_return(dumps_to_restore)
  1057. flexmock(module).should_receive('get_configured_data_source').with_args(
  1058. config=object,
  1059. restore_dump=module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  1060. ).and_return({'name': 'foo'})
  1061. flexmock(module).should_receive('get_configured_data_source').with_args(
  1062. config=object,
  1063. restore_dump=module.Dump(hook_name='postgresql_databases', data_source_name='bar'),
  1064. ).and_return(None)
  1065. flexmock(module).should_receive('get_configured_data_source').with_args(
  1066. config=object,
  1067. restore_dump=module.Dump(hook_name='postgresql_databases', data_source_name='all'),
  1068. ).and_return(None)
  1069. flexmock(module).should_receive('restore_single_dump').with_args(
  1070. repository=object,
  1071. config=object,
  1072. local_borg_version=object,
  1073. global_arguments=object,
  1074. local_path=object,
  1075. remote_path=object,
  1076. archive_name=object,
  1077. hook_name='postgresql_databases',
  1078. data_source={'name': 'foo', 'schemas': None},
  1079. connection_params=object,
  1080. borgmatic_runtime_directory=borgmatic_runtime_directory,
  1081. ).once()
  1082. flexmock(module).should_receive('restore_single_dump').with_args(
  1083. repository=object,
  1084. config=object,
  1085. local_borg_version=object,
  1086. global_arguments=object,
  1087. local_path=object,
  1088. remote_path=object,
  1089. archive_name=object,
  1090. hook_name='postgresql_databases',
  1091. data_source={'name': 'bar', 'schemas': None},
  1092. connection_params=object,
  1093. borgmatic_runtime_directory=borgmatic_runtime_directory,
  1094. ).never()
  1095. flexmock(module).should_receive('ensure_requested_dumps_restored')
  1096. module.run_restore(
  1097. repository={'path': 'repo'},
  1098. config=flexmock(),
  1099. local_borg_version=flexmock(),
  1100. restore_arguments=flexmock(
  1101. repository='repo',
  1102. archive='archive',
  1103. data_sources=flexmock(),
  1104. schemas=None,
  1105. hostname=None,
  1106. port=None,
  1107. username=None,
  1108. password=None,
  1109. restore_path=None,
  1110. ),
  1111. global_arguments=flexmock(dry_run=False),
  1112. local_path=flexmock(),
  1113. remote_path=flexmock(),
  1114. )
  1115. def test_run_restore_restores_data_sources_from_different_hooks():
  1116. dumps_to_restore = {
  1117. module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  1118. module.Dump(hook_name='mysql_databases', data_source_name='foo'),
  1119. }
  1120. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').and_return(True)
  1121. borgmatic_runtime_directory = flexmock()
  1122. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  1123. borgmatic_runtime_directory,
  1124. )
  1125. flexmock(module.borgmatic.config.paths).should_receive(
  1126. 'make_runtime_directory_glob',
  1127. ).replace_with(lambda path: path)
  1128. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks_even_if_unconfigured')
  1129. flexmock(module.borgmatic.borg.repo_list).should_receive('resolve_archive_name').and_return(
  1130. flexmock(),
  1131. )
  1132. flexmock(module).should_receive('collect_dumps_from_archive').and_return(flexmock())
  1133. flexmock(module).should_receive('get_dumps_to_restore').and_return(dumps_to_restore)
  1134. flexmock(module).should_receive('get_configured_data_source').with_args(
  1135. config=object,
  1136. restore_dump=module.Dump(hook_name='postgresql_databases', data_source_name='foo'),
  1137. ).and_return({'name': 'foo'})
  1138. flexmock(module).should_receive('get_configured_data_source').with_args(
  1139. config=object,
  1140. restore_dump=module.Dump(hook_name='mysql_databases', data_source_name='foo'),
  1141. ).and_return({'name': 'bar'})
  1142. flexmock(module).should_receive('restore_single_dump').with_args(
  1143. repository=object,
  1144. config=object,
  1145. local_borg_version=object,
  1146. global_arguments=object,
  1147. local_path=object,
  1148. remote_path=object,
  1149. archive_name=object,
  1150. hook_name='postgresql_databases',
  1151. data_source={'name': 'foo', 'schemas': None},
  1152. connection_params=object,
  1153. borgmatic_runtime_directory=borgmatic_runtime_directory,
  1154. ).once()
  1155. flexmock(module).should_receive('restore_single_dump').with_args(
  1156. repository=object,
  1157. config=object,
  1158. local_borg_version=object,
  1159. global_arguments=object,
  1160. local_path=object,
  1161. remote_path=object,
  1162. archive_name=object,
  1163. hook_name='mysql_databases',
  1164. data_source={'name': 'bar', 'schemas': None},
  1165. connection_params=object,
  1166. borgmatic_runtime_directory=borgmatic_runtime_directory,
  1167. ).once()
  1168. flexmock(module).should_receive('ensure_requested_dumps_restored')
  1169. module.run_restore(
  1170. repository={'path': 'repo'},
  1171. config=flexmock(),
  1172. local_borg_version=flexmock(),
  1173. restore_arguments=flexmock(
  1174. repository='repo',
  1175. archive='archive',
  1176. data_sources=flexmock(),
  1177. schemas=None,
  1178. hostname=None,
  1179. port=None,
  1180. username=None,
  1181. password=None,
  1182. restore_path=None,
  1183. ),
  1184. global_arguments=flexmock(dry_run=False),
  1185. local_path=flexmock(),
  1186. remote_path=flexmock(),
  1187. )