test_postgresql.py 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160
  1. import logging
  2. import pytest
  3. from flexmock import flexmock
  4. from borgmatic.hooks 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. extra_env = module.make_extra_environment(database)
  24. assert extra_env == expected
  25. def test_make_extra_environment_with_cli_password_sets_correct_password():
  26. database = {'name': 'foo', 'restore_password': 'trustsome1', 'password': 'anotherpassword'}
  27. extra = module.make_extra_environment(
  28. database, restore_connection_params={'password': 'clipassword'}
  29. )
  30. assert extra['PGPASSWORD'] == 'clipassword'
  31. def test_make_extra_environment_without_cli_password_or_configured_password_does_not_set_password():
  32. database = {'name': 'foo'}
  33. extra = module.make_extra_environment(
  34. database, restore_connection_params={'username': 'someone'}
  35. )
  36. assert 'PGPASSWORD' not in extra
  37. def test_database_names_to_dump_passes_through_individual_database_name():
  38. database = {'name': 'foo'}
  39. assert module.database_names_to_dump(database, flexmock(), flexmock(), dry_run=False) == (
  40. 'foo',
  41. )
  42. def test_database_names_to_dump_passes_through_individual_database_name_with_format():
  43. database = {'name': 'foo', 'format': 'custom'}
  44. assert module.database_names_to_dump(database, flexmock(), flexmock(), dry_run=False) == (
  45. 'foo',
  46. )
  47. def test_database_names_to_dump_passes_through_all_without_format():
  48. database = {'name': 'all'}
  49. assert module.database_names_to_dump(database, flexmock(), flexmock(), dry_run=False) == (
  50. 'all',
  51. )
  52. def test_database_names_to_dump_with_all_and_format_and_dry_run_bails():
  53. database = {'name': 'all', 'format': 'custom'}
  54. flexmock(module).should_receive('execute_command_and_capture_output').never()
  55. assert module.database_names_to_dump(database, flexmock(), flexmock(), dry_run=True) == ()
  56. def test_database_names_to_dump_with_all_and_format_lists_databases():
  57. database = {'name': 'all', 'format': 'custom'}
  58. flexmock(module).should_receive('execute_command_and_capture_output').and_return(
  59. 'foo,test,\nbar,test,"stuff and such"'
  60. )
  61. assert module.database_names_to_dump(database, flexmock(), flexmock(), dry_run=False) == (
  62. 'foo',
  63. 'bar',
  64. )
  65. def test_database_names_to_dump_with_all_and_format_lists_databases_with_hostname_and_port():
  66. database = {'name': 'all', 'format': 'custom', 'hostname': 'localhost', 'port': 1234}
  67. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  68. (
  69. 'psql',
  70. '--list',
  71. '--no-password',
  72. '--no-psqlrc',
  73. '--csv',
  74. '--tuples-only',
  75. '--host',
  76. 'localhost',
  77. '--port',
  78. '1234',
  79. ),
  80. extra_environment=object,
  81. ).and_return('foo,test,\nbar,test,"stuff and such"')
  82. assert module.database_names_to_dump(database, flexmock(), flexmock(), dry_run=False) == (
  83. 'foo',
  84. 'bar',
  85. )
  86. def test_database_names_to_dump_with_all_and_format_lists_databases_with_username():
  87. database = {'name': 'all', 'format': 'custom', 'username': 'postgres'}
  88. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  89. (
  90. 'psql',
  91. '--list',
  92. '--no-password',
  93. '--no-psqlrc',
  94. '--csv',
  95. '--tuples-only',
  96. '--username',
  97. 'postgres',
  98. ),
  99. extra_environment=object,
  100. ).and_return('foo,test,\nbar,test,"stuff and such"')
  101. assert module.database_names_to_dump(database, flexmock(), flexmock(), dry_run=False) == (
  102. 'foo',
  103. 'bar',
  104. )
  105. def test_database_names_to_dump_with_all_and_format_lists_databases_with_options():
  106. database = {'name': 'all', 'format': 'custom', 'list_options': '--harder'}
  107. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  108. ('psql', '--list', '--no-password', '--no-psqlrc', '--csv', '--tuples-only', '--harder'),
  109. extra_environment=object,
  110. ).and_return('foo,test,\nbar,test,"stuff and such"')
  111. assert module.database_names_to_dump(database, flexmock(), flexmock(), dry_run=False) == (
  112. 'foo',
  113. 'bar',
  114. )
  115. def test_database_names_to_dump_with_all_and_format_excludes_particular_databases():
  116. database = {'name': 'all', 'format': 'custom'}
  117. flexmock(module).should_receive('execute_command_and_capture_output').and_return(
  118. 'foo,test,\ntemplate0,test,blah'
  119. )
  120. assert module.database_names_to_dump(database, flexmock(), flexmock(), dry_run=False) == (
  121. 'foo',
  122. )
  123. def test_database_names_to_dump_with_all_and_psql_command_uses_custom_command():
  124. database = {'name': 'all', 'format': 'custom', 'psql_command': 'docker exec mycontainer psql'}
  125. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  126. (
  127. 'docker',
  128. 'exec',
  129. 'mycontainer',
  130. 'psql',
  131. '--list',
  132. '--no-password',
  133. '--no-psqlrc',
  134. '--csv',
  135. '--tuples-only',
  136. ),
  137. extra_environment=object,
  138. ).and_return('foo,text').once()
  139. assert module.database_names_to_dump(database, flexmock(), flexmock(), dry_run=False) == (
  140. 'foo',
  141. )
  142. def test_dump_databases_runs_pg_dump_for_each_database():
  143. databases = [{'name': 'foo'}, {'name': 'bar'}]
  144. processes = [flexmock(), flexmock()]
  145. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  146. flexmock(module).should_receive('make_dump_path').and_return('')
  147. flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
  148. ('bar',)
  149. )
  150. flexmock(module.dump).should_receive('make_database_dump_filename').and_return(
  151. 'databases/localhost/foo'
  152. ).and_return('databases/localhost/bar')
  153. flexmock(module.os.path).should_receive('exists').and_return(False)
  154. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  155. for name, process in zip(('foo', 'bar'), processes):
  156. flexmock(module).should_receive('execute_command').with_args(
  157. (
  158. 'pg_dump',
  159. '--no-password',
  160. '--clean',
  161. '--if-exists',
  162. '--format',
  163. 'custom',
  164. name,
  165. '>',
  166. f'databases/localhost/{name}',
  167. ),
  168. shell=True,
  169. extra_environment={'PGSSLMODE': 'disable'},
  170. run_to_completion=False,
  171. ).and_return(process).once()
  172. assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == processes
  173. def test_dump_databases_raises_when_no_database_names_to_dump():
  174. databases = [{'name': 'foo'}, {'name': 'bar'}]
  175. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  176. flexmock(module).should_receive('make_dump_path').and_return('')
  177. flexmock(module).should_receive('database_names_to_dump').and_return(())
  178. with pytest.raises(ValueError):
  179. module.dump_databases(databases, {}, 'test.yaml', dry_run=False)
  180. def test_dump_databases_does_not_raise_when_no_database_names_to_dump():
  181. databases = [{'name': 'foo'}, {'name': 'bar'}]
  182. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  183. flexmock(module).should_receive('make_dump_path').and_return('')
  184. flexmock(module).should_receive('database_names_to_dump').and_return(())
  185. module.dump_databases(databases, {}, 'test.yaml', dry_run=True) == []
  186. def test_dump_databases_with_duplicate_dump_skips_pg_dump():
  187. databases = [{'name': 'foo'}, {'name': 'bar'}]
  188. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  189. flexmock(module).should_receive('make_dump_path').and_return('')
  190. flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
  191. ('bar',)
  192. )
  193. flexmock(module.dump).should_receive('make_database_dump_filename').and_return(
  194. 'databases/localhost/foo'
  195. ).and_return('databases/localhost/bar')
  196. flexmock(module.os.path).should_receive('exists').and_return(True)
  197. flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
  198. flexmock(module).should_receive('execute_command').never()
  199. assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == []
  200. def test_dump_databases_with_dry_run_skips_pg_dump():
  201. databases = [{'name': 'foo'}, {'name': 'bar'}]
  202. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  203. flexmock(module).should_receive('make_dump_path').and_return('')
  204. flexmock(module).should_receive('database_names_to_dump').and_return(('foo',)).and_return(
  205. ('bar',)
  206. )
  207. flexmock(module.dump).should_receive('make_database_dump_filename').and_return(
  208. 'databases/localhost/foo'
  209. ).and_return('databases/localhost/bar')
  210. flexmock(module.os.path).should_receive('exists').and_return(False)
  211. flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
  212. flexmock(module).should_receive('execute_command').never()
  213. assert module.dump_databases(databases, {}, 'test.yaml', dry_run=True) == []
  214. def test_dump_databases_runs_pg_dump_with_hostname_and_port():
  215. databases = [{'name': 'foo', 'hostname': 'database.example.org', 'port': 5433}]
  216. process = flexmock()
  217. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  218. flexmock(module).should_receive('make_dump_path').and_return('')
  219. flexmock(module).should_receive('database_names_to_dump').and_return(('foo',))
  220. flexmock(module.dump).should_receive('make_database_dump_filename').and_return(
  221. 'databases/database.example.org/foo'
  222. )
  223. flexmock(module.os.path).should_receive('exists').and_return(False)
  224. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  225. flexmock(module).should_receive('execute_command').with_args(
  226. (
  227. 'pg_dump',
  228. '--no-password',
  229. '--clean',
  230. '--if-exists',
  231. '--host',
  232. 'database.example.org',
  233. '--port',
  234. '5433',
  235. '--format',
  236. 'custom',
  237. 'foo',
  238. '>',
  239. 'databases/database.example.org/foo',
  240. ),
  241. shell=True,
  242. extra_environment={'PGSSLMODE': 'disable'},
  243. run_to_completion=False,
  244. ).and_return(process).once()
  245. assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == [process]
  246. def test_dump_databases_runs_pg_dump_with_username_and_password():
  247. databases = [{'name': 'foo', 'username': 'postgres', 'password': 'trustsome1'}]
  248. process = flexmock()
  249. flexmock(module).should_receive('make_extra_environment').and_return(
  250. {'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'}
  251. )
  252. flexmock(module).should_receive('make_dump_path').and_return('')
  253. flexmock(module).should_receive('database_names_to_dump').and_return(('foo',))
  254. flexmock(module.dump).should_receive('make_database_dump_filename').and_return(
  255. 'databases/localhost/foo'
  256. )
  257. flexmock(module.os.path).should_receive('exists').and_return(False)
  258. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  259. flexmock(module).should_receive('execute_command').with_args(
  260. (
  261. 'pg_dump',
  262. '--no-password',
  263. '--clean',
  264. '--if-exists',
  265. '--username',
  266. 'postgres',
  267. '--format',
  268. 'custom',
  269. 'foo',
  270. '>',
  271. 'databases/localhost/foo',
  272. ),
  273. shell=True,
  274. extra_environment={'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'},
  275. run_to_completion=False,
  276. ).and_return(process).once()
  277. assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == [process]
  278. def test_dump_databases_runs_pg_dump_with_directory_format():
  279. databases = [{'name': 'foo', 'format': 'directory'}]
  280. flexmock(module).should_receive('make_extra_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',))
  283. flexmock(module.dump).should_receive('make_database_dump_filename').and_return(
  284. 'databases/localhost/foo'
  285. )
  286. flexmock(module.os.path).should_receive('exists').and_return(False)
  287. flexmock(module.dump).should_receive('create_parent_directory_for_dump')
  288. flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
  289. flexmock(module).should_receive('execute_command').with_args(
  290. (
  291. 'pg_dump',
  292. '--no-password',
  293. '--clean',
  294. '--if-exists',
  295. '--format',
  296. 'directory',
  297. '--file',
  298. 'databases/localhost/foo',
  299. 'foo',
  300. ),
  301. shell=True,
  302. extra_environment={'PGSSLMODE': 'disable'},
  303. ).and_return(flexmock()).once()
  304. assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == []
  305. def test_dump_databases_runs_pg_dump_with_options():
  306. databases = [{'name': 'foo', 'options': '--stuff=such'}]
  307. process = flexmock()
  308. flexmock(module).should_receive('make_extra_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_database_dump_filename').and_return(
  312. 'databases/localhost/foo'
  313. )
  314. flexmock(module.os.path).should_receive('exists').and_return(False)
  315. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  316. flexmock(module).should_receive('execute_command').with_args(
  317. (
  318. 'pg_dump',
  319. '--no-password',
  320. '--clean',
  321. '--if-exists',
  322. '--format',
  323. 'custom',
  324. '--stuff=such',
  325. 'foo',
  326. '>',
  327. 'databases/localhost/foo',
  328. ),
  329. shell=True,
  330. extra_environment={'PGSSLMODE': 'disable'},
  331. run_to_completion=False,
  332. ).and_return(process).once()
  333. assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == [process]
  334. def test_dump_databases_runs_pg_dumpall_for_all_databases():
  335. databases = [{'name': 'all'}]
  336. process = flexmock()
  337. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  338. flexmock(module).should_receive('make_dump_path').and_return('')
  339. flexmock(module).should_receive('database_names_to_dump').and_return(('all',))
  340. flexmock(module.dump).should_receive('make_database_dump_filename').and_return(
  341. 'databases/localhost/all'
  342. )
  343. flexmock(module.os.path).should_receive('exists').and_return(False)
  344. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  345. flexmock(module).should_receive('execute_command').with_args(
  346. ('pg_dumpall', '--no-password', '--clean', '--if-exists', '>', 'databases/localhost/all'),
  347. shell=True,
  348. extra_environment={'PGSSLMODE': 'disable'},
  349. run_to_completion=False,
  350. ).and_return(process).once()
  351. assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == [process]
  352. def test_dump_databases_runs_non_default_pg_dump():
  353. databases = [{'name': 'foo', 'pg_dump_command': 'special_pg_dump'}]
  354. process = flexmock()
  355. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  356. flexmock(module).should_receive('make_dump_path').and_return('')
  357. flexmock(module).should_receive('database_names_to_dump').and_return(('foo',))
  358. flexmock(module.dump).should_receive('make_database_dump_filename').and_return(
  359. 'databases/localhost/foo'
  360. )
  361. flexmock(module.os.path).should_receive('exists').and_return(False)
  362. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  363. flexmock(module).should_receive('execute_command').with_args(
  364. (
  365. 'special_pg_dump',
  366. '--no-password',
  367. '--clean',
  368. '--if-exists',
  369. '--format',
  370. 'custom',
  371. 'foo',
  372. '>',
  373. 'databases/localhost/foo',
  374. ),
  375. shell=True,
  376. extra_environment={'PGSSLMODE': 'disable'},
  377. run_to_completion=False,
  378. ).and_return(process).once()
  379. assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == [process]
  380. def test_restore_database_dump_runs_pg_restore():
  381. databases_config = [{'name': 'foo', 'schemas': None}, {'name': 'bar'}]
  382. extract_process = flexmock(stdout=flexmock())
  383. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  384. flexmock(module).should_receive('make_dump_path')
  385. flexmock(module.dump).should_receive('make_database_dump_filename')
  386. flexmock(module).should_receive('execute_command_with_processes').with_args(
  387. (
  388. 'pg_restore',
  389. '--no-password',
  390. '--if-exists',
  391. '--exit-on-error',
  392. '--clean',
  393. '--dbname',
  394. 'foo',
  395. ),
  396. processes=[extract_process],
  397. output_log_level=logging.DEBUG,
  398. input_file=extract_process.stdout,
  399. extra_environment={'PGSSLMODE': 'disable'},
  400. ).once()
  401. flexmock(module).should_receive('execute_command').with_args(
  402. (
  403. 'psql',
  404. '--no-password',
  405. '--no-psqlrc',
  406. '--quiet',
  407. '--dbname',
  408. 'foo',
  409. '--command',
  410. 'ANALYZE',
  411. ),
  412. extra_environment={'PGSSLMODE': 'disable'},
  413. ).once()
  414. module.restore_database_dump(
  415. databases_config,
  416. {},
  417. 'test.yaml',
  418. database_name='foo',
  419. dry_run=False,
  420. extract_process=extract_process,
  421. connection_params={
  422. 'hostname': None,
  423. 'port': None,
  424. 'username': None,
  425. 'password': None,
  426. },
  427. )
  428. def test_restore_database_dump_errors_when_database_missing_from_configuration():
  429. databases_config = [{'name': 'foo', 'schemas': None}, {'name': 'bar'}]
  430. extract_process = flexmock(stdout=flexmock())
  431. flexmock(module).should_receive('execute_command_with_processes').never()
  432. flexmock(module).should_receive('execute_command').never()
  433. with pytest.raises(ValueError):
  434. module.restore_database_dump(
  435. databases_config,
  436. {},
  437. 'test.yaml',
  438. database_name='other',
  439. dry_run=False,
  440. extract_process=extract_process,
  441. connection_params={
  442. 'hostname': None,
  443. 'port': None,
  444. 'username': None,
  445. 'password': None,
  446. },
  447. )
  448. def test_restore_database_dump_runs_pg_restore_with_hostname_and_port():
  449. databases_config = [
  450. {'name': 'foo', 'hostname': 'database.example.org', 'port': 5433, 'schemas': None}
  451. ]
  452. extract_process = flexmock(stdout=flexmock())
  453. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  454. flexmock(module).should_receive('make_dump_path')
  455. flexmock(module.dump).should_receive('make_database_dump_filename')
  456. flexmock(module).should_receive('execute_command_with_processes').with_args(
  457. (
  458. 'pg_restore',
  459. '--no-password',
  460. '--if-exists',
  461. '--exit-on-error',
  462. '--clean',
  463. '--dbname',
  464. 'foo',
  465. '--host',
  466. 'database.example.org',
  467. '--port',
  468. '5433',
  469. ),
  470. processes=[extract_process],
  471. output_log_level=logging.DEBUG,
  472. input_file=extract_process.stdout,
  473. extra_environment={'PGSSLMODE': 'disable'},
  474. ).once()
  475. flexmock(module).should_receive('execute_command').with_args(
  476. (
  477. 'psql',
  478. '--no-password',
  479. '--no-psqlrc',
  480. '--quiet',
  481. '--host',
  482. 'database.example.org',
  483. '--port',
  484. '5433',
  485. '--dbname',
  486. 'foo',
  487. '--command',
  488. 'ANALYZE',
  489. ),
  490. extra_environment={'PGSSLMODE': 'disable'},
  491. ).once()
  492. module.restore_database_dump(
  493. databases_config,
  494. {},
  495. 'test.yaml',
  496. database_name='foo',
  497. dry_run=False,
  498. extract_process=extract_process,
  499. connection_params={
  500. 'hostname': None,
  501. 'port': None,
  502. 'username': None,
  503. 'password': None,
  504. },
  505. )
  506. def test_restore_database_dump_runs_pg_restore_with_username_and_password():
  507. databases_config = [
  508. {'name': 'foo', 'username': 'postgres', 'password': 'trustsome1', 'schemas': None}
  509. ]
  510. extract_process = flexmock(stdout=flexmock())
  511. flexmock(module).should_receive('make_extra_environment').and_return(
  512. {'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'}
  513. )
  514. flexmock(module).should_receive('make_dump_path')
  515. flexmock(module.dump).should_receive('make_database_dump_filename')
  516. flexmock(module).should_receive('execute_command_with_processes').with_args(
  517. (
  518. 'pg_restore',
  519. '--no-password',
  520. '--if-exists',
  521. '--exit-on-error',
  522. '--clean',
  523. '--dbname',
  524. 'foo',
  525. '--username',
  526. 'postgres',
  527. ),
  528. processes=[extract_process],
  529. output_log_level=logging.DEBUG,
  530. input_file=extract_process.stdout,
  531. extra_environment={'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'},
  532. ).once()
  533. flexmock(module).should_receive('execute_command').with_args(
  534. (
  535. 'psql',
  536. '--no-password',
  537. '--no-psqlrc',
  538. '--quiet',
  539. '--username',
  540. 'postgres',
  541. '--dbname',
  542. 'foo',
  543. '--command',
  544. 'ANALYZE',
  545. ),
  546. extra_environment={'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'},
  547. ).once()
  548. module.restore_database_dump(
  549. databases_config,
  550. {},
  551. 'test.yaml',
  552. database_name='foo',
  553. dry_run=False,
  554. extract_process=extract_process,
  555. connection_params={
  556. 'hostname': None,
  557. 'port': None,
  558. 'username': None,
  559. 'password': None,
  560. },
  561. )
  562. def test_restore_database_dump_with_connection_params_uses_connection_params_for_restore():
  563. databases_config = [
  564. {
  565. 'name': 'foo',
  566. 'hostname': 'database.example.org',
  567. 'port': 5433,
  568. 'username': 'postgres',
  569. 'password': 'trustsome1',
  570. 'restore_hostname': 'restorehost',
  571. 'restore_port': 'restoreport',
  572. 'restore_username': 'restoreusername',
  573. 'restore_password': 'restorepassword',
  574. 'schemas': None,
  575. }
  576. ]
  577. extract_process = flexmock(stdout=flexmock())
  578. flexmock(module).should_receive('make_extra_environment').and_return(
  579. {'PGPASSWORD': 'clipassword', 'PGSSLMODE': 'disable'}
  580. )
  581. flexmock(module).should_receive('make_dump_path')
  582. flexmock(module.dump).should_receive('make_database_dump_filename')
  583. flexmock(module).should_receive('execute_command_with_processes').with_args(
  584. (
  585. 'pg_restore',
  586. '--no-password',
  587. '--if-exists',
  588. '--exit-on-error',
  589. '--clean',
  590. '--dbname',
  591. 'foo',
  592. '--host',
  593. 'clihost',
  594. '--port',
  595. 'cliport',
  596. '--username',
  597. 'cliusername',
  598. ),
  599. processes=[extract_process],
  600. output_log_level=logging.DEBUG,
  601. input_file=extract_process.stdout,
  602. extra_environment={'PGPASSWORD': 'clipassword', 'PGSSLMODE': 'disable'},
  603. ).once()
  604. flexmock(module).should_receive('execute_command').with_args(
  605. (
  606. 'psql',
  607. '--no-password',
  608. '--no-psqlrc',
  609. '--quiet',
  610. '--host',
  611. 'clihost',
  612. '--port',
  613. 'cliport',
  614. '--username',
  615. 'cliusername',
  616. '--dbname',
  617. 'foo',
  618. '--command',
  619. 'ANALYZE',
  620. ),
  621. extra_environment={'PGPASSWORD': 'clipassword', 'PGSSLMODE': 'disable'},
  622. ).once()
  623. module.restore_database_dump(
  624. databases_config,
  625. {},
  626. 'test.yaml',
  627. database_name='foo',
  628. dry_run=False,
  629. extract_process=extract_process,
  630. connection_params={
  631. 'hostname': 'clihost',
  632. 'port': 'cliport',
  633. 'username': 'cliusername',
  634. 'password': 'clipassword',
  635. },
  636. )
  637. def test_restore_database_dump_without_connection_params_uses_restore_params_in_config_for_restore():
  638. databases_config = [
  639. {
  640. 'name': 'foo',
  641. 'hostname': 'database.example.org',
  642. 'port': 5433,
  643. 'username': 'postgres',
  644. 'password': 'trustsome1',
  645. 'schemas': None,
  646. 'restore_hostname': 'restorehost',
  647. 'restore_port': 'restoreport',
  648. 'restore_username': 'restoreusername',
  649. 'restore_password': 'restorepassword',
  650. }
  651. ]
  652. extract_process = flexmock(stdout=flexmock())
  653. flexmock(module).should_receive('make_extra_environment').and_return(
  654. {'PGPASSWORD': 'restorepassword', 'PGSSLMODE': 'disable'}
  655. )
  656. flexmock(module).should_receive('make_dump_path')
  657. flexmock(module.dump).should_receive('make_database_dump_filename')
  658. flexmock(module).should_receive('execute_command_with_processes').with_args(
  659. (
  660. 'pg_restore',
  661. '--no-password',
  662. '--if-exists',
  663. '--exit-on-error',
  664. '--clean',
  665. '--dbname',
  666. 'foo',
  667. '--host',
  668. 'restorehost',
  669. '--port',
  670. 'restoreport',
  671. '--username',
  672. 'restoreusername',
  673. ),
  674. processes=[extract_process],
  675. output_log_level=logging.DEBUG,
  676. input_file=extract_process.stdout,
  677. extra_environment={'PGPASSWORD': 'restorepassword', 'PGSSLMODE': 'disable'},
  678. ).once()
  679. flexmock(module).should_receive('execute_command').with_args(
  680. (
  681. 'psql',
  682. '--no-password',
  683. '--no-psqlrc',
  684. '--quiet',
  685. '--host',
  686. 'restorehost',
  687. '--port',
  688. 'restoreport',
  689. '--username',
  690. 'restoreusername',
  691. '--dbname',
  692. 'foo',
  693. '--command',
  694. 'ANALYZE',
  695. ),
  696. extra_environment={'PGPASSWORD': 'restorepassword', 'PGSSLMODE': 'disable'},
  697. ).once()
  698. module.restore_database_dump(
  699. databases_config,
  700. {},
  701. 'test.yaml',
  702. database_name='foo',
  703. dry_run=False,
  704. extract_process=extract_process,
  705. connection_params={
  706. 'hostname': None,
  707. 'port': None,
  708. 'username': None,
  709. 'password': None,
  710. },
  711. )
  712. def test_restore_database_dump_runs_pg_restore_with_options():
  713. databases_config = [
  714. {
  715. 'name': 'foo',
  716. 'restore_options': '--harder',
  717. 'analyze_options': '--smarter',
  718. 'schemas': None,
  719. }
  720. ]
  721. extract_process = flexmock(stdout=flexmock())
  722. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  723. flexmock(module).should_receive('make_dump_path')
  724. flexmock(module.dump).should_receive('make_database_dump_filename')
  725. flexmock(module).should_receive('execute_command_with_processes').with_args(
  726. (
  727. 'pg_restore',
  728. '--no-password',
  729. '--if-exists',
  730. '--exit-on-error',
  731. '--clean',
  732. '--dbname',
  733. 'foo',
  734. '--harder',
  735. ),
  736. processes=[extract_process],
  737. output_log_level=logging.DEBUG,
  738. input_file=extract_process.stdout,
  739. extra_environment={'PGSSLMODE': 'disable'},
  740. ).once()
  741. flexmock(module).should_receive('execute_command').with_args(
  742. (
  743. 'psql',
  744. '--no-password',
  745. '--no-psqlrc',
  746. '--quiet',
  747. '--dbname',
  748. 'foo',
  749. '--smarter',
  750. '--command',
  751. 'ANALYZE',
  752. ),
  753. extra_environment={'PGSSLMODE': 'disable'},
  754. ).once()
  755. module.restore_database_dump(
  756. databases_config,
  757. {},
  758. 'test.yaml',
  759. database_name='foo',
  760. dry_run=False,
  761. extract_process=extract_process,
  762. connection_params={
  763. 'hostname': None,
  764. 'port': None,
  765. 'username': None,
  766. 'password': None,
  767. },
  768. )
  769. def test_restore_database_dump_runs_psql_for_all_database_dump():
  770. databases_config = [{'name': 'all', 'schemas': None}]
  771. extract_process = flexmock(stdout=flexmock())
  772. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  773. flexmock(module).should_receive('make_dump_path')
  774. flexmock(module.dump).should_receive('make_database_dump_filename')
  775. flexmock(module).should_receive('execute_command_with_processes').with_args(
  776. (
  777. 'psql',
  778. '--no-password',
  779. '--no-psqlrc',
  780. ),
  781. processes=[extract_process],
  782. output_log_level=logging.DEBUG,
  783. input_file=extract_process.stdout,
  784. extra_environment={'PGSSLMODE': 'disable'},
  785. ).once()
  786. flexmock(module).should_receive('execute_command').with_args(
  787. ('psql', '--no-password', '--no-psqlrc', '--quiet', '--command', 'ANALYZE'),
  788. extra_environment={'PGSSLMODE': 'disable'},
  789. ).once()
  790. module.restore_database_dump(
  791. databases_config,
  792. {},
  793. 'test.yaml',
  794. database_name='all',
  795. dry_run=False,
  796. extract_process=extract_process,
  797. connection_params={
  798. 'hostname': None,
  799. 'port': None,
  800. 'username': None,
  801. 'password': None,
  802. },
  803. )
  804. def test_restore_database_dump_runs_psql_for_plain_database_dump():
  805. databases_config = [{'name': 'foo', 'format': 'plain', 'schemas': None}]
  806. extract_process = flexmock(stdout=flexmock())
  807. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  808. flexmock(module).should_receive('make_dump_path')
  809. flexmock(module.dump).should_receive('make_database_dump_filename')
  810. flexmock(module).should_receive('execute_command_with_processes').with_args(
  811. ('psql', '--no-password', '--no-psqlrc', '--dbname', 'foo'),
  812. processes=[extract_process],
  813. output_log_level=logging.DEBUG,
  814. input_file=extract_process.stdout,
  815. extra_environment={'PGSSLMODE': 'disable'},
  816. ).once()
  817. flexmock(module).should_receive('execute_command').with_args(
  818. (
  819. 'psql',
  820. '--no-password',
  821. '--no-psqlrc',
  822. '--quiet',
  823. '--dbname',
  824. 'foo',
  825. '--command',
  826. 'ANALYZE',
  827. ),
  828. extra_environment={'PGSSLMODE': 'disable'},
  829. ).once()
  830. module.restore_database_dump(
  831. databases_config,
  832. {},
  833. 'test.yaml',
  834. database_name='foo',
  835. dry_run=False,
  836. extract_process=extract_process,
  837. connection_params={
  838. 'hostname': None,
  839. 'port': None,
  840. 'username': None,
  841. 'password': None,
  842. },
  843. )
  844. def test_restore_database_dump_runs_non_default_pg_restore_and_psql():
  845. databases_config = [
  846. {
  847. 'name': 'foo',
  848. 'pg_restore_command': 'docker exec mycontainer pg_restore',
  849. 'psql_command': 'docker exec mycontainer psql',
  850. 'schemas': None,
  851. }
  852. ]
  853. extract_process = flexmock(stdout=flexmock())
  854. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  855. flexmock(module).should_receive('make_dump_path')
  856. flexmock(module.dump).should_receive('make_database_dump_filename')
  857. flexmock(module).should_receive('execute_command_with_processes').with_args(
  858. (
  859. 'docker',
  860. 'exec',
  861. 'mycontainer',
  862. 'pg_restore',
  863. '--no-password',
  864. '--if-exists',
  865. '--exit-on-error',
  866. '--clean',
  867. '--dbname',
  868. 'foo',
  869. ),
  870. processes=[extract_process],
  871. output_log_level=logging.DEBUG,
  872. input_file=extract_process.stdout,
  873. extra_environment={'PGSSLMODE': 'disable'},
  874. ).once()
  875. flexmock(module).should_receive('execute_command').with_args(
  876. (
  877. 'docker',
  878. 'exec',
  879. 'mycontainer',
  880. 'psql',
  881. '--no-password',
  882. '--no-psqlrc',
  883. '--quiet',
  884. '--dbname',
  885. 'foo',
  886. '--command',
  887. 'ANALYZE',
  888. ),
  889. extra_environment={'PGSSLMODE': 'disable'},
  890. ).once()
  891. module.restore_database_dump(
  892. databases_config,
  893. {},
  894. 'test.yaml',
  895. database_name='foo',
  896. dry_run=False,
  897. extract_process=extract_process,
  898. connection_params={
  899. 'hostname': None,
  900. 'port': None,
  901. 'username': None,
  902. 'password': None,
  903. },
  904. )
  905. def test_restore_database_dump_with_dry_run_skips_restore():
  906. databases_config = [{'name': 'foo', 'schemas': None}]
  907. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  908. flexmock(module).should_receive('make_dump_path')
  909. flexmock(module.dump).should_receive('make_database_dump_filename')
  910. flexmock(module).should_receive('execute_command_with_processes').never()
  911. module.restore_database_dump(
  912. databases_config,
  913. {},
  914. 'test.yaml',
  915. database_name='foo',
  916. dry_run=True,
  917. extract_process=flexmock(),
  918. connection_params={
  919. 'hostname': None,
  920. 'port': None,
  921. 'username': None,
  922. 'password': None,
  923. },
  924. )
  925. def test_restore_database_dump_without_extract_process_restores_from_disk():
  926. databases_config = [{'name': 'foo', 'schemas': None}]
  927. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  928. flexmock(module).should_receive('make_dump_path')
  929. flexmock(module.dump).should_receive('make_database_dump_filename').and_return('/dump/path')
  930. flexmock(module).should_receive('execute_command_with_processes').with_args(
  931. (
  932. 'pg_restore',
  933. '--no-password',
  934. '--if-exists',
  935. '--exit-on-error',
  936. '--clean',
  937. '--dbname',
  938. 'foo',
  939. '/dump/path',
  940. ),
  941. processes=[],
  942. output_log_level=logging.DEBUG,
  943. input_file=None,
  944. extra_environment={'PGSSLMODE': 'disable'},
  945. ).once()
  946. flexmock(module).should_receive('execute_command').with_args(
  947. (
  948. 'psql',
  949. '--no-password',
  950. '--no-psqlrc',
  951. '--quiet',
  952. '--dbname',
  953. 'foo',
  954. '--command',
  955. 'ANALYZE',
  956. ),
  957. extra_environment={'PGSSLMODE': 'disable'},
  958. ).once()
  959. module.restore_database_dump(
  960. databases_config,
  961. {},
  962. 'test.yaml',
  963. database_name='foo',
  964. dry_run=False,
  965. extract_process=None,
  966. connection_params={
  967. 'hostname': None,
  968. 'port': None,
  969. 'username': None,
  970. 'password': None,
  971. },
  972. )
  973. def test_restore_database_dump_with_schemas_restores_schemas():
  974. databases_config = [{'name': 'foo', 'schemas': ['bar', 'baz']}]
  975. flexmock(module).should_receive('make_extra_environment').and_return({'PGSSLMODE': 'disable'})
  976. flexmock(module).should_receive('make_dump_path')
  977. flexmock(module.dump).should_receive('make_database_dump_filename').and_return('/dump/path')
  978. flexmock(module).should_receive('execute_command_with_processes').with_args(
  979. (
  980. 'pg_restore',
  981. '--no-password',
  982. '--if-exists',
  983. '--exit-on-error',
  984. '--clean',
  985. '--dbname',
  986. 'foo',
  987. '/dump/path',
  988. '--schema',
  989. 'bar',
  990. '--schema',
  991. 'baz',
  992. ),
  993. processes=[],
  994. output_log_level=logging.DEBUG,
  995. input_file=None,
  996. extra_environment={'PGSSLMODE': 'disable'},
  997. ).once()
  998. flexmock(module).should_receive('execute_command').with_args(
  999. (
  1000. 'psql',
  1001. '--no-password',
  1002. '--no-psqlrc',
  1003. '--quiet',
  1004. '--dbname',
  1005. 'foo',
  1006. '--command',
  1007. 'ANALYZE',
  1008. ),
  1009. extra_environment={'PGSSLMODE': 'disable'},
  1010. ).once()
  1011. module.restore_database_dump(
  1012. databases_config,
  1013. {},
  1014. 'test.yaml',
  1015. database_name='foo',
  1016. dry_run=False,
  1017. extract_process=None,
  1018. connection_params={
  1019. 'hostname': None,
  1020. 'port': None,
  1021. 'username': None,
  1022. 'password': None,
  1023. },
  1024. )