test_mariadb.py 57 KB

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