test_mariadb.py 55 KB

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