test_mongodb.py 17 KB

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