test_mariadb.py 60 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761
  1. import logging
  2. import pytest
  3. from flexmock import flexmock
  4. from borgmatic.hooks.data_source import mariadb as module
  5. def test_parse_extra_options_passes_through_empty_options():
  6. assert module.parse_extra_options('') == ((), None)
  7. def test_parse_extra_options_with_defaults_extra_file_removes_and_and_parses_out_filename():
  8. assert module.parse_extra_options('--defaults-extra-file=extra.cnf --skip-ssl') == (
  9. ('--skip-ssl',),
  10. 'extra.cnf',
  11. )
  12. def test_parse_extra_options_without_defaults_extra_file_passes_through_options():
  13. assert module.parse_extra_options('--skip-ssl --and=stuff') == (
  14. ('--skip-ssl', '--and=stuff'),
  15. None,
  16. )
  17. def test_make_defaults_file_pipe_without_username_or_password_bails():
  18. flexmock(module.os).should_receive('pipe').never()
  19. assert module.make_defaults_file_options(username=None, password=None) == ()
  20. def test_make_defaults_file_option_with_username_and_password_writes_them_to_file_descriptor():
  21. read_descriptor = 99
  22. write_descriptor = flexmock()
  23. flexmock(module.os).should_receive('pipe').and_return(read_descriptor, write_descriptor)
  24. flexmock(module.os).should_receive('write').with_args(
  25. write_descriptor,
  26. b'[client]\nuser=root\npassword="trustsome1"',
  27. ).once()
  28. flexmock(module.os).should_receive('close')
  29. flexmock(module.os).should_receive('set_inheritable')
  30. assert module.make_defaults_file_options(username='root', password='trustsome1') == (
  31. '--defaults-extra-file=/dev/fd/99',
  32. )
  33. def test_make_defaults_file_escapes_password_containing_backslash():
  34. read_descriptor = 99
  35. write_descriptor = flexmock()
  36. flexmock(module.os).should_receive('pipe').and_return(read_descriptor, write_descriptor)
  37. flexmock(module.os).should_receive('write').with_args(
  38. write_descriptor,
  39. b'[client]\nuser=root\n' + rb'password="trust\\nsome1"',
  40. ).once()
  41. flexmock(module.os).should_receive('close')
  42. flexmock(module.os).should_receive('set_inheritable')
  43. assert module.make_defaults_file_options(username='root', password=r'trust\nsome1') == (
  44. '--defaults-extra-file=/dev/fd/99',
  45. )
  46. def test_make_defaults_file_pipe_with_only_username_writes_it_to_file_descriptor():
  47. read_descriptor = 99
  48. write_descriptor = flexmock()
  49. flexmock(module.os).should_receive('pipe').and_return(read_descriptor, write_descriptor)
  50. flexmock(module.os).should_receive('write').with_args(
  51. write_descriptor,
  52. b'[client]\nuser=root',
  53. ).once()
  54. flexmock(module.os).should_receive('close')
  55. flexmock(module.os).should_receive('set_inheritable')
  56. assert module.make_defaults_file_options(username='root', password=None) == (
  57. '--defaults-extra-file=/dev/fd/99',
  58. )
  59. def test_make_defaults_file_pipe_with_only_password_writes_it_to_file_descriptor():
  60. read_descriptor = 99
  61. write_descriptor = flexmock()
  62. flexmock(module.os).should_receive('pipe').and_return(read_descriptor, write_descriptor)
  63. flexmock(module.os).should_receive('write').with_args(
  64. write_descriptor,
  65. b'[client]\npassword="trustsome1"',
  66. ).once()
  67. flexmock(module.os).should_receive('close')
  68. flexmock(module.os).should_receive('set_inheritable')
  69. assert module.make_defaults_file_options(username=None, password='trustsome1') == (
  70. '--defaults-extra-file=/dev/fd/99',
  71. )
  72. def test_make_defaults_file_option_with_defaults_extra_filename_includes_it_in_file_descriptor():
  73. read_descriptor = 99
  74. write_descriptor = flexmock()
  75. flexmock(module.os).should_receive('pipe').and_return(read_descriptor, write_descriptor)
  76. flexmock(module.os).should_receive('write').with_args(
  77. write_descriptor,
  78. b'!include extra.cnf\n[client]\nuser=root\npassword="trustsome1"',
  79. ).once()
  80. flexmock(module.os).should_receive('close')
  81. flexmock(module.os).should_receive('set_inheritable')
  82. assert module.make_defaults_file_options(
  83. username='root',
  84. password='trustsome1',
  85. defaults_extra_filename='extra.cnf',
  86. ) == ('--defaults-extra-file=/dev/fd/99',)
  87. def test_make_defaults_file_option_with_only_defaults_extra_filename_uses_it_instead_of_file_descriptor():
  88. flexmock(module.os).should_receive('pipe').never()
  89. assert module.make_defaults_file_options(
  90. username=None,
  91. password=None,
  92. defaults_extra_filename='extra.cnf',
  93. ) == ('--defaults-extra-file=extra.cnf',)
  94. def test_database_names_to_dump_passes_through_name():
  95. environment = flexmock()
  96. names = module.database_names_to_dump(
  97. {'name': 'foo'},
  98. {},
  99. 'root',
  100. 'trustsome1',
  101. environment,
  102. dry_run=False,
  103. )
  104. assert names == ('foo',)
  105. def test_database_names_to_dump_with_non_all_name_and_skip_names_warns():
  106. environment = flexmock()
  107. flexmock(module.logger).should_receive('warning').once()
  108. names = module.database_names_to_dump(
  109. {'name': 'foo', 'skip_names': ('foo', 'bar')},
  110. {},
  111. 'root',
  112. 'trustsome1',
  113. environment,
  114. dry_run=False,
  115. )
  116. assert names == ('foo',)
  117. def test_database_names_to_dump_bails_for_dry_run():
  118. environment = flexmock()
  119. flexmock(module).should_receive('execute_command_and_capture_output').never()
  120. names = module.database_names_to_dump(
  121. {'name': 'all'},
  122. {},
  123. 'root',
  124. 'trustsome1',
  125. environment,
  126. dry_run=True,
  127. )
  128. assert names == ()
  129. def test_database_names_to_dump_queries_mariadb_for_database_names():
  130. environment = flexmock()
  131. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  132. 'resolve_credential',
  133. ).replace_with(lambda value, config: value)
  134. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  135. flexmock(module).should_receive('make_defaults_file_options').with_args(
  136. 'root',
  137. 'trustsome1',
  138. None,
  139. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  140. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  141. '/path/to/working/dir'
  142. )
  143. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  144. (
  145. 'mariadb',
  146. '--defaults-extra-file=/dev/fd/99',
  147. '--skip-column-names',
  148. '--batch',
  149. '--execute',
  150. 'show schemas',
  151. ),
  152. environment=environment,
  153. working_directory='/path/to/working/dir',
  154. ).and_return('foo\nbar\nmysql\n').once()
  155. names = module.database_names_to_dump(
  156. {'name': 'all'},
  157. {},
  158. 'root',
  159. 'trustsome1',
  160. environment,
  161. dry_run=False,
  162. )
  163. assert names == ('foo', 'bar')
  164. def test_database_names_to_dump_with_database_name_all_and_skip_names_filters_out_unwanted_databases():
  165. environment = flexmock()
  166. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  167. 'resolve_credential',
  168. ).replace_with(lambda value, config: value)
  169. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  170. flexmock(module).should_receive('make_defaults_file_options').with_args(
  171. 'root',
  172. 'trustsome1',
  173. None,
  174. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  175. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  176. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  177. (
  178. 'mariadb',
  179. '--defaults-extra-file=/dev/fd/99',
  180. '--skip-column-names',
  181. '--batch',
  182. '--execute',
  183. 'show schemas',
  184. ),
  185. environment=environment,
  186. working_directory=None,
  187. ).and_return('foo\nbar\nbaz\nmysql\n').once()
  188. names = module.database_names_to_dump(
  189. {'name': 'all', 'skip_names': ('foo', 'bar')},
  190. {},
  191. 'root',
  192. 'trustsome1',
  193. environment,
  194. dry_run=False,
  195. )
  196. assert names == ('baz',)
  197. def test_database_names_to_dump_with_environment_password_transport_skips_defaults_file_and_passes_user_flag():
  198. environment = flexmock()
  199. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  200. 'resolve_credential',
  201. ).replace_with(lambda value, config: value)
  202. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  203. flexmock(module).should_receive('make_defaults_file_options').never()
  204. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  205. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  206. (
  207. 'mariadb',
  208. '--user',
  209. 'root',
  210. '--skip-column-names',
  211. '--batch',
  212. '--execute',
  213. 'show schemas',
  214. ),
  215. environment=environment,
  216. working_directory=None,
  217. ).and_return('foo\nbar\nmysql\n').once()
  218. names = module.database_names_to_dump(
  219. {'name': 'all', 'password_transport': 'environment'},
  220. {},
  221. 'root',
  222. 'trustsome1',
  223. environment,
  224. dry_run=False,
  225. )
  226. assert names == ('foo', 'bar')
  227. def test_database_names_to_dump_runs_mariadb_with_tls():
  228. environment = flexmock()
  229. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  230. 'resolve_credential',
  231. ).replace_with(lambda value, config: value)
  232. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  233. flexmock(module).should_receive('make_defaults_file_options').with_args(
  234. 'root',
  235. 'trustsome1',
  236. None,
  237. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  238. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  239. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  240. (
  241. 'mariadb',
  242. '--defaults-extra-file=/dev/fd/99',
  243. '--ssl',
  244. '--skip-column-names',
  245. '--batch',
  246. '--execute',
  247. 'show schemas',
  248. ),
  249. environment=environment,
  250. working_directory=None,
  251. ).and_return('foo\nbar\nmysql\n').once()
  252. names = module.database_names_to_dump(
  253. {'name': 'all', 'tls': True},
  254. {},
  255. 'root',
  256. 'trustsome1',
  257. environment,
  258. dry_run=False,
  259. )
  260. assert names == ('foo', 'bar')
  261. def test_database_names_to_dump_runs_mariadb_without_tls():
  262. environment = flexmock()
  263. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  264. 'resolve_credential',
  265. ).replace_with(lambda value, config: value)
  266. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  267. flexmock(module).should_receive('make_defaults_file_options').with_args(
  268. 'root',
  269. 'trustsome1',
  270. None,
  271. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  272. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  273. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  274. (
  275. 'mariadb',
  276. '--defaults-extra-file=/dev/fd/99',
  277. '--skip-ssl',
  278. '--skip-column-names',
  279. '--batch',
  280. '--execute',
  281. 'show schemas',
  282. ),
  283. environment=environment,
  284. working_directory=None,
  285. ).and_return('foo\nbar\nmysql\n').once()
  286. names = module.database_names_to_dump(
  287. {'name': 'all', 'tls': False},
  288. {},
  289. 'root',
  290. 'trustsome1',
  291. environment,
  292. dry_run=False,
  293. )
  294. assert names == ('foo', 'bar')
  295. def test_use_streaming_true_for_any_databases():
  296. assert module.use_streaming(
  297. databases=[flexmock(), flexmock()],
  298. config=flexmock(),
  299. )
  300. def test_use_streaming_false_for_no_databases():
  301. assert not module.use_streaming(databases=[], config=flexmock())
  302. def test_dump_data_sources_dumps_each_database():
  303. databases = [{'name': 'foo'}, {'name': 'bar'}]
  304. processes = [flexmock(), flexmock()]
  305. flexmock(module).should_receive('make_dump_path').and_return('')
  306. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  307. 'resolve_credential',
  308. ).and_return(None)
  309. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  310. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  311. 'resolve_credential',
  312. ).replace_with(lambda value, config: value)
  313. flexmock(module).should_receive('database_names_to_dump').with_args(
  314. database=databases[0],
  315. config={},
  316. username=None,
  317. password=None,
  318. environment={'USER': 'root'},
  319. dry_run=False,
  320. ).and_return(('foo',))
  321. flexmock(module).should_receive('database_names_to_dump').with_args(
  322. database=databases[1],
  323. config={},
  324. username=None,
  325. password=None,
  326. environment={'USER': 'root'},
  327. dry_run=False,
  328. ).and_return(('bar',))
  329. for name, process in zip(('foo', 'bar'), processes):
  330. flexmock(module).should_receive('execute_dump_command').with_args(
  331. database={'name': name},
  332. config={},
  333. username=None,
  334. password=None,
  335. dump_path=object,
  336. database_names=(name,),
  337. environment={'USER': 'root'},
  338. dry_run=object,
  339. dry_run_label=object,
  340. ).and_return(process).once()
  341. flexmock(module.dump).should_receive('write_data_source_dumps_metadata').with_args(
  342. '/run/borgmatic',
  343. 'mariadb_databases',
  344. [
  345. module.borgmatic.actions.restore.Dump('mariadb_databases', 'foo'),
  346. module.borgmatic.actions.restore.Dump('mariadb_databases', 'bar'),
  347. ],
  348. ).once()
  349. flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').with_args(
  350. object,
  351. module.borgmatic.borg.pattern.Pattern(
  352. '/run/borgmatic/mariadb_databases',
  353. source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
  354. ),
  355. ).once()
  356. assert (
  357. module.dump_data_sources(
  358. databases,
  359. {},
  360. config_paths=('test.yaml',),
  361. borgmatic_runtime_directory='/run/borgmatic',
  362. patterns=[],
  363. dry_run=False,
  364. )
  365. == processes
  366. )
  367. def test_dump_data_sources_dumps_with_password():
  368. database = {'name': 'foo', 'username': 'root', 'password': 'trustsome1'}
  369. process = flexmock()
  370. flexmock(module).should_receive('make_dump_path').and_return('')
  371. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  372. 'resolve_credential',
  373. ).replace_with(lambda value, config: value)
  374. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  375. flexmock(module).should_receive('database_names_to_dump').with_args(
  376. database=database,
  377. config={},
  378. username='root',
  379. password='trustsome1',
  380. environment={'USER': 'root'},
  381. dry_run=False,
  382. ).and_return(('foo',))
  383. flexmock(module).should_receive('execute_dump_command').with_args(
  384. database=database,
  385. config={},
  386. username='root',
  387. password='trustsome1',
  388. dump_path=object,
  389. database_names=('foo',),
  390. environment={'USER': 'root'},
  391. dry_run=object,
  392. dry_run_label=object,
  393. ).and_return(process).once()
  394. flexmock(module.dump).should_receive('write_data_source_dumps_metadata').with_args(
  395. '/run/borgmatic',
  396. 'mariadb_databases',
  397. [
  398. module.borgmatic.actions.restore.Dump('mariadb_databases', 'foo'),
  399. ],
  400. ).once()
  401. flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').with_args(
  402. object,
  403. module.borgmatic.borg.pattern.Pattern(
  404. '/run/borgmatic/mariadb_databases',
  405. source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
  406. ),
  407. ).once()
  408. assert module.dump_data_sources(
  409. [database],
  410. {},
  411. config_paths=('test.yaml',),
  412. borgmatic_runtime_directory='/run/borgmatic',
  413. patterns=[],
  414. dry_run=False,
  415. ) == [process]
  416. def test_dump_data_sources_dumps_with_environment_password_transport_passes_password_environment_variable():
  417. database = {
  418. 'name': 'foo',
  419. 'username': 'root',
  420. 'password': 'trustsome1',
  421. 'password_transport': 'environment',
  422. }
  423. process = flexmock()
  424. flexmock(module).should_receive('make_dump_path').and_return('')
  425. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  426. 'resolve_credential',
  427. ).replace_with(lambda value, config: value)
  428. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  429. flexmock(module).should_receive('database_names_to_dump').with_args(
  430. database=database,
  431. config={},
  432. username='root',
  433. password='trustsome1',
  434. environment={'USER': 'root', 'MYSQL_PWD': 'trustsome1'},
  435. dry_run=False,
  436. ).and_return(('foo',))
  437. flexmock(module).should_receive('execute_dump_command').with_args(
  438. database=database,
  439. config={},
  440. username='root',
  441. password='trustsome1',
  442. dump_path=object,
  443. database_names=('foo',),
  444. environment={'USER': 'root', 'MYSQL_PWD': 'trustsome1'},
  445. dry_run=object,
  446. dry_run_label=object,
  447. ).and_return(process).once()
  448. flexmock(module.dump).should_receive('write_data_source_dumps_metadata').with_args(
  449. '/run/borgmatic',
  450. 'mariadb_databases',
  451. [
  452. module.borgmatic.actions.restore.Dump('mariadb_databases', 'foo'),
  453. ],
  454. ).once()
  455. flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').with_args(
  456. object,
  457. module.borgmatic.borg.pattern.Pattern(
  458. '/run/borgmatic/mariadb_databases',
  459. source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
  460. ),
  461. ).once()
  462. assert module.dump_data_sources(
  463. [database],
  464. {},
  465. config_paths=('test.yaml',),
  466. borgmatic_runtime_directory='/run/borgmatic',
  467. patterns=[],
  468. dry_run=False,
  469. ) == [process]
  470. def test_dump_data_sources_dumps_all_databases_at_once():
  471. databases = [{'name': 'all'}]
  472. process = flexmock()
  473. flexmock(module).should_receive('make_dump_path').and_return('')
  474. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  475. 'resolve_credential',
  476. ).replace_with(lambda value, config: value)
  477. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  478. flexmock(module).should_receive('database_names_to_dump').and_return(('foo', 'bar'))
  479. flexmock(module).should_receive('execute_dump_command').with_args(
  480. database={'name': 'all'},
  481. config={},
  482. username=None,
  483. password=None,
  484. dump_path=object,
  485. database_names=('foo', 'bar'),
  486. environment={'USER': 'root'},
  487. dry_run=object,
  488. dry_run_label=object,
  489. ).and_return(process).once()
  490. flexmock(module.dump).should_receive('write_data_source_dumps_metadata').with_args(
  491. '/run/borgmatic',
  492. 'mariadb_databases',
  493. [
  494. module.borgmatic.actions.restore.Dump('mariadb_databases', 'all'),
  495. ],
  496. ).once()
  497. flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').with_args(
  498. object,
  499. module.borgmatic.borg.pattern.Pattern(
  500. '/run/borgmatic/mariadb_databases',
  501. source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
  502. ),
  503. ).once()
  504. assert module.dump_data_sources(
  505. databases,
  506. {},
  507. config_paths=('test.yaml',),
  508. borgmatic_runtime_directory='/run/borgmatic',
  509. patterns=[],
  510. dry_run=False,
  511. ) == [process]
  512. def test_dump_data_sources_dumps_all_databases_separately_when_format_configured():
  513. databases = [{'name': 'all', 'format': 'sql'}]
  514. processes = [flexmock(), flexmock()]
  515. flexmock(module).should_receive('make_dump_path').and_return('')
  516. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  517. 'resolve_credential',
  518. ).and_return(None)
  519. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  520. flexmock(module).should_receive('database_names_to_dump').and_return(('foo', 'bar'))
  521. for name, process in zip(('foo', 'bar'), processes):
  522. flexmock(module).should_receive('execute_dump_command').with_args(
  523. database={'name': name, 'format': 'sql'},
  524. config={},
  525. username=None,
  526. password=None,
  527. dump_path=object,
  528. database_names=(name,),
  529. environment={'USER': 'root'},
  530. dry_run=object,
  531. dry_run_label=object,
  532. ).and_return(process).once()
  533. flexmock(module.dump).should_receive('write_data_source_dumps_metadata').with_args(
  534. '/run/borgmatic',
  535. 'mariadb_databases',
  536. [
  537. module.borgmatic.actions.restore.Dump('mariadb_databases', 'foo'),
  538. module.borgmatic.actions.restore.Dump('mariadb_databases', 'bar'),
  539. ],
  540. ).once()
  541. flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').with_args(
  542. object,
  543. module.borgmatic.borg.pattern.Pattern(
  544. '/run/borgmatic/mariadb_databases',
  545. source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
  546. ),
  547. ).once()
  548. assert (
  549. module.dump_data_sources(
  550. databases,
  551. {},
  552. config_paths=('test.yaml',),
  553. borgmatic_runtime_directory='/run/borgmatic',
  554. patterns=[],
  555. dry_run=False,
  556. )
  557. == processes
  558. )
  559. def test_dump_data_sources_errors_for_missing_all_databases():
  560. databases = [{'name': 'all'}]
  561. flexmock(module).should_receive('make_dump_path').and_return('')
  562. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  563. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  564. 'resolve_credential',
  565. ).replace_with(lambda value, config: value)
  566. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
  567. 'databases/localhost/all',
  568. )
  569. flexmock(module).should_receive('database_names_to_dump').and_return(())
  570. flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').never()
  571. with pytest.raises(ValueError):
  572. assert module.dump_data_sources(
  573. databases,
  574. {},
  575. config_paths=('test.yaml',),
  576. borgmatic_runtime_directory='/run/borgmatic',
  577. patterns=[],
  578. dry_run=False,
  579. )
  580. def test_dump_data_sources_does_not_error_for_missing_all_databases_with_dry_run():
  581. databases = [{'name': 'all'}]
  582. flexmock(module).should_receive('make_dump_path').and_return('')
  583. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  584. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  585. 'resolve_credential',
  586. ).replace_with(lambda value, config: value)
  587. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return(
  588. 'databases/localhost/all',
  589. )
  590. flexmock(module).should_receive('database_names_to_dump').and_return(())
  591. flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').never()
  592. assert (
  593. module.dump_data_sources(
  594. databases,
  595. {},
  596. config_paths=('test.yaml',),
  597. borgmatic_runtime_directory='/run/borgmatic',
  598. patterns=[],
  599. dry_run=True,
  600. )
  601. == []
  602. )
  603. def test_database_names_to_dump_runs_mariadb_with_list_options():
  604. database = {'name': 'all', 'list_options': '--defaults-extra-file=mariadb.cnf --skip-ssl'}
  605. flexmock(module).should_receive('parse_extra_options').and_return(
  606. ('--skip-ssl',),
  607. 'mariadb.cnf',
  608. )
  609. flexmock(module).should_receive('make_defaults_file_options').with_args(
  610. 'root',
  611. 'trustsome1',
  612. 'mariadb.cnf',
  613. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  614. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  615. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  616. (
  617. 'mariadb',
  618. '--defaults-extra-file=/dev/fd/99',
  619. '--skip-ssl',
  620. '--skip-column-names',
  621. '--batch',
  622. '--execute',
  623. 'show schemas',
  624. ),
  625. environment=None,
  626. working_directory=None,
  627. ).and_return('foo\nbar').once()
  628. assert module.database_names_to_dump(database, {}, 'root', 'trustsome1', None, '') == (
  629. 'foo',
  630. 'bar',
  631. )
  632. def test_database_names_to_dump_runs_non_default_mariadb_with_list_options():
  633. database = {
  634. 'name': 'all',
  635. 'list_options': '--defaults-extra-file=mariadb.cnf --skip-ssl',
  636. 'mariadb_command': 'custom_mariadb',
  637. }
  638. flexmock(module).should_receive('parse_extra_options').and_return(
  639. ('--skip-ssl',),
  640. 'mariadb.cnf',
  641. )
  642. flexmock(module).should_receive('make_defaults_file_options').with_args(
  643. 'root',
  644. 'trustsome1',
  645. 'mariadb.cnf',
  646. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  647. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  648. flexmock(module).should_receive('execute_command_and_capture_output').with_args(
  649. environment=None,
  650. full_command=(
  651. 'custom_mariadb', # Custom MariaDB command
  652. '--defaults-extra-file=/dev/fd/99',
  653. '--skip-ssl',
  654. '--skip-column-names',
  655. '--batch',
  656. '--execute',
  657. 'show schemas',
  658. ),
  659. working_directory=None,
  660. ).and_return('foo\nbar').once()
  661. assert module.database_names_to_dump(database, {}, 'root', 'trustsome1', None, '') == (
  662. 'foo',
  663. 'bar',
  664. )
  665. def test_execute_dump_command_runs_mariadb_dump():
  666. process = flexmock()
  667. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
  668. flexmock(module.os.path).should_receive('exists').and_return(False)
  669. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  670. 'resolve_credential',
  671. ).replace_with(lambda value, config: value)
  672. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  673. flexmock(module).should_receive('make_defaults_file_options').with_args(
  674. 'root',
  675. 'trustsome1',
  676. None,
  677. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  678. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  679. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  680. flexmock(module).should_receive('execute_command').with_args(
  681. (
  682. 'mariadb-dump',
  683. '--defaults-extra-file=/dev/fd/99',
  684. '--add-drop-database',
  685. '--databases',
  686. 'foo',
  687. '--result-file',
  688. 'dump',
  689. ),
  690. environment=None,
  691. run_to_completion=False,
  692. working_directory=None,
  693. ).and_return(process).once()
  694. assert (
  695. module.execute_dump_command(
  696. database={'name': 'foo'},
  697. config={},
  698. username='root',
  699. password='trustsome1',
  700. dump_path=flexmock(),
  701. database_names=('foo',),
  702. environment=None,
  703. dry_run=False,
  704. dry_run_label='',
  705. )
  706. == process
  707. )
  708. def test_execute_dump_command_with_environment_password_transport_skips_defaults_file_and_passes_user_flag():
  709. process = flexmock()
  710. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
  711. flexmock(module.os.path).should_receive('exists').and_return(False)
  712. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  713. 'resolve_credential',
  714. ).replace_with(lambda value, config: value)
  715. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  716. flexmock(module).should_receive('make_defaults_file_options').never()
  717. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  718. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  719. flexmock(module).should_receive('execute_command').with_args(
  720. (
  721. 'mariadb-dump',
  722. '--add-drop-database',
  723. '--user',
  724. 'root',
  725. '--databases',
  726. 'foo',
  727. '--result-file',
  728. 'dump',
  729. ),
  730. environment=None,
  731. run_to_completion=False,
  732. working_directory=None,
  733. ).and_return(process).once()
  734. assert (
  735. module.execute_dump_command(
  736. database={'name': 'foo', 'password_transport': 'environment'},
  737. config={},
  738. username='root',
  739. password='trustsome1',
  740. dump_path=flexmock(),
  741. database_names=('foo',),
  742. environment=None,
  743. dry_run=False,
  744. dry_run_label='',
  745. )
  746. == process
  747. )
  748. def test_execute_dump_command_runs_mariadb_dump_without_add_drop_database():
  749. process = flexmock()
  750. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
  751. flexmock(module.os.path).should_receive('exists').and_return(False)
  752. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  753. 'resolve_credential',
  754. ).replace_with(lambda value, config: value)
  755. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  756. flexmock(module).should_receive('make_defaults_file_options').with_args(
  757. 'root',
  758. 'trustsome1',
  759. None,
  760. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  761. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  762. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  763. flexmock(module).should_receive('execute_command').with_args(
  764. (
  765. 'mariadb-dump',
  766. '--defaults-extra-file=/dev/fd/99',
  767. '--databases',
  768. 'foo',
  769. '--result-file',
  770. 'dump',
  771. ),
  772. environment=None,
  773. run_to_completion=False,
  774. working_directory=None,
  775. ).and_return(process).once()
  776. assert (
  777. module.execute_dump_command(
  778. database={'name': 'foo', 'add_drop_database': False},
  779. config={},
  780. username='root',
  781. password='trustsome1',
  782. dump_path=flexmock(),
  783. database_names=('foo',),
  784. environment=None,
  785. dry_run=False,
  786. dry_run_label='',
  787. )
  788. == process
  789. )
  790. def test_execute_dump_command_runs_mariadb_dump_with_hostname_and_port():
  791. process = flexmock()
  792. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
  793. flexmock(module.os.path).should_receive('exists').and_return(False)
  794. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  795. 'resolve_credential',
  796. ).replace_with(lambda value, config: value)
  797. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  798. flexmock(module).should_receive('make_defaults_file_options').with_args(
  799. 'root',
  800. 'trustsome1',
  801. None,
  802. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  803. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  804. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  805. flexmock(module).should_receive('execute_command').with_args(
  806. (
  807. 'mariadb-dump',
  808. '--defaults-extra-file=/dev/fd/99',
  809. '--add-drop-database',
  810. '--host',
  811. 'database.example.org',
  812. '--port',
  813. '5433',
  814. '--protocol',
  815. 'tcp',
  816. '--databases',
  817. 'foo',
  818. '--result-file',
  819. 'dump',
  820. ),
  821. environment=None,
  822. run_to_completion=False,
  823. working_directory=None,
  824. ).and_return(process).once()
  825. assert (
  826. module.execute_dump_command(
  827. database={'name': 'foo', 'hostname': 'database.example.org', 'port': 5433},
  828. config={},
  829. username='root',
  830. password='trustsome1',
  831. dump_path=flexmock(),
  832. database_names=('foo',),
  833. environment=None,
  834. dry_run=False,
  835. dry_run_label='',
  836. )
  837. == process
  838. )
  839. def test_execute_dump_command_runs_mariadb_dump_with_tls():
  840. process = flexmock()
  841. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
  842. flexmock(module.os.path).should_receive('exists').and_return(False)
  843. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  844. 'resolve_credential',
  845. ).replace_with(lambda value, config: value)
  846. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  847. flexmock(module).should_receive('make_defaults_file_options').with_args(
  848. 'root',
  849. 'trustsome1',
  850. None,
  851. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  852. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  853. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  854. flexmock(module).should_receive('execute_command').with_args(
  855. (
  856. 'mariadb-dump',
  857. '--defaults-extra-file=/dev/fd/99',
  858. '--add-drop-database',
  859. '--ssl',
  860. '--databases',
  861. 'foo',
  862. '--result-file',
  863. 'dump',
  864. ),
  865. environment=None,
  866. run_to_completion=False,
  867. working_directory=None,
  868. ).and_return(process).once()
  869. assert (
  870. module.execute_dump_command(
  871. database={'name': 'foo', 'tls': True},
  872. config={},
  873. username='root',
  874. password='trustsome1',
  875. dump_path=flexmock(),
  876. database_names=('foo',),
  877. environment=None,
  878. dry_run=False,
  879. dry_run_label='',
  880. )
  881. == process
  882. )
  883. def test_execute_dump_command_runs_mariadb_dump_without_tls():
  884. process = flexmock()
  885. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
  886. flexmock(module.os.path).should_receive('exists').and_return(False)
  887. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  888. 'resolve_credential',
  889. ).replace_with(lambda value, config: value)
  890. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  891. flexmock(module).should_receive('make_defaults_file_options').with_args(
  892. 'root',
  893. 'trustsome1',
  894. None,
  895. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  896. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  897. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  898. flexmock(module).should_receive('execute_command').with_args(
  899. (
  900. 'mariadb-dump',
  901. '--defaults-extra-file=/dev/fd/99',
  902. '--add-drop-database',
  903. '--skip-ssl',
  904. '--databases',
  905. 'foo',
  906. '--result-file',
  907. 'dump',
  908. ),
  909. environment=None,
  910. run_to_completion=False,
  911. working_directory=None,
  912. ).and_return(process).once()
  913. assert (
  914. module.execute_dump_command(
  915. database={'name': 'foo', 'tls': False},
  916. config={},
  917. username='root',
  918. password='trustsome1',
  919. dump_path=flexmock(),
  920. database_names=('foo',),
  921. environment=None,
  922. dry_run=False,
  923. dry_run_label='',
  924. )
  925. == process
  926. )
  927. def test_execute_dump_command_runs_mariadb_dump_with_username_and_password():
  928. process = flexmock()
  929. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
  930. flexmock(module.os.path).should_receive('exists').and_return(False)
  931. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  932. 'resolve_credential',
  933. ).replace_with(lambda value, config: value)
  934. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  935. flexmock(module).should_receive('make_defaults_file_options').with_args(
  936. 'root',
  937. 'trustsome1',
  938. None,
  939. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  940. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  941. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  942. flexmock(module).should_receive('execute_command').with_args(
  943. (
  944. 'mariadb-dump',
  945. '--defaults-extra-file=/dev/fd/99',
  946. '--add-drop-database',
  947. '--databases',
  948. 'foo',
  949. '--result-file',
  950. 'dump',
  951. ),
  952. environment={},
  953. run_to_completion=False,
  954. working_directory=None,
  955. ).and_return(process).once()
  956. assert (
  957. module.execute_dump_command(
  958. database={'name': 'foo', 'username': 'root', 'password': 'trustsome1'},
  959. config={},
  960. username='root',
  961. password='trustsome1',
  962. dump_path=flexmock(),
  963. database_names=('foo',),
  964. environment={},
  965. dry_run=False,
  966. dry_run_label='',
  967. )
  968. == process
  969. )
  970. def test_execute_dump_command_runs_mariadb_dump_with_options():
  971. process = flexmock()
  972. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
  973. flexmock(module.os.path).should_receive('exists').and_return(False)
  974. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  975. 'resolve_credential',
  976. ).replace_with(lambda value, config: value)
  977. flexmock(module).should_receive('parse_extra_options').and_return(('--stuff=such',), None)
  978. flexmock(module).should_receive('make_defaults_file_options').with_args(
  979. 'root',
  980. 'trustsome1',
  981. None,
  982. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  983. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  984. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  985. flexmock(module).should_receive('execute_command').with_args(
  986. (
  987. 'mariadb-dump',
  988. '--defaults-extra-file=/dev/fd/99',
  989. '--stuff=such',
  990. '--add-drop-database',
  991. '--databases',
  992. 'foo',
  993. '--result-file',
  994. 'dump',
  995. ),
  996. environment=None,
  997. run_to_completion=False,
  998. working_directory=None,
  999. ).and_return(process).once()
  1000. assert (
  1001. module.execute_dump_command(
  1002. database={'name': 'foo', 'options': '--stuff=such'},
  1003. config={},
  1004. username='root',
  1005. password='trustsome1',
  1006. dump_path=flexmock(),
  1007. database_names=('foo',),
  1008. environment=None,
  1009. dry_run=False,
  1010. dry_run_label='',
  1011. )
  1012. == process
  1013. )
  1014. def test_execute_dump_command_runs_non_default_mariadb_dump_with_options():
  1015. process = flexmock()
  1016. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
  1017. flexmock(module.os.path).should_receive('exists').and_return(False)
  1018. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1019. 'resolve_credential',
  1020. ).replace_with(lambda value, config: value)
  1021. flexmock(module).should_receive('parse_extra_options').and_return(('--stuff=such',), None)
  1022. flexmock(module).should_receive('make_defaults_file_options').with_args(
  1023. 'root',
  1024. 'trustsome1',
  1025. None,
  1026. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  1027. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  1028. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1029. flexmock(module).should_receive('execute_command').with_args(
  1030. (
  1031. 'custom_mariadb_dump', # Custom MariaDB dump command
  1032. '--defaults-extra-file=/dev/fd/99',
  1033. '--stuff=such',
  1034. '--add-drop-database',
  1035. '--databases',
  1036. 'foo',
  1037. '--result-file',
  1038. 'dump',
  1039. ),
  1040. environment=None,
  1041. run_to_completion=False,
  1042. working_directory=None,
  1043. ).and_return(process).once()
  1044. assert (
  1045. module.execute_dump_command(
  1046. database={
  1047. 'name': 'foo',
  1048. 'mariadb_dump_command': 'custom_mariadb_dump',
  1049. 'options': '--stuff=such',
  1050. }, # Custom MariaDB dump command specified
  1051. config={},
  1052. username='root',
  1053. password='trustsome1',
  1054. dump_path=flexmock(),
  1055. database_names=('foo',),
  1056. environment=None,
  1057. dry_run=False,
  1058. dry_run_label='',
  1059. )
  1060. == process
  1061. )
  1062. def test_execute_dump_command_with_duplicate_dump_skips_mariadb_dump():
  1063. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
  1064. flexmock(module.os.path).should_receive('exists').and_return(True)
  1065. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  1066. flexmock(module).should_receive('make_defaults_file_options').with_args(
  1067. 'root',
  1068. 'trustsome1',
  1069. None,
  1070. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  1071. flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
  1072. flexmock(module).should_receive('execute_command').never()
  1073. assert (
  1074. module.execute_dump_command(
  1075. database={'name': 'foo'},
  1076. config={},
  1077. username='root',
  1078. password='trustsome1',
  1079. dump_path=flexmock(),
  1080. database_names=('foo',),
  1081. environment=None,
  1082. dry_run=True,
  1083. dry_run_label='SO DRY',
  1084. )
  1085. is None
  1086. )
  1087. def test_execute_dump_command_with_dry_run_skips_mariadb_dump():
  1088. flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
  1089. flexmock(module.os.path).should_receive('exists').and_return(False)
  1090. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1091. 'resolve_credential',
  1092. ).replace_with(lambda value, config: value)
  1093. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  1094. flexmock(module).should_receive('make_defaults_file_options').with_args(
  1095. 'root',
  1096. 'trustsome1',
  1097. None,
  1098. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  1099. flexmock(module.dump).should_receive('create_named_pipe_for_dump')
  1100. flexmock(module).should_receive('execute_command').never()
  1101. assert (
  1102. module.execute_dump_command(
  1103. database={'name': 'foo'},
  1104. config={},
  1105. username='root',
  1106. password='trustsome1',
  1107. dump_path=flexmock(),
  1108. database_names=('foo',),
  1109. environment=None,
  1110. dry_run=True,
  1111. dry_run_label='SO DRY',
  1112. )
  1113. is None
  1114. )
  1115. def test_restore_data_source_dump_runs_mariadb_to_restore():
  1116. hook_config = [{'name': 'foo'}, {'name': 'bar'}]
  1117. extract_process = flexmock(stdout=flexmock())
  1118. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1119. 'resolve_credential',
  1120. ).replace_with(lambda value, config: value)
  1121. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  1122. flexmock(module).should_receive('make_defaults_file_options').with_args(
  1123. None,
  1124. None,
  1125. None,
  1126. ).and_return(())
  1127. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  1128. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1129. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1130. ('mariadb', '--batch'),
  1131. processes=[extract_process],
  1132. output_log_level=logging.DEBUG,
  1133. input_file=extract_process.stdout,
  1134. environment={'USER': 'root'},
  1135. working_directory=None,
  1136. ).once()
  1137. module.restore_data_source_dump(
  1138. hook_config,
  1139. {},
  1140. data_source={'name': 'foo'},
  1141. dry_run=False,
  1142. extract_process=extract_process,
  1143. connection_params={
  1144. 'hostname': None,
  1145. 'port': None,
  1146. 'username': None,
  1147. 'password': None,
  1148. },
  1149. borgmatic_runtime_directory='/run/borgmatic',
  1150. )
  1151. def test_restore_data_source_dump_runs_mariadb_with_options():
  1152. hook_config = [{'name': 'foo', 'restore_options': '--harder'}]
  1153. extract_process = flexmock(stdout=flexmock())
  1154. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1155. 'resolve_credential',
  1156. ).replace_with(lambda value, config: value)
  1157. flexmock(module).should_receive('parse_extra_options').and_return(('--harder',), None)
  1158. flexmock(module).should_receive('make_defaults_file_options').with_args(
  1159. None,
  1160. None,
  1161. None,
  1162. ).and_return(())
  1163. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  1164. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1165. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1166. ('mariadb', '--harder', '--batch'),
  1167. processes=[extract_process],
  1168. output_log_level=logging.DEBUG,
  1169. input_file=extract_process.stdout,
  1170. environment={'USER': 'root'},
  1171. working_directory=None,
  1172. ).once()
  1173. module.restore_data_source_dump(
  1174. hook_config,
  1175. {},
  1176. data_source=hook_config[0],
  1177. dry_run=False,
  1178. extract_process=extract_process,
  1179. connection_params={
  1180. 'hostname': None,
  1181. 'port': None,
  1182. 'username': None,
  1183. 'password': None,
  1184. },
  1185. borgmatic_runtime_directory='/run/borgmatic',
  1186. )
  1187. def test_restore_data_source_dump_runs_non_default_mariadb_with_options():
  1188. hook_config = [
  1189. {'name': 'foo', 'restore_options': '--harder', 'mariadb_command': 'custom_mariadb'},
  1190. ]
  1191. extract_process = flexmock(stdout=flexmock())
  1192. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1193. 'resolve_credential',
  1194. ).replace_with(lambda value, config: value)
  1195. flexmock(module).should_receive('parse_extra_options').and_return(('--harder',), None)
  1196. flexmock(module).should_receive('make_defaults_file_options').with_args(
  1197. None,
  1198. None,
  1199. None,
  1200. ).and_return(())
  1201. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  1202. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1203. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1204. ('custom_mariadb', '--harder', '--batch'),
  1205. processes=[extract_process],
  1206. output_log_level=logging.DEBUG,
  1207. input_file=extract_process.stdout,
  1208. environment={'USER': 'root'},
  1209. working_directory=None,
  1210. ).once()
  1211. module.restore_data_source_dump(
  1212. hook_config,
  1213. {},
  1214. data_source=hook_config[0],
  1215. dry_run=False,
  1216. extract_process=extract_process,
  1217. connection_params={
  1218. 'hostname': None,
  1219. 'port': None,
  1220. 'username': None,
  1221. 'password': None,
  1222. },
  1223. borgmatic_runtime_directory='/run/borgmatic',
  1224. )
  1225. def test_restore_data_source_dump_runs_mariadb_with_hostname_and_port():
  1226. hook_config = [{'name': 'foo', 'hostname': 'database.example.org', 'port': 5433}]
  1227. extract_process = flexmock(stdout=flexmock())
  1228. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1229. 'resolve_credential',
  1230. ).replace_with(lambda value, config: value)
  1231. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  1232. flexmock(module).should_receive('make_defaults_file_options').with_args(
  1233. None,
  1234. None,
  1235. None,
  1236. ).and_return(())
  1237. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  1238. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1239. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1240. (
  1241. 'mariadb',
  1242. '--batch',
  1243. '--host',
  1244. 'database.example.org',
  1245. '--port',
  1246. '5433',
  1247. '--protocol',
  1248. 'tcp',
  1249. ),
  1250. processes=[extract_process],
  1251. output_log_level=logging.DEBUG,
  1252. input_file=extract_process.stdout,
  1253. environment={'USER': 'root'},
  1254. working_directory=None,
  1255. ).once()
  1256. module.restore_data_source_dump(
  1257. hook_config,
  1258. {},
  1259. data_source=hook_config[0],
  1260. dry_run=False,
  1261. extract_process=extract_process,
  1262. connection_params={
  1263. 'hostname': None,
  1264. 'port': None,
  1265. 'username': None,
  1266. 'password': None,
  1267. },
  1268. borgmatic_runtime_directory='/run/borgmatic',
  1269. )
  1270. def test_restore_data_source_dump_runs_mariadb_with_tls():
  1271. hook_config = [{'name': 'foo', 'tls': True}]
  1272. extract_process = flexmock(stdout=flexmock())
  1273. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1274. 'resolve_credential',
  1275. ).replace_with(lambda value, config: value)
  1276. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  1277. flexmock(module).should_receive('make_defaults_file_options').with_args(
  1278. None,
  1279. None,
  1280. None,
  1281. ).and_return(())
  1282. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  1283. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1284. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1285. (
  1286. 'mariadb',
  1287. '--batch',
  1288. '--ssl',
  1289. ),
  1290. processes=[extract_process],
  1291. output_log_level=logging.DEBUG,
  1292. input_file=extract_process.stdout,
  1293. environment={'USER': 'root'},
  1294. working_directory=None,
  1295. ).once()
  1296. module.restore_data_source_dump(
  1297. hook_config,
  1298. {},
  1299. data_source=hook_config[0],
  1300. dry_run=False,
  1301. extract_process=extract_process,
  1302. connection_params={
  1303. 'hostname': None,
  1304. 'port': None,
  1305. 'username': None,
  1306. 'password': None,
  1307. },
  1308. borgmatic_runtime_directory='/run/borgmatic',
  1309. )
  1310. def test_restore_data_source_dump_runs_mariadb_without_tls():
  1311. hook_config = [{'name': 'foo', 'tls': False}]
  1312. extract_process = flexmock(stdout=flexmock())
  1313. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1314. 'resolve_credential',
  1315. ).replace_with(lambda value, config: value)
  1316. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  1317. flexmock(module).should_receive('make_defaults_file_options').with_args(
  1318. None,
  1319. None,
  1320. None,
  1321. ).and_return(())
  1322. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  1323. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1324. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1325. (
  1326. 'mariadb',
  1327. '--batch',
  1328. '--skip-ssl',
  1329. ),
  1330. processes=[extract_process],
  1331. output_log_level=logging.DEBUG,
  1332. input_file=extract_process.stdout,
  1333. environment={'USER': 'root'},
  1334. working_directory=None,
  1335. ).once()
  1336. module.restore_data_source_dump(
  1337. hook_config,
  1338. {},
  1339. data_source=hook_config[0],
  1340. dry_run=False,
  1341. extract_process=extract_process,
  1342. connection_params={
  1343. 'hostname': None,
  1344. 'port': None,
  1345. 'username': None,
  1346. 'password': None,
  1347. },
  1348. borgmatic_runtime_directory='/run/borgmatic',
  1349. )
  1350. def test_restore_data_source_dump_runs_mariadb_with_username_and_password():
  1351. hook_config = [{'name': 'foo', 'username': 'root', 'password': 'trustsome1'}]
  1352. extract_process = flexmock(stdout=flexmock())
  1353. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1354. 'resolve_credential',
  1355. ).replace_with(lambda value, config: value)
  1356. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  1357. flexmock(module).should_receive('make_defaults_file_options').with_args(
  1358. 'root',
  1359. 'trustsome1',
  1360. None,
  1361. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  1362. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  1363. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1364. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1365. ('mariadb', '--defaults-extra-file=/dev/fd/99', '--batch'),
  1366. processes=[extract_process],
  1367. output_log_level=logging.DEBUG,
  1368. input_file=extract_process.stdout,
  1369. environment={'USER': 'root'},
  1370. working_directory=None,
  1371. ).once()
  1372. module.restore_data_source_dump(
  1373. hook_config,
  1374. {},
  1375. data_source=hook_config[0],
  1376. dry_run=False,
  1377. extract_process=extract_process,
  1378. connection_params={
  1379. 'hostname': None,
  1380. 'port': None,
  1381. 'username': None,
  1382. 'password': None,
  1383. },
  1384. borgmatic_runtime_directory='/run/borgmatic',
  1385. )
  1386. def test_restore_data_source_with_environment_password_transport_skips_defaults_file_and_passes_user_flag():
  1387. hook_config = [
  1388. {
  1389. 'name': 'foo',
  1390. 'username': 'root',
  1391. 'password': 'trustsome1',
  1392. 'password_transport': 'environment',
  1393. },
  1394. ]
  1395. extract_process = flexmock(stdout=flexmock())
  1396. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1397. 'resolve_credential',
  1398. ).replace_with(lambda value, config: value)
  1399. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  1400. flexmock(module).should_receive('make_defaults_file_options').never()
  1401. flexmock(module.os).should_receive('environ').and_return(
  1402. {'USER': 'root', 'MYSQL_PWD': 'trustsome1'},
  1403. )
  1404. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1405. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1406. ('mariadb', '--batch', '--user', 'root'),
  1407. processes=[extract_process],
  1408. output_log_level=logging.DEBUG,
  1409. input_file=extract_process.stdout,
  1410. environment={'USER': 'root', 'MYSQL_PWD': 'trustsome1'},
  1411. working_directory=None,
  1412. ).once()
  1413. module.restore_data_source_dump(
  1414. hook_config,
  1415. {},
  1416. data_source=hook_config[0],
  1417. dry_run=False,
  1418. extract_process=extract_process,
  1419. connection_params={
  1420. 'hostname': None,
  1421. 'port': None,
  1422. 'username': None,
  1423. 'password': None,
  1424. },
  1425. borgmatic_runtime_directory='/run/borgmatic',
  1426. )
  1427. def test_restore_data_source_dump_with_connection_params_uses_connection_params_for_restore():
  1428. hook_config = [
  1429. {
  1430. 'name': 'foo',
  1431. 'username': 'root',
  1432. 'password': 'trustsome1',
  1433. 'restore_hostname': 'restorehost',
  1434. 'restore_port': 'restoreport',
  1435. 'restore_username': 'restoreusername',
  1436. 'restore_password': 'restorepassword',
  1437. },
  1438. ]
  1439. extract_process = flexmock(stdout=flexmock())
  1440. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1441. 'resolve_credential',
  1442. ).replace_with(lambda value, config: value)
  1443. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  1444. flexmock(module).should_receive('make_defaults_file_options').with_args(
  1445. 'cliusername',
  1446. 'clipassword',
  1447. None,
  1448. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  1449. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  1450. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1451. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1452. (
  1453. 'mariadb',
  1454. '--defaults-extra-file=/dev/fd/99',
  1455. '--batch',
  1456. '--host',
  1457. 'clihost',
  1458. '--port',
  1459. 'cliport',
  1460. '--protocol',
  1461. 'tcp',
  1462. ),
  1463. processes=[extract_process],
  1464. output_log_level=logging.DEBUG,
  1465. input_file=extract_process.stdout,
  1466. environment={'USER': 'root'},
  1467. working_directory=None,
  1468. ).once()
  1469. module.restore_data_source_dump(
  1470. hook_config,
  1471. {},
  1472. data_source=hook_config[0],
  1473. dry_run=False,
  1474. extract_process=extract_process,
  1475. connection_params={
  1476. 'hostname': 'clihost',
  1477. 'port': 'cliport',
  1478. 'username': 'cliusername',
  1479. 'password': 'clipassword',
  1480. },
  1481. borgmatic_runtime_directory='/run/borgmatic',
  1482. )
  1483. def test_restore_data_source_dump_without_connection_params_uses_restore_params_in_config_for_restore():
  1484. hook_config = [
  1485. {
  1486. 'name': 'foo',
  1487. 'username': 'root',
  1488. 'password': 'trustsome1',
  1489. 'hostname': 'dbhost',
  1490. 'port': 'dbport',
  1491. 'tls': True,
  1492. 'restore_username': 'restoreuser',
  1493. 'restore_password': 'restorepass',
  1494. 'restore_hostname': 'restorehost',
  1495. 'restore_port': 'restoreport',
  1496. 'restore_tls': False,
  1497. },
  1498. ]
  1499. extract_process = flexmock(stdout=flexmock())
  1500. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1501. 'resolve_credential',
  1502. ).replace_with(lambda value, config: value)
  1503. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  1504. flexmock(module).should_receive('make_defaults_file_options').with_args(
  1505. 'restoreuser',
  1506. 'restorepass',
  1507. None,
  1508. ).and_return(('--defaults-extra-file=/dev/fd/99',))
  1509. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  1510. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
  1511. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1512. (
  1513. 'mariadb',
  1514. '--defaults-extra-file=/dev/fd/99',
  1515. '--batch',
  1516. '--host',
  1517. 'restorehost',
  1518. '--port',
  1519. 'restoreport',
  1520. '--protocol',
  1521. 'tcp',
  1522. '--skip-ssl',
  1523. ),
  1524. processes=[extract_process],
  1525. output_log_level=logging.DEBUG,
  1526. input_file=extract_process.stdout,
  1527. environment={'USER': 'root'},
  1528. working_directory=None,
  1529. ).once()
  1530. module.restore_data_source_dump(
  1531. hook_config,
  1532. {},
  1533. data_source=hook_config[0],
  1534. dry_run=False,
  1535. extract_process=extract_process,
  1536. connection_params={
  1537. 'hostname': None,
  1538. 'port': None,
  1539. 'username': None,
  1540. 'password': None,
  1541. },
  1542. borgmatic_runtime_directory='/run/borgmatic',
  1543. )
  1544. def test_restore_data_source_dump_with_dry_run_skips_restore():
  1545. hook_config = [{'name': 'foo'}]
  1546. flexmock(module.borgmatic.hooks.credential.parse).should_receive(
  1547. 'resolve_credential',
  1548. ).replace_with(lambda value, config: value)
  1549. flexmock(module).should_receive('parse_extra_options').and_return((), None)
  1550. flexmock(module).should_receive('make_defaults_file_options').with_args(
  1551. None,
  1552. None,
  1553. None,
  1554. ).and_return(())
  1555. flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
  1556. flexmock(module).should_receive('execute_command_with_processes').never()
  1557. module.restore_data_source_dump(
  1558. hook_config,
  1559. {},
  1560. data_source={'name': 'foo'},
  1561. dry_run=True,
  1562. extract_process=flexmock(),
  1563. connection_params={
  1564. 'hostname': None,
  1565. 'port': None,
  1566. 'username': None,
  1567. 'password': None,
  1568. },
  1569. borgmatic_runtime_directory='/run/borgmatic',
  1570. )