2
0

test_restore.py 47 KB

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