test_mysql.py 57 KB

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