test_mongodb.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. import logging
  2. import pytest
  3. from flexmock import flexmock
  4. from borgmatic.hooks import mongodb as module
  5. def test_dump_databases_runs_mongodump_for_each_database():
  6. databases = [{'name': 'foo'}, {'name': 'bar'}]
  7. processes = [flexmock(), flexmock()]
  8. flexmock(module).should_receive('make_dump_path').and_return('')
  9. flexmock(module.dump).should_receive('make_database_dump_filename').and_return(
  10. 'databases/localhost/foo'
  11. ).and_return('databases/localhost/bar')
  12. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  13. for name, process in zip(('foo', 'bar'), processes):
  14. flexmock(module).should_receive('execute_command').with_args(
  15. ['mongodump', '--db', name, '--archive', '>', f'databases/localhost/{name}'],
  16. shell=True,
  17. run_to_completion=False,
  18. ).and_return(process).once()
  19. assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == processes
  20. def test_dump_databases_with_dry_run_skips_mongodump():
  21. databases = [{'name': 'foo'}, {'name': 'bar'}]
  22. flexmock(module).should_receive('make_dump_path').and_return('')
  23. flexmock(module.dump).should_receive('make_database_dump_filename').and_return(
  24. 'databases/localhost/foo'
  25. ).and_return('databases/localhost/bar')
  26. flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
  27. flexmock(module).should_receive('execute_command').never()
  28. assert module.dump_databases(databases, {}, 'test.yaml', dry_run=True) == []
  29. def test_dump_databases_runs_mongodump_with_hostname_and_port():
  30. databases = [{'name': 'foo', 'hostname': 'database.example.org', 'port': 5433}]
  31. process = flexmock()
  32. flexmock(module).should_receive('make_dump_path').and_return('')
  33. flexmock(module.dump).should_receive('make_database_dump_filename').and_return(
  34. 'databases/database.example.org/foo'
  35. )
  36. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  37. flexmock(module).should_receive('execute_command').with_args(
  38. [
  39. 'mongodump',
  40. '--host',
  41. 'database.example.org',
  42. '--port',
  43. '5433',
  44. '--db',
  45. 'foo',
  46. '--archive',
  47. '>',
  48. 'databases/database.example.org/foo',
  49. ],
  50. shell=True,
  51. run_to_completion=False,
  52. ).and_return(process).once()
  53. assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == [process]
  54. def test_dump_databases_runs_mongodump_with_username_and_password():
  55. databases = [
  56. {
  57. 'name': 'foo',
  58. 'username': 'mongo',
  59. 'password': 'trustsome1',
  60. 'authentication_database': 'admin',
  61. }
  62. ]
  63. process = flexmock()
  64. flexmock(module).should_receive('make_dump_path').and_return('')
  65. flexmock(module.dump).should_receive('make_database_dump_filename').and_return(
  66. 'databases/localhost/foo'
  67. )
  68. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  69. flexmock(module).should_receive('execute_command').with_args(
  70. [
  71. 'mongodump',
  72. '--username',
  73. 'mongo',
  74. '--password',
  75. 'trustsome1',
  76. '--authenticationDatabase',
  77. 'admin',
  78. '--db',
  79. 'foo',
  80. '--archive',
  81. '>',
  82. 'databases/localhost/foo',
  83. ],
  84. shell=True,
  85. run_to_completion=False,
  86. ).and_return(process).once()
  87. assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == [process]
  88. def test_dump_databases_runs_mongodump_with_directory_format():
  89. databases = [{'name': 'foo', 'format': 'directory'}]
  90. flexmock(module).should_receive('make_dump_path').and_return('')
  91. flexmock(module.dump).should_receive('make_database_dump_filename').and_return(
  92. 'databases/localhost/foo'
  93. )
  94. flexmock(module.dump).should_receive('create_parent_directory_for_dump')
  95. flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
  96. flexmock(module).should_receive('execute_command').with_args(
  97. ['mongodump', '--out', 'databases/localhost/foo', '--db', 'foo'],
  98. shell=True,
  99. ).and_return(flexmock()).once()
  100. assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == []
  101. def test_dump_databases_runs_mongodump_with_options():
  102. databases = [{'name': 'foo', 'options': '--stuff=such'}]
  103. process = flexmock()
  104. flexmock(module).should_receive('make_dump_path').and_return('')
  105. flexmock(module.dump).should_receive('make_database_dump_filename').and_return(
  106. 'databases/localhost/foo'
  107. )
  108. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  109. flexmock(module).should_receive('execute_command').with_args(
  110. ['mongodump', '--db', 'foo', '--stuff=such', '--archive', '>', 'databases/localhost/foo'],
  111. shell=True,
  112. run_to_completion=False,
  113. ).and_return(process).once()
  114. assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == [process]
  115. def test_dump_databases_runs_mongodumpall_for_all_databases():
  116. databases = [{'name': 'all'}]
  117. process = flexmock()
  118. flexmock(module).should_receive('make_dump_path').and_return('')
  119. flexmock(module.dump).should_receive('make_database_dump_filename').and_return(
  120. 'databases/localhost/all'
  121. )
  122. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  123. flexmock(module).should_receive('execute_command').with_args(
  124. ['mongodump', '--archive', '>', 'databases/localhost/all'],
  125. shell=True,
  126. run_to_completion=False,
  127. ).and_return(process).once()
  128. assert module.dump_databases(databases, {}, 'test.yaml', dry_run=False) == [process]
  129. def test_restore_database_dump_runs_mongorestore():
  130. databases_config = [{'name': 'foo', 'schemas': None}, {'name': 'bar'}]
  131. extract_process = flexmock(stdout=flexmock())
  132. flexmock(module).should_receive('make_dump_path')
  133. flexmock(module.dump).should_receive('make_database_dump_filename')
  134. flexmock(module).should_receive('execute_command_with_processes').with_args(
  135. ['mongorestore', '--archive', '--drop', '--db', 'foo'],
  136. processes=[extract_process],
  137. output_log_level=logging.DEBUG,
  138. input_file=extract_process.stdout,
  139. ).once()
  140. module.restore_database_dump(
  141. databases_config,
  142. {},
  143. 'test.yaml',
  144. database_name='foo',
  145. dry_run=False,
  146. extract_process=extract_process,
  147. connection_params={
  148. 'hostname': None,
  149. 'port': None,
  150. 'username': None,
  151. 'password': None,
  152. },
  153. )
  154. def test_restore_database_dump_errors_on_empty_databases_config():
  155. databases_config = []
  156. flexmock(module).should_receive('make_dump_path')
  157. flexmock(module.dump).should_receive('make_database_dump_filename')
  158. flexmock(module).should_receive('execute_command_with_processes').never()
  159. flexmock(module).should_receive('execute_command').never()
  160. with pytest.raises(ValueError):
  161. module.restore_database_dump(
  162. databases_config,
  163. {},
  164. 'test.yaml',
  165. database_name='foo',
  166. dry_run=False,
  167. extract_process=flexmock(),
  168. connection_params={
  169. 'hostname': None,
  170. 'port': None,
  171. 'username': None,
  172. 'password': None,
  173. },
  174. )
  175. def test_restore_database_dump_runs_mongorestore_with_hostname_and_port():
  176. databases_config = [
  177. {'name': 'foo', 'hostname': 'database.example.org', 'port': 5433, 'schemas': None}
  178. ]
  179. extract_process = flexmock(stdout=flexmock())
  180. flexmock(module).should_receive('make_dump_path')
  181. flexmock(module.dump).should_receive('make_database_dump_filename')
  182. flexmock(module).should_receive('execute_command_with_processes').with_args(
  183. [
  184. 'mongorestore',
  185. '--archive',
  186. '--drop',
  187. '--db',
  188. 'foo',
  189. '--host',
  190. 'database.example.org',
  191. '--port',
  192. '5433',
  193. ],
  194. processes=[extract_process],
  195. output_log_level=logging.DEBUG,
  196. input_file=extract_process.stdout,
  197. ).once()
  198. module.restore_database_dump(
  199. databases_config,
  200. {},
  201. 'test.yaml',
  202. database_name='foo',
  203. dry_run=False,
  204. extract_process=extract_process,
  205. connection_params={
  206. 'hostname': None,
  207. 'port': None,
  208. 'username': None,
  209. 'password': None,
  210. },
  211. )
  212. def test_restore_database_dump_runs_mongorestore_with_username_and_password():
  213. databases_config = [
  214. {
  215. 'name': 'foo',
  216. 'username': 'mongo',
  217. 'password': 'trustsome1',
  218. 'authentication_database': 'admin',
  219. 'schemas': None,
  220. }
  221. ]
  222. extract_process = flexmock(stdout=flexmock())
  223. flexmock(module).should_receive('make_dump_path')
  224. flexmock(module.dump).should_receive('make_database_dump_filename')
  225. flexmock(module).should_receive('execute_command_with_processes').with_args(
  226. [
  227. 'mongorestore',
  228. '--archive',
  229. '--drop',
  230. '--db',
  231. 'foo',
  232. '--username',
  233. 'mongo',
  234. '--password',
  235. 'trustsome1',
  236. '--authenticationDatabase',
  237. 'admin',
  238. ],
  239. processes=[extract_process],
  240. output_log_level=logging.DEBUG,
  241. input_file=extract_process.stdout,
  242. ).once()
  243. module.restore_database_dump(
  244. databases_config,
  245. {},
  246. 'test.yaml',
  247. database_name='foo',
  248. dry_run=False,
  249. extract_process=extract_process,
  250. connection_params={
  251. 'hostname': None,
  252. 'port': None,
  253. 'username': None,
  254. 'password': None,
  255. },
  256. )
  257. def test_restore_database_dump_with_connection_params_uses_connection_params_for_restore():
  258. databases_config = [
  259. {
  260. 'name': 'foo',
  261. 'username': 'mongo',
  262. 'password': 'trustsome1',
  263. 'authentication_database': 'admin',
  264. 'restore_hostname': 'restorehost',
  265. 'restore_port': 'restoreport',
  266. 'restore_username': 'restoreusername',
  267. 'restore_password': 'restorepassword',
  268. 'schemas': None,
  269. }
  270. ]
  271. extract_process = flexmock(stdout=flexmock())
  272. flexmock(module).should_receive('make_dump_path')
  273. flexmock(module.dump).should_receive('make_database_dump_filename')
  274. flexmock(module).should_receive('execute_command_with_processes').with_args(
  275. [
  276. 'mongorestore',
  277. '--archive',
  278. '--drop',
  279. '--db',
  280. 'foo',
  281. '--host',
  282. 'clihost',
  283. '--port',
  284. 'cliport',
  285. '--username',
  286. 'cliusername',
  287. '--password',
  288. 'clipassword',
  289. '--authenticationDatabase',
  290. 'admin',
  291. ],
  292. processes=[extract_process],
  293. output_log_level=logging.DEBUG,
  294. input_file=extract_process.stdout,
  295. ).once()
  296. module.restore_database_dump(
  297. databases_config,
  298. {},
  299. 'test.yaml',
  300. database_name='foo',
  301. dry_run=False,
  302. extract_process=extract_process,
  303. connection_params={
  304. 'hostname': 'clihost',
  305. 'port': 'cliport',
  306. 'username': 'cliusername',
  307. 'password': 'clipassword',
  308. },
  309. )
  310. def test_restore_database_dump_without_connection_params_uses_restore_params_in_config_for_restore():
  311. databases_config = [
  312. {
  313. 'name': 'foo',
  314. 'username': 'mongo',
  315. 'password': 'trustsome1',
  316. 'authentication_database': 'admin',
  317. 'schemas': None,
  318. 'restore_hostname': 'restorehost',
  319. 'restore_port': 'restoreport',
  320. 'restore_username': 'restoreuser',
  321. 'restore_password': 'restorepass',
  322. }
  323. ]
  324. extract_process = flexmock(stdout=flexmock())
  325. flexmock(module).should_receive('make_dump_path')
  326. flexmock(module.dump).should_receive('make_database_dump_filename')
  327. flexmock(module).should_receive('execute_command_with_processes').with_args(
  328. [
  329. 'mongorestore',
  330. '--archive',
  331. '--drop',
  332. '--db',
  333. 'foo',
  334. '--host',
  335. 'restorehost',
  336. '--port',
  337. 'restoreport',
  338. '--username',
  339. 'restoreuser',
  340. '--password',
  341. 'restorepass',
  342. '--authenticationDatabase',
  343. 'admin',
  344. ],
  345. processes=[extract_process],
  346. output_log_level=logging.DEBUG,
  347. input_file=extract_process.stdout,
  348. ).once()
  349. module.restore_database_dump(
  350. databases_config,
  351. {},
  352. 'test.yaml',
  353. database_name='foo',
  354. dry_run=False,
  355. extract_process=extract_process,
  356. connection_params={
  357. 'hostname': None,
  358. 'port': None,
  359. 'username': None,
  360. 'password': None,
  361. },
  362. )
  363. def test_restore_database_dump_runs_mongorestore_with_options():
  364. databases_config = [{'name': 'foo', 'restore_options': '--harder', 'schemas': None}]
  365. extract_process = flexmock(stdout=flexmock())
  366. flexmock(module).should_receive('make_dump_path')
  367. flexmock(module.dump).should_receive('make_database_dump_filename')
  368. flexmock(module).should_receive('execute_command_with_processes').with_args(
  369. ['mongorestore', '--archive', '--drop', '--db', 'foo', '--harder'],
  370. processes=[extract_process],
  371. output_log_level=logging.DEBUG,
  372. input_file=extract_process.stdout,
  373. ).once()
  374. module.restore_database_dump(
  375. databases_config,
  376. {},
  377. 'test.yaml',
  378. database_name='foo',
  379. dry_run=False,
  380. extract_process=extract_process,
  381. connection_params={
  382. 'hostname': None,
  383. 'port': None,
  384. 'username': None,
  385. 'password': None,
  386. },
  387. )
  388. def test_restore_databases_dump_runs_mongorestore_with_schemas():
  389. databases_config = [{'name': 'foo', 'schemas': ['bar', 'baz']}]
  390. extract_process = flexmock(stdout=flexmock())
  391. flexmock(module).should_receive('make_dump_path')
  392. flexmock(module.dump).should_receive('make_database_dump_filename')
  393. flexmock(module).should_receive('execute_command_with_processes').with_args(
  394. [
  395. 'mongorestore',
  396. '--archive',
  397. '--drop',
  398. '--db',
  399. 'foo',
  400. '--nsInclude',
  401. 'bar',
  402. '--nsInclude',
  403. 'baz',
  404. ],
  405. processes=[extract_process],
  406. output_log_level=logging.DEBUG,
  407. input_file=extract_process.stdout,
  408. ).once()
  409. module.restore_database_dump(
  410. databases_config,
  411. {},
  412. 'test.yaml',
  413. database_name='foo',
  414. dry_run=False,
  415. extract_process=extract_process,
  416. connection_params={
  417. 'hostname': None,
  418. 'port': None,
  419. 'username': None,
  420. 'password': None,
  421. },
  422. )
  423. def test_restore_database_dump_runs_psql_for_all_database_dump():
  424. databases_config = [{'name': 'all', 'schemas': None}]
  425. extract_process = flexmock(stdout=flexmock())
  426. flexmock(module).should_receive('make_dump_path')
  427. flexmock(module.dump).should_receive('make_database_dump_filename')
  428. flexmock(module).should_receive('execute_command_with_processes').with_args(
  429. ['mongorestore', '--archive'],
  430. processes=[extract_process],
  431. output_log_level=logging.DEBUG,
  432. input_file=extract_process.stdout,
  433. ).once()
  434. module.restore_database_dump(
  435. databases_config,
  436. {},
  437. 'test.yaml',
  438. database_name='all',
  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_with_dry_run_skips_restore():
  449. databases_config = [{'name': 'foo', 'schemas': None}]
  450. flexmock(module).should_receive('make_dump_path')
  451. flexmock(module.dump).should_receive('make_database_dump_filename')
  452. flexmock(module).should_receive('execute_command_with_processes').never()
  453. module.restore_database_dump(
  454. databases_config,
  455. {},
  456. 'test.yaml',
  457. database_name='foo',
  458. dry_run=True,
  459. extract_process=flexmock(),
  460. connection_params={
  461. 'hostname': None,
  462. 'port': None,
  463. 'username': None,
  464. 'password': None,
  465. },
  466. )
  467. def test_restore_database_dump_without_extract_process_restores_from_disk():
  468. databases_config = [{'name': 'foo', 'format': 'directory', 'schemas': None}]
  469. flexmock(module).should_receive('make_dump_path')
  470. flexmock(module.dump).should_receive('make_database_dump_filename').and_return('/dump/path')
  471. flexmock(module).should_receive('execute_command_with_processes').with_args(
  472. ['mongorestore', '--dir', '/dump/path', '--drop', '--db', 'foo'],
  473. processes=[],
  474. output_log_level=logging.DEBUG,
  475. input_file=None,
  476. ).once()
  477. module.restore_database_dump(
  478. databases_config,
  479. {},
  480. 'test.yaml',
  481. database_name='foo',
  482. dry_run=False,
  483. extract_process=None,
  484. connection_params={
  485. 'hostname': None,
  486. 'port': None,
  487. 'username': None,
  488. 'password': None,
  489. },
  490. )