test_mysql.py 52 KB

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