test_mysql.py 64 KB

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