test_postgresql.py 46 KB

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