test_mysql.py 55 KB

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