test_postgresql.py 46 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387
  1. import logging
  2. import pytest
  3. from flexmock import flexmock
  4. from borgmatic.hooks.data_source import postgresql as module
  5. def test_make_environment_maps_options_to_environment():
  6. database = {
  7. 'name': 'foo',
  8. 'password': 'pass',
  9. 'ssl_mode': 'require',
  10. 'ssl_cert': 'cert.crt',
  11. 'ssl_key': 'key.key',
  12. 'ssl_root_cert': 'root.crt',
  13. 'ssl_crl': 'crl.crl',
  14. }
  15. expected = {
  16. 'USER': 'root',
  17. 'PGPASSWORD': 'pass',
  18. 'PGSSLMODE': 'require',
  19. 'PGSSLCERT': 'cert.crt',
  20. 'PGSSLKEY': 'key.key',
  21. 'PGSSLROOTCERT': 'root.crt',
  22. 'PGSSLCRL': 'crl.crl',
  23. }
  24. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  25. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  26. 'resolve_credential'
  27. ).replace_with(lambda value, config: value)
  28. assert module.make_environment(database, {}) == expected
  29. def test_make_environment_with_cli_password_sets_correct_password():
  30. database = {'name': 'foo', 'restore_password': 'trustsome1', 'password': 'anotherpassword'}
  31. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  32. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  33. 'resolve_credential'
  34. ).replace_with(lambda value, config: value)
  35. environment = module.make_environment(
  36. database, {}, restore_connection_params={'password': 'clipassword'}
  37. )
  38. assert environment['PGPASSWORD'] == 'clipassword'
  39. def test_make_environment_without_cli_password_or_configured_password_does_not_set_password():
  40. database = {'name': 'foo'}
  41. environment = module.make_environment(
  42. database, {}, restore_connection_params={'username': 'someone'}
  43. )
  44. assert 'PGPASSWORD' not in environment
  45. def test_make_environment_without_ssl_mode_does_not_set_ssl_mode():
  46. database = {'name': 'foo'}
  47. environment = module.make_environment(database, {})
  48. assert 'PGSSLMODE' not in environment
  49. def test_database_names_to_dump_passes_through_individual_database_name():
  50. database = {'name': 'foo'}
  51. assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == ('foo',)
  52. def test_database_names_to_dump_passes_through_individual_database_name_with_format():
  53. database = {'name': 'foo', 'format': 'custom'}
  54. assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == ('foo',)
  55. def test_database_names_to_dump_passes_through_all_without_format():
  56. database = {'name': 'all'}
  57. assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == ('all',)
  58. def test_database_names_to_dump_with_all_and_format_and_dry_run_bails():
  59. database = {'name': 'all', 'format': 'custom'}
  60. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  61. 'resolve_credential'
  62. ).replace_with(lambda value, config: value)
  63. flexmock(module).should_receive('execute_command_and_capture_output').never()
  64. assert module.database_names_to_dump(database, {}, flexmock(), dry_run=True) == ()
  65. def test_database_names_to_dump_with_all_and_format_lists_databases():
  66. database = {'name': 'all', 'format': 'custom'}
  67. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  68. 'resolve_credential'
  69. ).replace_with(lambda value, config: value)
  70. flexmock(module).should_receive('execute_command_and_capture_output').and_return(
  71. 'foo,test,\nbar,test,"stuff and such"'
  72. )
  73. assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == (
  74. 'foo',
  75. 'bar',
  76. )
  77. def test_database_names_to_dump_with_all_and_format_lists_databases_with_hostname_and_port():
  78. database = {'name': 'all', 'format': 'custom', 'hostname': 'localhost', 'port': 1234}
  79. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  80. 'resolve_credential'
  81. ).replace_with(lambda value, config: value)
  82. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  83. (
  84. 'psql',
  85. '--list',
  86. '--no-password',
  87. '--no-psqlrc',
  88. '--csv',
  89. '--tuples-only',
  90. '--host',
  91. 'localhost',
  92. '--port',
  93. '1234',
  94. ),
  95. environment=object,
  96. ).and_return('foo,test,\nbar,test,"stuff and such"')
  97. assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == (
  98. 'foo',
  99. 'bar',
  100. )
  101. def test_database_names_to_dump_with_all_and_format_lists_databases_with_username():
  102. database = {'name': 'all', 'format': 'custom', 'username': 'postgres'}
  103. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  104. 'resolve_credential'
  105. ).replace_with(lambda value, config: value)
  106. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  107. (
  108. 'psql',
  109. '--list',
  110. '--no-password',
  111. '--no-psqlrc',
  112. '--csv',
  113. '--tuples-only',
  114. '--username',
  115. 'postgres',
  116. ),
  117. environment=object,
  118. ).and_return('foo,test,\nbar,test,"stuff and such"')
  119. assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == (
  120. 'foo',
  121. 'bar',
  122. )
  123. def test_database_names_to_dump_with_all_and_format_lists_databases_with_options():
  124. database = {'name': 'all', 'format': 'custom', 'list_options': '--harder'}
  125. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  126. 'resolve_credential'
  127. ).replace_with(lambda value, config: value)
  128. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  129. ('psql', '--list', '--no-password', '--no-psqlrc', '--csv', '--tuples-only', '--harder'),
  130. environment=object,
  131. ).and_return('foo,test,\nbar,test,"stuff and such"')
  132. assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == (
  133. 'foo',
  134. 'bar',
  135. )
  136. def test_database_names_to_dump_with_all_and_format_excludes_particular_databases():
  137. database = {'name': 'all', 'format': 'custom'}
  138. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  139. 'resolve_credential'
  140. ).replace_with(lambda value, config: value)
  141. flexmock(module).should_receive('execute_command_and_capture_output').and_return(
  142. 'foo,test,\ntemplate0,test,blah'
  143. )
  144. assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == ('foo',)
  145. def test_database_names_to_dump_with_all_and_psql_command_uses_custom_command():
  146. database = {
  147. 'name': 'all',
  148. 'format': 'custom',
  149. 'psql_command': 'docker exec --workdir * mycontainer psql',
  150. }
  151. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  152. 'resolve_credential'
  153. ).replace_with(lambda value, config: value)
  154. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  155. (
  156. 'docker',
  157. 'exec',
  158. '--workdir',
  159. "'*'", # Should get shell escaped to prevent injection attacks.
  160. 'mycontainer',
  161. 'psql',
  162. '--list',
  163. '--no-password',
  164. '--no-psqlrc',
  165. '--csv',
  166. '--tuples-only',
  167. ),
  168. environment=object,
  169. ).and_return('foo,text').once()
  170. assert module.database_names_to_dump(database, {}, flexmock(), dry_run=False) == ('foo',)
  171. def test_use_streaming_true_for_any_non_directory_format_databases():
  172. assert module.use_streaming(
  173. databases=[{'format': 'stuff'}, {'format': 'directory'}, {}],
  174. config=flexmock(),
  175. )
  176. def test_use_streaming_false_for_all_directory_format_databases():
  177. assert not module.use_streaming(
  178. databases=[{'format': 'directory'}, {'format': 'directory'}],
  179. config=flexmock(),
  180. )
  181. def test_use_streaming_false_for_no_databases():
  182. assert not module.use_streaming(databases=[], config=flexmock())
  183. def test_dump_data_sources_runs_pg_dump_for_each_database():
  184. databases = [{'name': 'foo'}, {'name': 'bar'}]
  185. processes = [flexmock(), flexmock()]
  186. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  187. flexmock(module).should_receive('make_dump_path').and_return('')
  188. flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
  189. ('bar',)
  190. )
  191. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
  192. 'databases/localhost/foo'
  193. ).and_return('databases/localhost/bar')
  194. flexmock(module.os.path).should_receive('exists').and_return(False)
  195. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  196. 'resolve_credential'
  197. ).replace_with(lambda value, config: value)
  198. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  199. for name, process in zip(('foo', 'bar'), processes):
  200. flexmock(module).should_receive('execute_command').with_args(
  201. (
  202. 'pg_dump',
  203. '--no-password',
  204. '--clean',
  205. '--if-exists',
  206. '--format',
  207. 'custom',
  208. name,
  209. '>',
  210. f'databases/localhost/{name}',
  211. ),
  212. shell=True,
  213. environment={'PGSSLMODE': 'disable'},
  214. run_to_completion=False,
  215. ).and_return(process).once()
  216. assert (
  217. module.dump_data_sources(
  218. databases,
  219. {},
  220. config_paths=('test.yaml',),
  221. borgmatic_runtime_directory='/run/borgmatic',
  222. patterns=[],
  223. dry_run=False,
  224. )
  225. == processes
  226. )
  227. def test_dump_data_sources_raises_when_no_database_names_to_dump():
  228. databases = [{'name': 'foo'}, {'name': 'bar'}]
  229. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  230. flexmock(module).should_receive('make_dump_path').and_return('')
  231. flexmock(module).should_receive('database_names_to_dump').and_return(())
  232. with pytest.raises(ValueError):
  233. module.dump_data_sources(
  234. databases,
  235. {},
  236. config_paths=('test.yaml',),
  237. borgmatic_runtime_directory='/run/borgmatic',
  238. patterns=[],
  239. dry_run=False,
  240. )
  241. def test_dump_data_sources_does_not_raise_when_no_database_names_to_dump():
  242. databases = [{'name': 'foo'}, {'name': 'bar'}]
  243. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  244. flexmock(module).should_receive('make_dump_path').and_return('')
  245. flexmock(module).should_receive('database_names_to_dump').and_return(())
  246. module.dump_data_sources(
  247. databases,
  248. {},
  249. config_paths=('test.yaml',),
  250. borgmatic_runtime_directory='/run/borgmatic',
  251. patterns=[],
  252. dry_run=True,
  253. ) == []
  254. def test_dump_data_sources_with_duplicate_dump_skips_pg_dump():
  255. databases = [{'name': 'foo'}, {'name': 'bar'}]
  256. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  257. flexmock(module).should_receive('make_dump_path').and_return('')
  258. flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
  259. ('bar',)
  260. )
  261. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
  262. 'databases/localhost/foo'
  263. ).and_return('databases/localhost/bar')
  264. flexmock(module.os.path).should_receive('exists').and_return(True)
  265. flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
  266. flexmock(module).should_receive('execute_command').never()
  267. assert (
  268. module.dump_data_sources(
  269. databases,
  270. {},
  271. config_paths=('test.yaml',),
  272. borgmatic_runtime_directory='/run/borgmatic',
  273. patterns=[],
  274. dry_run=False,
  275. )
  276. == []
  277. )
  278. def test_dump_data_sources_with_dry_run_skips_pg_dump():
  279. databases = [{'name': 'foo'}, {'name': 'bar'}]
  280. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  281. flexmock(module).should_receive('make_dump_path').and_return('')
  282. flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
  283. ('bar',)
  284. )
  285. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
  286. 'databases/localhost/foo'
  287. ).and_return('databases/localhost/bar')
  288. flexmock(module.os.path).should_receive('exists').and_return(False)
  289. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  290. 'resolve_credential'
  291. ).replace_with(lambda value, config: value)
  292. flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
  293. flexmock(module).should_receive('execute_command').never()
  294. assert (
  295. module.dump_data_sources(
  296. databases,
  297. {},
  298. config_paths=('test.yaml',),
  299. borgmatic_runtime_directory='/run/borgmatic',
  300. patterns=[],
  301. dry_run=True,
  302. )
  303. == []
  304. )
  305. def test_dump_data_sources_runs_pg_dump_with_hostname_and_port():
  306. databases = [{'name': 'foo', 'hostname': 'database.example.org', 'port': 5433}]
  307. process = flexmock()
  308. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  309. flexmock(module).should_receive('make_dump_path').and_return('')
  310. flexmock(module).should_receive('database_names_to_dump').and_return(('foo',))
  311. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
  312. 'databases/database.example.org/foo'
  313. )
  314. flexmock(module.os.path).should_receive('exists').and_return(False)
  315. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  316. 'resolve_credential'
  317. ).replace_with(lambda value, config: value)
  318. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  319. flexmock(module).should_receive('execute_command').with_args(
  320. (
  321. 'pg_dump',
  322. '--no-password',
  323. '--clean',
  324. '--if-exists',
  325. '--host',
  326. 'database.example.org',
  327. '--port',
  328. '5433',
  329. '--format',
  330. 'custom',
  331. 'foo',
  332. '>',
  333. 'databases/database.example.org/foo',
  334. ),
  335. shell=True,
  336. environment={'PGSSLMODE': 'disable'},
  337. run_to_completion=False,
  338. ).and_return(process).once()
  339. assert module.dump_data_sources(
  340. databases,
  341. {},
  342. config_paths=('test.yaml',),
  343. borgmatic_runtime_directory='/run/borgmatic',
  344. patterns=[],
  345. dry_run=False,
  346. ) == [process]
  347. def test_dump_data_sources_runs_pg_dump_with_username_and_password():
  348. databases = [{'name': 'foo', 'username': 'postgres', 'password': 'trustsome1'}]
  349. process = flexmock()
  350. flexmock(module).should_receive('make_environment').and_return(
  351. {'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'}
  352. )
  353. flexmock(module).should_receive('make_dump_path').and_return('')
  354. flexmock(module).should_receive('database_names_to_dump').and_return(('foo',))
  355. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
  356. 'databases/localhost/foo'
  357. )
  358. flexmock(module.os.path).should_receive('exists').and_return(False)
  359. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  360. 'resolve_credential'
  361. ).replace_with(lambda value, config: value)
  362. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  363. flexmock(module).should_receive('execute_command').with_args(
  364. (
  365. 'pg_dump',
  366. '--no-password',
  367. '--clean',
  368. '--if-exists',
  369. '--username',
  370. 'postgres',
  371. '--format',
  372. 'custom',
  373. 'foo',
  374. '>',
  375. 'databases/localhost/foo',
  376. ),
  377. shell=True,
  378. environment={'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'},
  379. run_to_completion=False,
  380. ).and_return(process).once()
  381. assert module.dump_data_sources(
  382. databases,
  383. {},
  384. config_paths=('test.yaml',),
  385. borgmatic_runtime_directory='/run/borgmatic',
  386. patterns=[],
  387. dry_run=False,
  388. ) == [process]
  389. def test_dump_data_sources_with_username_injection_attack_gets_escaped():
  390. databases = [{'name': 'foo', 'username': 'postgres; naughty-command', 'password': 'trustsome1'}]
  391. process = flexmock()
  392. flexmock(module).should_receive('make_environment').and_return(
  393. {'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'}
  394. )
  395. flexmock(module).should_receive('make_dump_path').and_return('')
  396. flexmock(module).should_receive('database_names_to_dump').and_return(('foo',))
  397. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
  398. 'databases/localhost/foo'
  399. )
  400. flexmock(module.os.path).should_receive('exists').and_return(False)
  401. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  402. 'resolve_credential'
  403. ).replace_with(lambda value, config: value)
  404. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  405. flexmock(module).should_receive('execute_command').with_args(
  406. (
  407. 'pg_dump',
  408. '--no-password',
  409. '--clean',
  410. '--if-exists',
  411. '--username',
  412. "'postgres; naughty-command'",
  413. '--format',
  414. 'custom',
  415. 'foo',
  416. '>',
  417. 'databases/localhost/foo',
  418. ),
  419. shell=True,
  420. environment={'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'},
  421. run_to_completion=False,
  422. ).and_return(process).once()
  423. assert module.dump_data_sources(
  424. databases,
  425. {},
  426. config_paths=('test.yaml',),
  427. borgmatic_runtime_directory='/run/borgmatic',
  428. patterns=[],
  429. dry_run=False,
  430. ) == [process]
  431. def test_dump_data_sources_runs_pg_dump_with_directory_format():
  432. databases = [{'name': 'foo', 'format': 'directory'}]
  433. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  434. flexmock(module).should_receive('make_dump_path').and_return('')
  435. flexmock(module).should_receive('database_names_to_dump').and_return(('foo',))
  436. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
  437. 'databases/localhost/foo'
  438. )
  439. flexmock(module.os.path).should_receive('exists').and_return(False)
  440. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  441. 'resolve_credential'
  442. ).replace_with(lambda value, config: value)
  443. flexmock(module.dump).should_receive('create_parent_directory_for_dump')
  444. flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
  445. flexmock(module).should_receive('execute_command').with_args(
  446. (
  447. 'pg_dump',
  448. '--no-password',
  449. '--clean',
  450. '--if-exists',
  451. '--format',
  452. 'directory',
  453. '--file',
  454. 'databases/localhost/foo',
  455. 'foo',
  456. ),
  457. shell=True,
  458. environment={'PGSSLMODE': 'disable'},
  459. ).and_return(flexmock()).once()
  460. assert (
  461. module.dump_data_sources(
  462. databases,
  463. {},
  464. config_paths=('test.yaml',),
  465. borgmatic_runtime_directory='/run/borgmatic',
  466. patterns=[],
  467. dry_run=False,
  468. )
  469. == []
  470. )
  471. def test_dump_data_sources_runs_pg_dump_with_options():
  472. databases = [{'name': 'foo', 'options': '--stuff=such'}]
  473. process = flexmock()
  474. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  475. flexmock(module).should_receive('make_dump_path').and_return('')
  476. flexmock(module).should_receive('database_names_to_dump').and_return(('foo',))
  477. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
  478. 'databases/localhost/foo'
  479. )
  480. flexmock(module.os.path).should_receive('exists').and_return(False)
  481. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  482. 'resolve_credential'
  483. ).replace_with(lambda value, config: value)
  484. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  485. flexmock(module).should_receive('execute_command').with_args(
  486. (
  487. 'pg_dump',
  488. '--no-password',
  489. '--clean',
  490. '--if-exists',
  491. '--format',
  492. 'custom',
  493. '--stuff=such',
  494. 'foo',
  495. '>',
  496. 'databases/localhost/foo',
  497. ),
  498. shell=True,
  499. environment={'PGSSLMODE': 'disable'},
  500. run_to_completion=False,
  501. ).and_return(process).once()
  502. assert module.dump_data_sources(
  503. databases,
  504. {},
  505. config_paths=('test.yaml',),
  506. borgmatic_runtime_directory='/run/borgmatic',
  507. patterns=[],
  508. dry_run=False,
  509. ) == [process]
  510. def test_dump_data_sources_runs_pg_dumpall_for_all_databases():
  511. databases = [{'name': 'all'}]
  512. process = flexmock()
  513. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  514. flexmock(module).should_receive('make_dump_path').and_return('')
  515. flexmock(module).should_receive('database_names_to_dump').and_return(('all',))
  516. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
  517. 'databases/localhost/all'
  518. )
  519. flexmock(module.os.path).should_receive('exists').and_return(False)
  520. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  521. 'resolve_credential'
  522. ).replace_with(lambda value, config: value)
  523. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  524. flexmock(module).should_receive('execute_command').with_args(
  525. ('pg_dumpall', '--no-password', '--clean', '--if-exists', '>', 'databases/localhost/all'),
  526. shell=True,
  527. environment={'PGSSLMODE': 'disable'},
  528. run_to_completion=False,
  529. ).and_return(process).once()
  530. assert module.dump_data_sources(
  531. databases,
  532. {},
  533. config_paths=('test.yaml',),
  534. borgmatic_runtime_directory='/run/borgmatic',
  535. patterns=[],
  536. dry_run=False,
  537. ) == [process]
  538. def test_dump_data_sources_runs_non_default_pg_dump():
  539. databases = [{'name': 'foo', 'pg_dump_command': 'special_pg_dump --compress *'}]
  540. process = flexmock()
  541. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  542. flexmock(module).should_receive('make_dump_path').and_return('')
  543. flexmock(module).should_receive('database_names_to_dump').and_return(('foo',))
  544. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
  545. 'databases/localhost/foo'
  546. )
  547. flexmock(module.os.path).should_receive('exists').and_return(False)
  548. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  549. 'resolve_credential'
  550. ).replace_with(lambda value, config: value)
  551. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  552. flexmock(module).should_receive('execute_command').with_args(
  553. (
  554. 'special_pg_dump',
  555. '--compress',
  556. "'*'", # Should get shell escaped to prevent injection attacks.
  557. '--no-password',
  558. '--clean',
  559. '--if-exists',
  560. '--format',
  561. 'custom',
  562. 'foo',
  563. '>',
  564. 'databases/localhost/foo',
  565. ),
  566. shell=True,
  567. environment={'PGSSLMODE': 'disable'},
  568. run_to_completion=False,
  569. ).and_return(process).once()
  570. assert module.dump_data_sources(
  571. databases,
  572. {},
  573. config_paths=('test.yaml',),
  574. borgmatic_runtime_directory='/run/borgmatic',
  575. patterns=[],
  576. dry_run=False,
  577. ) == [process]
  578. def test_restore_data_source_dump_runs_pg_restore():
  579. hook_config = [{'name': 'foo', 'schemas': None}, {'name': 'bar'}]
  580. extract_process = flexmock(stdout=flexmock())
  581. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  582. 'resolve_credential'
  583. ).replace_with(lambda value, config: value)
  584. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  585. flexmock(module).should_receive('make_dump_path')
  586. flexmock(module.dump).should_receive('make_data_source_dump_filename')
  587. flexmock(module).should_receive('execute_command_with_processes').with_args(
  588. (
  589. 'pg_restore',
  590. '--no-password',
  591. '--if-exists',
  592. '--exit-on-error',
  593. '--clean',
  594. '--dbname',
  595. 'foo',
  596. ),
  597. processes=[extract_process],
  598. output_log_level=logging.DEBUG,
  599. input_file=extract_process.stdout,
  600. environment={'PGSSLMODE': 'disable'},
  601. ).once()
  602. flexmock(module).should_receive('execute_command').with_args(
  603. (
  604. 'psql',
  605. '--no-password',
  606. '--no-psqlrc',
  607. '--quiet',
  608. '--dbname',
  609. 'foo',
  610. '--command',
  611. 'ANALYZE',
  612. ),
  613. environment={'PGSSLMODE': 'disable'},
  614. ).once()
  615. module.restore_data_source_dump(
  616. hook_config,
  617. {},
  618. data_source={'name': 'foo'},
  619. dry_run=False,
  620. extract_process=extract_process,
  621. connection_params={
  622. 'hostname': None,
  623. 'port': None,
  624. 'username': None,
  625. 'password': None,
  626. },
  627. borgmatic_runtime_directory='/run/borgmatic',
  628. )
  629. def test_restore_data_source_dump_runs_pg_restore_with_hostname_and_port():
  630. hook_config = [
  631. {'name': 'foo', 'hostname': 'database.example.org', 'port': 5433, 'schemas': None}
  632. ]
  633. extract_process = flexmock(stdout=flexmock())
  634. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  635. 'resolve_credential'
  636. ).replace_with(lambda value, config: value)
  637. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  638. flexmock(module).should_receive('make_dump_path')
  639. flexmock(module.dump).should_receive('make_data_source_dump_filename')
  640. flexmock(module).should_receive('execute_command_with_processes').with_args(
  641. (
  642. 'pg_restore',
  643. '--no-password',
  644. '--if-exists',
  645. '--exit-on-error',
  646. '--clean',
  647. '--dbname',
  648. 'foo',
  649. '--host',
  650. 'database.example.org',
  651. '--port',
  652. '5433',
  653. ),
  654. processes=[extract_process],
  655. output_log_level=logging.DEBUG,
  656. input_file=extract_process.stdout,
  657. environment={'PGSSLMODE': 'disable'},
  658. ).once()
  659. flexmock(module).should_receive('execute_command').with_args(
  660. (
  661. 'psql',
  662. '--no-password',
  663. '--no-psqlrc',
  664. '--quiet',
  665. '--host',
  666. 'database.example.org',
  667. '--port',
  668. '5433',
  669. '--dbname',
  670. 'foo',
  671. '--command',
  672. 'ANALYZE',
  673. ),
  674. environment={'PGSSLMODE': 'disable'},
  675. ).once()
  676. module.restore_data_source_dump(
  677. hook_config,
  678. {},
  679. data_source=hook_config[0],
  680. dry_run=False,
  681. extract_process=extract_process,
  682. connection_params={
  683. 'hostname': None,
  684. 'port': None,
  685. 'username': None,
  686. 'password': None,
  687. },
  688. borgmatic_runtime_directory='/run/borgmatic',
  689. )
  690. def test_restore_data_source_dump_runs_pg_restore_with_username_and_password():
  691. hook_config = [
  692. {'name': 'foo', 'username': 'postgres', 'password': 'trustsome1', 'schemas': None}
  693. ]
  694. extract_process = flexmock(stdout=flexmock())
  695. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  696. 'resolve_credential'
  697. ).replace_with(lambda value, config: value)
  698. flexmock(module).should_receive('make_environment').and_return(
  699. {'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'}
  700. )
  701. flexmock(module).should_receive('make_dump_path')
  702. flexmock(module.dump).should_receive('make_data_source_dump_filename')
  703. flexmock(module).should_receive('execute_command_with_processes').with_args(
  704. (
  705. 'pg_restore',
  706. '--no-password',
  707. '--if-exists',
  708. '--exit-on-error',
  709. '--clean',
  710. '--dbname',
  711. 'foo',
  712. '--username',
  713. 'postgres',
  714. ),
  715. processes=[extract_process],
  716. output_log_level=logging.DEBUG,
  717. input_file=extract_process.stdout,
  718. environment={'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'},
  719. ).once()
  720. flexmock(module).should_receive('execute_command').with_args(
  721. (
  722. 'psql',
  723. '--no-password',
  724. '--no-psqlrc',
  725. '--quiet',
  726. '--username',
  727. 'postgres',
  728. '--dbname',
  729. 'foo',
  730. '--command',
  731. 'ANALYZE',
  732. ),
  733. environment={'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'},
  734. ).once()
  735. module.restore_data_source_dump(
  736. hook_config,
  737. {},
  738. data_source=hook_config[0],
  739. dry_run=False,
  740. extract_process=extract_process,
  741. connection_params={
  742. 'hostname': None,
  743. 'port': None,
  744. 'username': None,
  745. 'password': None,
  746. },
  747. borgmatic_runtime_directory='/run/borgmatic',
  748. )
  749. def test_restore_data_source_dump_with_connection_params_uses_connection_params_for_restore():
  750. hook_config = [
  751. {
  752. 'name': 'foo',
  753. 'hostname': 'database.example.org',
  754. 'port': 5433,
  755. 'username': 'postgres',
  756. 'password': 'trustsome1',
  757. 'restore_hostname': 'restorehost',
  758. 'restore_port': 'restoreport',
  759. 'restore_username': 'restoreusername',
  760. 'restore_password': 'restorepassword',
  761. 'schemas': None,
  762. }
  763. ]
  764. extract_process = flexmock(stdout=flexmock())
  765. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  766. 'resolve_credential'
  767. ).replace_with(lambda value, config: value)
  768. flexmock(module).should_receive('make_environment').and_return(
  769. {'PGPASSWORD': 'clipassword', 'PGSSLMODE': 'disable'}
  770. )
  771. flexmock(module).should_receive('make_dump_path')
  772. flexmock(module.dump).should_receive('make_data_source_dump_filename')
  773. flexmock(module).should_receive('execute_command_with_processes').with_args(
  774. (
  775. 'pg_restore',
  776. '--no-password',
  777. '--if-exists',
  778. '--exit-on-error',
  779. '--clean',
  780. '--dbname',
  781. 'foo',
  782. '--host',
  783. 'clihost',
  784. '--port',
  785. 'cliport',
  786. '--username',
  787. 'cliusername',
  788. ),
  789. processes=[extract_process],
  790. output_log_level=logging.DEBUG,
  791. input_file=extract_process.stdout,
  792. environment={'PGPASSWORD': 'clipassword', 'PGSSLMODE': 'disable'},
  793. ).once()
  794. flexmock(module).should_receive('execute_command').with_args(
  795. (
  796. 'psql',
  797. '--no-password',
  798. '--no-psqlrc',
  799. '--quiet',
  800. '--host',
  801. 'clihost',
  802. '--port',
  803. 'cliport',
  804. '--username',
  805. 'cliusername',
  806. '--dbname',
  807. 'foo',
  808. '--command',
  809. 'ANALYZE',
  810. ),
  811. environment={'PGPASSWORD': 'clipassword', 'PGSSLMODE': 'disable'},
  812. ).once()
  813. module.restore_data_source_dump(
  814. hook_config,
  815. {},
  816. data_source={'name': 'foo'},
  817. dry_run=False,
  818. extract_process=extract_process,
  819. connection_params={
  820. 'hostname': 'clihost',
  821. 'port': 'cliport',
  822. 'username': 'cliusername',
  823. 'password': 'clipassword',
  824. },
  825. borgmatic_runtime_directory='/run/borgmatic',
  826. )
  827. def test_restore_data_source_dump_without_connection_params_uses_restore_params_in_config_for_restore():
  828. hook_config = [
  829. {
  830. 'name': 'foo',
  831. 'hostname': 'database.example.org',
  832. 'port': 5433,
  833. 'username': 'postgres',
  834. 'password': 'trustsome1',
  835. 'schemas': None,
  836. 'restore_hostname': 'restorehost',
  837. 'restore_port': 'restoreport',
  838. 'restore_username': 'restoreusername',
  839. 'restore_password': 'restorepassword',
  840. }
  841. ]
  842. extract_process = flexmock(stdout=flexmock())
  843. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  844. 'resolve_credential'
  845. ).replace_with(lambda value, config: value)
  846. flexmock(module).should_receive('make_environment').and_return(
  847. {'PGPASSWORD': 'restorepassword', 'PGSSLMODE': 'disable'}
  848. )
  849. flexmock(module).should_receive('make_dump_path')
  850. flexmock(module.dump).should_receive('make_data_source_dump_filename')
  851. flexmock(module).should_receive('execute_command_with_processes').with_args(
  852. (
  853. 'pg_restore',
  854. '--no-password',
  855. '--if-exists',
  856. '--exit-on-error',
  857. '--clean',
  858. '--dbname',
  859. 'foo',
  860. '--host',
  861. 'restorehost',
  862. '--port',
  863. 'restoreport',
  864. '--username',
  865. 'restoreusername',
  866. ),
  867. processes=[extract_process],
  868. output_log_level=logging.DEBUG,
  869. input_file=extract_process.stdout,
  870. environment={'PGPASSWORD': 'restorepassword', 'PGSSLMODE': 'disable'},
  871. ).once()
  872. flexmock(module).should_receive('execute_command').with_args(
  873. (
  874. 'psql',
  875. '--no-password',
  876. '--no-psqlrc',
  877. '--quiet',
  878. '--host',
  879. 'restorehost',
  880. '--port',
  881. 'restoreport',
  882. '--username',
  883. 'restoreusername',
  884. '--dbname',
  885. 'foo',
  886. '--command',
  887. 'ANALYZE',
  888. ),
  889. environment={'PGPASSWORD': 'restorepassword', 'PGSSLMODE': 'disable'},
  890. ).once()
  891. module.restore_data_source_dump(
  892. hook_config,
  893. {},
  894. data_source=hook_config[0],
  895. dry_run=False,
  896. extract_process=extract_process,
  897. connection_params={
  898. 'hostname': None,
  899. 'port': None,
  900. 'username': None,
  901. 'password': None,
  902. },
  903. borgmatic_runtime_directory='/run/borgmatic',
  904. )
  905. def test_restore_data_source_dump_runs_pg_restore_with_options():
  906. hook_config = [
  907. {
  908. 'name': 'foo',
  909. 'restore_options': '--harder',
  910. 'analyze_options': '--smarter',
  911. 'schemas': None,
  912. }
  913. ]
  914. extract_process = flexmock(stdout=flexmock())
  915. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  916. 'resolve_credential'
  917. ).replace_with(lambda value, config: value)
  918. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  919. flexmock(module).should_receive('make_dump_path')
  920. flexmock(module.dump).should_receive('make_data_source_dump_filename')
  921. flexmock(module).should_receive('execute_command_with_processes').with_args(
  922. (
  923. 'pg_restore',
  924. '--no-password',
  925. '--if-exists',
  926. '--exit-on-error',
  927. '--clean',
  928. '--dbname',
  929. 'foo',
  930. '--harder',
  931. ),
  932. processes=[extract_process],
  933. output_log_level=logging.DEBUG,
  934. input_file=extract_process.stdout,
  935. environment={'PGSSLMODE': 'disable'},
  936. ).once()
  937. flexmock(module).should_receive('execute_command').with_args(
  938. (
  939. 'psql',
  940. '--no-password',
  941. '--no-psqlrc',
  942. '--quiet',
  943. '--dbname',
  944. 'foo',
  945. '--smarter',
  946. '--command',
  947. 'ANALYZE',
  948. ),
  949. environment={'PGSSLMODE': 'disable'},
  950. ).once()
  951. module.restore_data_source_dump(
  952. hook_config,
  953. {},
  954. data_source=hook_config[0],
  955. dry_run=False,
  956. extract_process=extract_process,
  957. connection_params={
  958. 'hostname': None,
  959. 'port': None,
  960. 'username': None,
  961. 'password': None,
  962. },
  963. borgmatic_runtime_directory='/run/borgmatic',
  964. )
  965. def test_restore_data_source_dump_runs_psql_for_all_database_dump():
  966. hook_config = [{'name': 'all', 'schemas': None}]
  967. extract_process = flexmock(stdout=flexmock())
  968. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  969. 'resolve_credential'
  970. ).replace_with(lambda value, config: value)
  971. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  972. flexmock(module).should_receive('make_dump_path')
  973. flexmock(module.dump).should_receive('make_data_source_dump_filename')
  974. flexmock(module).should_receive('execute_command_with_processes').with_args(
  975. (
  976. 'psql',
  977. '--no-password',
  978. '--no-psqlrc',
  979. ),
  980. processes=[extract_process],
  981. output_log_level=logging.DEBUG,
  982. input_file=extract_process.stdout,
  983. environment={'PGSSLMODE': 'disable'},
  984. ).once()
  985. flexmock(module).should_receive('execute_command').with_args(
  986. ('psql', '--no-password', '--no-psqlrc', '--quiet', '--command', 'ANALYZE'),
  987. environment={'PGSSLMODE': 'disable'},
  988. ).once()
  989. module.restore_data_source_dump(
  990. hook_config,
  991. {},
  992. data_source={'name': 'all'},
  993. dry_run=False,
  994. extract_process=extract_process,
  995. connection_params={
  996. 'hostname': None,
  997. 'port': None,
  998. 'username': None,
  999. 'password': None,
  1000. },
  1001. borgmatic_runtime_directory='/run/borgmatic',
  1002. )
  1003. def test_restore_data_source_dump_runs_psql_for_plain_database_dump():
  1004. hook_config = [{'name': 'foo', 'format': 'plain', 'schemas': None}]
  1005. extract_process = flexmock(stdout=flexmock())
  1006. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1007. 'resolve_credential'
  1008. ).replace_with(lambda value, config: value)
  1009. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  1010. flexmock(module).should_receive('make_dump_path')
  1011. flexmock(module.dump).should_receive('make_data_source_dump_filename')
  1012. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1013. ('psql', '--no-password', '--no-psqlrc', '--dbname', 'foo'),
  1014. processes=[extract_process],
  1015. output_log_level=logging.DEBUG,
  1016. input_file=extract_process.stdout,
  1017. environment={'PGSSLMODE': 'disable'},
  1018. ).once()
  1019. flexmock(module).should_receive('execute_command').with_args(
  1020. (
  1021. 'psql',
  1022. '--no-password',
  1023. '--no-psqlrc',
  1024. '--quiet',
  1025. '--dbname',
  1026. 'foo',
  1027. '--command',
  1028. 'ANALYZE',
  1029. ),
  1030. environment={'PGSSLMODE': 'disable'},
  1031. ).once()
  1032. module.restore_data_source_dump(
  1033. hook_config,
  1034. {},
  1035. data_source=hook_config[0],
  1036. dry_run=False,
  1037. extract_process=extract_process,
  1038. connection_params={
  1039. 'hostname': None,
  1040. 'port': None,
  1041. 'username': None,
  1042. 'password': None,
  1043. },
  1044. borgmatic_runtime_directory='/run/borgmatic',
  1045. )
  1046. def test_restore_data_source_dump_runs_non_default_pg_restore_and_psql():
  1047. hook_config = [
  1048. {
  1049. 'name': 'foo',
  1050. 'pg_restore_command': 'docker exec --workdir * mycontainer pg_restore',
  1051. 'psql_command': 'docker exec --workdir * mycontainer psql',
  1052. 'schemas': None,
  1053. }
  1054. ]
  1055. extract_process = flexmock(stdout=flexmock())
  1056. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1057. 'resolve_credential'
  1058. ).replace_with(lambda value, config: value)
  1059. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  1060. flexmock(module).should_receive('make_dump_path')
  1061. flexmock(module.dump).should_receive('make_data_source_dump_filename')
  1062. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1063. (
  1064. 'docker',
  1065. 'exec',
  1066. '--workdir',
  1067. "'*'", # Should get shell escaped to prevent injection attacks.
  1068. 'mycontainer',
  1069. 'pg_restore',
  1070. '--no-password',
  1071. '--if-exists',
  1072. '--exit-on-error',
  1073. '--clean',
  1074. '--dbname',
  1075. 'foo',
  1076. ),
  1077. processes=[extract_process],
  1078. output_log_level=logging.DEBUG,
  1079. input_file=extract_process.stdout,
  1080. environment={'PGSSLMODE': 'disable'},
  1081. ).once()
  1082. flexmock(module).should_receive('execute_command').with_args(
  1083. (
  1084. 'docker',
  1085. 'exec',
  1086. '--workdir',
  1087. "'*'", # Should get shell escaped to prevent injection attacks.
  1088. 'mycontainer',
  1089. 'psql',
  1090. '--no-password',
  1091. '--no-psqlrc',
  1092. '--quiet',
  1093. '--dbname',
  1094. 'foo',
  1095. '--command',
  1096. 'ANALYZE',
  1097. ),
  1098. environment={'PGSSLMODE': 'disable'},
  1099. ).once()
  1100. module.restore_data_source_dump(
  1101. hook_config,
  1102. {},
  1103. data_source=hook_config[0],
  1104. dry_run=False,
  1105. extract_process=extract_process,
  1106. connection_params={
  1107. 'hostname': None,
  1108. 'port': None,
  1109. 'username': None,
  1110. 'password': None,
  1111. },
  1112. borgmatic_runtime_directory='/run/borgmatic',
  1113. )
  1114. def test_restore_data_source_dump_with_dry_run_skips_restore():
  1115. hook_config = [{'name': 'foo', 'schemas': None}]
  1116. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1117. 'resolve_credential'
  1118. ).replace_with(lambda value, config: value)
  1119. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  1120. flexmock(module).should_receive('make_dump_path')
  1121. flexmock(module.dump).should_receive('make_data_source_dump_filename')
  1122. flexmock(module).should_receive('execute_command_with_processes').never()
  1123. module.restore_data_source_dump(
  1124. hook_config,
  1125. {},
  1126. data_source={'name': 'foo'},
  1127. dry_run=True,
  1128. extract_process=flexmock(),
  1129. connection_params={
  1130. 'hostname': None,
  1131. 'port': None,
  1132. 'username': None,
  1133. 'password': None,
  1134. },
  1135. borgmatic_runtime_directory='/run/borgmatic',
  1136. )
  1137. def test_restore_data_source_dump_without_extract_process_restores_from_disk():
  1138. hook_config = [{'name': 'foo', 'schemas': None}]
  1139. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1140. 'resolve_credential'
  1141. ).replace_with(lambda value, config: value)
  1142. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  1143. flexmock(module).should_receive('make_dump_path')
  1144. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('/dump/path')
  1145. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1146. (
  1147. 'pg_restore',
  1148. '--no-password',
  1149. '--if-exists',
  1150. '--exit-on-error',
  1151. '--clean',
  1152. '--dbname',
  1153. 'foo',
  1154. '/dump/path',
  1155. ),
  1156. processes=[],
  1157. output_log_level=logging.DEBUG,
  1158. input_file=None,
  1159. environment={'PGSSLMODE': 'disable'},
  1160. ).once()
  1161. flexmock(module).should_receive('execute_command').with_args(
  1162. (
  1163. 'psql',
  1164. '--no-password',
  1165. '--no-psqlrc',
  1166. '--quiet',
  1167. '--dbname',
  1168. 'foo',
  1169. '--command',
  1170. 'ANALYZE',
  1171. ),
  1172. environment={'PGSSLMODE': 'disable'},
  1173. ).once()
  1174. module.restore_data_source_dump(
  1175. hook_config,
  1176. {},
  1177. data_source={'name': 'foo'},
  1178. dry_run=False,
  1179. extract_process=None,
  1180. connection_params={
  1181. 'hostname': None,
  1182. 'port': None,
  1183. 'username': None,
  1184. 'password': None,
  1185. },
  1186. borgmatic_runtime_directory='/run/borgmatic',
  1187. )
  1188. def test_restore_data_source_dump_with_schemas_restores_schemas():
  1189. hook_config = [{'name': 'foo', 'schemas': ['bar', 'baz']}]
  1190. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1191. 'resolve_credential'
  1192. ).replace_with(lambda value, config: value)
  1193. flexmock(module).should_receive('make_environment').and_return({'PGSSLMODE': 'disable'})
  1194. flexmock(module).should_receive('make_dump_path')
  1195. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('/dump/path')
  1196. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1197. (
  1198. 'pg_restore',
  1199. '--no-password',
  1200. '--if-exists',
  1201. '--exit-on-error',
  1202. '--clean',
  1203. '--dbname',
  1204. 'foo',
  1205. '/dump/path',
  1206. '--schema',
  1207. 'bar',
  1208. '--schema',
  1209. 'baz',
  1210. ),
  1211. processes=[],
  1212. output_log_level=logging.DEBUG,
  1213. input_file=None,
  1214. environment={'PGSSLMODE': 'disable'},
  1215. ).once()
  1216. flexmock(module).should_receive('execute_command').with_args(
  1217. (
  1218. 'psql',
  1219. '--no-password',
  1220. '--no-psqlrc',
  1221. '--quiet',
  1222. '--dbname',
  1223. 'foo',
  1224. '--command',
  1225. 'ANALYZE',
  1226. ),
  1227. environment={'PGSSLMODE': 'disable'},
  1228. ).once()
  1229. module.restore_data_source_dump(
  1230. hook_config,
  1231. {},
  1232. data_source=hook_config[0],
  1233. dry_run=False,
  1234. extract_process=None,
  1235. connection_params={
  1236. 'hostname': None,
  1237. 'port': None,
  1238. 'username': None,
  1239. 'password': None,
  1240. },
  1241. borgmatic_runtime_directory='/run/borgmatic',
  1242. )