test_borgmatic.py 94 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572
  1. import logging
  2. import subprocess
  3. import time
  4. import pytest
  5. from flexmock import flexmock
  6. import borgmatic.hooks.command
  7. from borgmatic.commands import borgmatic as module
  8. @pytest.mark.parametrize(
  9. 'config,arguments,expected_actions',
  10. (
  11. ({}, {}, []),
  12. ({'skip_actions': []}, {}, []),
  13. ({'skip_actions': ['prune', 'check']}, {}, ['prune', 'check']),
  14. (
  15. {'skip_actions': ['prune', 'check']},
  16. {'check': flexmock(force=False)},
  17. ['prune', 'check'],
  18. ),
  19. ({'skip_actions': ['prune', 'check']}, {'check': flexmock(force=True)}, ['prune']),
  20. ),
  21. )
  22. def test_get_skip_actions_uses_config_and_arguments(config, arguments, expected_actions):
  23. assert module.get_skip_actions(config, arguments) == expected_actions
  24. def test_monitoring_hooks_with_monioring_disabled_bails():
  25. flexmock(module).should_receive('get_verbosity').and_return(module.logging.INFO)
  26. flexmock(module).should_receive('verbosity_to_log_level').and_return(module.DISABLED)
  27. flexmock(module.dispatch).should_receive('call_hooks').never()
  28. with module.Monitoring_hooks(
  29. config_filename='test.yaml',
  30. config={},
  31. arguments={'create': flexmock()},
  32. global_arguments=flexmock(monitoring_verbosity=99, dry_run=False),
  33. ):
  34. pass
  35. def test_monitoring_hooks_with_non_primary_action_bails():
  36. flexmock(module).should_receive('get_verbosity').and_return(module.logging.INFO)
  37. flexmock(module).should_receive('verbosity_to_log_level').and_return(flexmock())
  38. flexmock(module.dispatch).should_receive('call_hooks').never()
  39. with module.Monitoring_hooks(
  40. config_filename='test.yaml',
  41. config={},
  42. arguments={'extract': flexmock()},
  43. global_arguments=flexmock(monitoring_verbosity=99, dry_run=False),
  44. ):
  45. pass
  46. def test_monitoring_hooks_pings_monitors():
  47. flexmock(module).should_receive('get_verbosity').and_return(module.logging.INFO)
  48. flexmock(module).should_receive('verbosity_to_log_level').and_return(flexmock())
  49. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  50. 'initialize_monitor',
  51. object,
  52. object,
  53. object,
  54. object,
  55. object,
  56. ).once()
  57. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  58. 'ping_monitor',
  59. object,
  60. object,
  61. object,
  62. module.monitor.State.START,
  63. object,
  64. object,
  65. ).once()
  66. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  67. 'ping_monitor',
  68. object,
  69. object,
  70. object,
  71. module.monitor.State.LOG,
  72. object,
  73. object,
  74. ).once()
  75. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  76. 'ping_monitor',
  77. object,
  78. object,
  79. object,
  80. module.monitor.State.FINISH,
  81. object,
  82. object,
  83. ).once()
  84. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  85. 'ping_monitor',
  86. object,
  87. object,
  88. object,
  89. module.monitor.State.FAIL,
  90. object,
  91. object,
  92. ).never()
  93. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  94. 'destroy_monitor',
  95. object,
  96. object,
  97. object,
  98. object,
  99. ).once()
  100. with module.Monitoring_hooks(
  101. config_filename='test.yaml',
  102. config={},
  103. arguments={'create': flexmock()},
  104. global_arguments=flexmock(monitoring_verbosity=99, dry_run=False),
  105. ):
  106. pass
  107. def test_monitoring_hooks_with_start_ping_error_raises():
  108. flexmock(module).should_receive('get_verbosity').and_return(module.logging.INFO)
  109. flexmock(module).should_receive('verbosity_to_log_level').and_return(flexmock())
  110. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  111. 'initialize_monitor',
  112. object,
  113. object,
  114. object,
  115. object,
  116. object,
  117. ).once()
  118. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  119. 'ping_monitor',
  120. object,
  121. object,
  122. object,
  123. module.monitor.State.START,
  124. object,
  125. object,
  126. ).and_raise(OSError).once()
  127. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  128. 'ping_monitor',
  129. object,
  130. object,
  131. object,
  132. module.monitor.State.LOG,
  133. object,
  134. object,
  135. ).never()
  136. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  137. 'ping_monitor',
  138. object,
  139. object,
  140. object,
  141. module.monitor.State.FINISH,
  142. object,
  143. object,
  144. ).never()
  145. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  146. 'destroy_monitor',
  147. object,
  148. object,
  149. object,
  150. object,
  151. ).never()
  152. with (
  153. pytest.raises(ValueError),
  154. module.Monitoring_hooks(
  155. config_filename='test.yaml',
  156. config={},
  157. arguments={'create': flexmock()},
  158. global_arguments=flexmock(monitoring_verbosity=99, dry_run=False),
  159. ),
  160. ):
  161. raise AssertionError() # This should never get called.
  162. def test_monitoring_hooks_with_log_ping_error_raises():
  163. flexmock(module).should_receive('get_verbosity').and_return(module.logging.INFO)
  164. flexmock(module).should_receive('verbosity_to_log_level').and_return(flexmock())
  165. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  166. 'initialize_monitor',
  167. object,
  168. object,
  169. object,
  170. object,
  171. object,
  172. ).once()
  173. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  174. 'ping_monitor',
  175. object,
  176. object,
  177. object,
  178. module.monitor.State.START,
  179. object,
  180. object,
  181. ).once()
  182. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  183. 'ping_monitor',
  184. object,
  185. object,
  186. object,
  187. module.monitor.State.LOG,
  188. object,
  189. object,
  190. ).and_raise(OSError).once()
  191. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  192. 'ping_monitor',
  193. object,
  194. object,
  195. object,
  196. module.monitor.State.FINISH,
  197. object,
  198. object,
  199. ).never()
  200. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  201. 'destroy_monitor',
  202. object,
  203. object,
  204. object,
  205. object,
  206. ).never()
  207. with (
  208. pytest.raises(ValueError),
  209. module.Monitoring_hooks(
  210. config_filename='test.yaml',
  211. config={},
  212. arguments={'create': flexmock()},
  213. global_arguments=flexmock(monitoring_verbosity=99, dry_run=False),
  214. ),
  215. ):
  216. pass
  217. def test_monitoring_hooks_with_finish_ping_error_raises():
  218. flexmock(module).should_receive('get_verbosity').and_return(module.logging.INFO)
  219. flexmock(module).should_receive('verbosity_to_log_level').and_return(flexmock())
  220. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  221. 'initialize_monitor',
  222. object,
  223. object,
  224. object,
  225. object,
  226. object,
  227. ).once()
  228. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  229. 'ping_monitor',
  230. object,
  231. object,
  232. object,
  233. module.monitor.State.START,
  234. object,
  235. object,
  236. ).once()
  237. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  238. 'ping_monitor',
  239. object,
  240. object,
  241. object,
  242. module.monitor.State.LOG,
  243. object,
  244. object,
  245. ).once()
  246. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  247. 'ping_monitor',
  248. object,
  249. object,
  250. object,
  251. module.monitor.State.FINISH,
  252. object,
  253. object,
  254. ).and_raise(OSError).once()
  255. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  256. 'destroy_monitor',
  257. object,
  258. object,
  259. object,
  260. object,
  261. ).never()
  262. with (
  263. pytest.raises(ValueError),
  264. module.Monitoring_hooks(
  265. config_filename='test.yaml',
  266. config={},
  267. arguments={'create': flexmock()},
  268. global_arguments=flexmock(monitoring_verbosity=99, dry_run=False),
  269. ),
  270. ):
  271. pass
  272. def test_monitoring_hooks_with_wrapped_code_error_pings_fail():
  273. flexmock(module).should_receive('get_verbosity').and_return(module.logging.INFO)
  274. flexmock(module).should_receive('verbosity_to_log_level').and_return(flexmock())
  275. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  276. 'initialize_monitor',
  277. object,
  278. object,
  279. object,
  280. object,
  281. object,
  282. ).once()
  283. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  284. 'ping_monitor',
  285. object,
  286. object,
  287. object,
  288. module.monitor.State.START,
  289. object,
  290. object,
  291. ).once()
  292. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  293. 'ping_monitor',
  294. object,
  295. object,
  296. object,
  297. module.monitor.State.LOG,
  298. object,
  299. object,
  300. ).once()
  301. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  302. 'ping_monitor',
  303. object,
  304. object,
  305. object,
  306. module.monitor.State.FINISH,
  307. object,
  308. object,
  309. ).never()
  310. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  311. 'ping_monitor',
  312. object,
  313. object,
  314. object,
  315. module.monitor.State.FAIL,
  316. object,
  317. object,
  318. ).once()
  319. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  320. 'destroy_monitor',
  321. object,
  322. object,
  323. object,
  324. object,
  325. ).once()
  326. with (
  327. pytest.raises(OSError),
  328. module.Monitoring_hooks(
  329. config_filename='test.yaml',
  330. config={},
  331. arguments={'create': flexmock()},
  332. global_arguments=flexmock(monitoring_verbosity=99, dry_run=False),
  333. ),
  334. ):
  335. raise OSError()
  336. def test_monitoring_hooks_with_fail_ping_error_raise_original_error():
  337. flexmock(module).should_receive('get_verbosity').and_return(module.logging.INFO)
  338. flexmock(module).should_receive('verbosity_to_log_level').and_return(flexmock())
  339. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  340. 'initialize_monitor',
  341. object,
  342. object,
  343. object,
  344. object,
  345. object,
  346. ).once()
  347. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  348. 'ping_monitor',
  349. object,
  350. object,
  351. object,
  352. module.monitor.State.START,
  353. object,
  354. object,
  355. ).once()
  356. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  357. 'ping_monitor',
  358. object,
  359. object,
  360. object,
  361. module.monitor.State.LOG,
  362. object,
  363. object,
  364. ).once()
  365. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  366. 'ping_monitor',
  367. object,
  368. object,
  369. object,
  370. module.monitor.State.FINISH,
  371. object,
  372. object,
  373. ).never()
  374. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  375. 'ping_monitor',
  376. object,
  377. object,
  378. object,
  379. module.monitor.State.FAIL,
  380. object,
  381. object,
  382. ).and_raise(OSError).once()
  383. flexmock(module.dispatch).should_receive('call_hooks').with_args(
  384. 'destroy_monitor',
  385. object,
  386. object,
  387. object,
  388. object,
  389. ).never()
  390. with (
  391. pytest.raises(OSError),
  392. module.Monitoring_hooks(
  393. config_filename='test.yaml',
  394. config={},
  395. arguments={'create': flexmock()},
  396. global_arguments=flexmock(monitoring_verbosity=99, dry_run=False),
  397. ),
  398. ):
  399. raise OSError()
  400. def test_run_configuration_runs_actions_for_each_repository():
  401. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  402. flexmock(module).should_receive('get_skip_actions').and_return([])
  403. flexmock(module).should_receive('Monitoring_hooks').and_return(flexmock())
  404. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  405. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  406. expected_results = [flexmock(), flexmock()]
  407. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  408. flexmock(module).should_receive('run_actions').and_return(expected_results[:1]).and_return(
  409. expected_results[1:],
  410. )
  411. config = {'repositories': [{'path': 'foo'}, {'path': 'bar'}]}
  412. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False)}
  413. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  414. assert results == expected_results
  415. def test_run_configuration_with_skip_actions_does_not_raise():
  416. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  417. flexmock(module).should_receive('get_skip_actions').and_return(['compact'])
  418. flexmock(module).should_receive('Monitoring_hooks').and_return(flexmock())
  419. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  420. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  421. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  422. flexmock(module).should_receive('run_actions').and_return(flexmock()).and_return(flexmock())
  423. config = {'repositories': [{'path': 'foo'}, {'path': 'bar'}], 'skip_actions': ['compact']}
  424. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False)}
  425. list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  426. def test_run_configuration_with_invalid_borg_version_errors():
  427. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  428. flexmock(module).should_receive('get_skip_actions').and_return([])
  429. flexmock(module).should_receive('Monitoring_hooks').and_return(flexmock())
  430. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  431. flexmock(module.borg_version).should_receive('local_borg_version').and_raise(ValueError)
  432. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  433. flexmock(module).should_receive('run_actions').never()
  434. config = {'repositories': [{'path': 'foo'}]}
  435. arguments = {
  436. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  437. 'prune': flexmock(),
  438. }
  439. list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  440. def test_run_configuration_logs_actions_error():
  441. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  442. flexmock(module).should_receive('get_skip_actions').and_return([])
  443. flexmock(module).should_receive('Monitoring_hooks').and_return(flexmock())
  444. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  445. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  446. expected_results = [flexmock(), flexmock()]
  447. flexmock(module).should_receive('log_error_records').and_return(
  448. expected_results[:1],
  449. ).and_return(expected_results[1:])
  450. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  451. flexmock(module).should_receive('run_actions').and_raise(OSError)
  452. flexmock(module.command).should_receive('filter_hooks')
  453. flexmock(module.command).should_receive('execute_hooks')
  454. config = {'repositories': [{'path': 'foo'}]}
  455. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False)}
  456. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  457. assert results == expected_results
  458. def test_run_configuration_skips_remaining_actions_for_actions_soft_failure_but_still_runs_next_repository_actions():
  459. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  460. flexmock(module).should_receive('get_skip_actions').and_return([])
  461. flexmock(module).should_receive('Monitoring_hooks').and_return(flexmock())
  462. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  463. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  464. error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
  465. log = flexmock()
  466. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  467. flexmock(module).should_receive('run_actions').twice().and_raise(error).and_yield(log)
  468. flexmock(module).should_receive('log_error_records').never()
  469. flexmock(module.command).should_receive('considered_soft_failure').and_return(True)
  470. config = {'repositories': [{'path': 'foo'}, {'path': 'bar'}]}
  471. arguments = {
  472. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  473. 'create': flexmock(),
  474. }
  475. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  476. assert results == [log]
  477. def test_run_configuration_does_not_call_monitoring_hooks_if_monitoring_hooks_are_disabled():
  478. flexmock(module).should_receive('verbosity_to_log_level').and_return(module.DISABLED)
  479. flexmock(module).should_receive('get_skip_actions').and_return([])
  480. flexmock(module).should_receive('Monitoring_hooks').and_return(flexmock())
  481. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  482. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  483. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  484. flexmock(module).should_receive('run_actions').and_return([])
  485. config = {'repositories': [{'path': 'foo'}]}
  486. arguments = {
  487. 'global': flexmock(monitoring_verbosity=-2, dry_run=False),
  488. 'create': flexmock(),
  489. }
  490. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  491. assert results == []
  492. def test_run_configuration_logs_on_error_hook_error():
  493. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  494. flexmock(module).should_receive('get_skip_actions').and_return([])
  495. flexmock(module).should_receive('Monitoring_hooks').and_return(flexmock())
  496. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  497. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  498. flexmock(module.command).should_receive('filter_hooks')
  499. flexmock(module.command).should_receive('execute_hooks').and_raise(OSError)
  500. expected_results = [flexmock(), flexmock()]
  501. flexmock(module).should_receive('log_error_records').and_return(
  502. expected_results[:1],
  503. ).and_return(expected_results[1:2]).and_return(expected_results[2:])
  504. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  505. flexmock(module).should_receive('run_actions').and_raise(OSError)
  506. config = {'repositories': [{'path': 'foo'}]}
  507. arguments = {
  508. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  509. 'create': flexmock(),
  510. }
  511. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  512. assert results == expected_results
  513. def test_run_configuration_logs_on_before_command_hook_error():
  514. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  515. flexmock(module).should_receive('get_skip_actions').and_return([])
  516. flexmock(module).should_receive('Monitoring_hooks').and_return(flexmock())
  517. flexmock(module.command).should_receive('Before_after_hooks').and_raise(OSError)
  518. flexmock(module.borg_version).should_receive('local_borg_version').never()
  519. flexmock(module.command).should_receive('filter_hooks')
  520. flexmock(module.command).should_receive('execute_hooks')
  521. expected_results = [flexmock()]
  522. flexmock(module).should_receive('log_error_records').and_return(expected_results)
  523. flexmock(module).should_receive('Log_prefix').never()
  524. flexmock(module).should_receive('run_actions').never()
  525. config = {'repositories': [{'path': 'foo'}]}
  526. arguments = {
  527. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  528. 'create': flexmock(),
  529. }
  530. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  531. assert results == expected_results
  532. def test_run_configuration_logs_on_monitoring_hook_error():
  533. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  534. flexmock(module).should_receive('get_skip_actions').and_return([])
  535. flexmock(module).should_receive('Monitoring_hooks').and_raise(OSError)
  536. flexmock(module.command).should_receive('Before_after_hooks').never()
  537. flexmock(module.borg_version).should_receive('local_borg_version').never()
  538. flexmock(module.command).should_receive('filter_hooks')
  539. flexmock(module.command).should_receive('execute_hooks')
  540. expected_results = [flexmock()]
  541. flexmock(module).should_receive('log_error_records').and_return(expected_results)
  542. flexmock(module).should_receive('Log_prefix').never()
  543. flexmock(module).should_receive('run_actions').never()
  544. config = {'repositories': [{'path': 'foo'}]}
  545. arguments = {
  546. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  547. 'create': flexmock(),
  548. }
  549. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  550. assert results == expected_results
  551. def test_run_configuration_bails_for_on_error_hook_soft_failure():
  552. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  553. flexmock(module).should_receive('get_skip_actions').and_return([])
  554. flexmock(module).should_receive('Monitoring_hooks').and_return(flexmock())
  555. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  556. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  557. error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
  558. flexmock(module.command).should_receive('filter_hooks')
  559. flexmock(module.command).should_receive('execute_hooks').and_raise(error)
  560. expected_results = [flexmock(), flexmock()]
  561. flexmock(module).should_receive('log_error_records').and_return(
  562. expected_results[:1],
  563. ).and_return(expected_results[1:])
  564. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  565. flexmock(module).should_receive('run_actions').and_raise(OSError)
  566. config = {'repositories': [{'path': 'foo'}]}
  567. arguments = {
  568. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  569. 'create': flexmock(),
  570. }
  571. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  572. assert results == expected_results
  573. def test_run_configuration_retries_soft_error():
  574. # Run action first fails, second passes.
  575. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  576. flexmock(module).should_receive('get_skip_actions').and_return([])
  577. flexmock(module).should_receive('Monitoring_hooks').and_return(flexmock())
  578. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  579. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  580. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  581. flexmock(module).should_receive('run_actions').and_raise(OSError).and_return([])
  582. flexmock(module).should_receive('log_error_records').and_return([flexmock()]).once()
  583. flexmock(module.command).should_receive('filter_hooks').never()
  584. flexmock(module.command).should_receive('execute_hooks').never()
  585. config = {'repositories': [{'path': 'foo'}], 'retries': 1}
  586. arguments = {
  587. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  588. 'create': flexmock(),
  589. }
  590. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  591. assert results == []
  592. def test_run_configuration_retries_hard_error():
  593. # Run action fails twice.
  594. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  595. flexmock(module).should_receive('get_skip_actions').and_return([])
  596. flexmock(module).should_receive('Monitoring_hooks').and_return(flexmock())
  597. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  598. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  599. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  600. flexmock(module).should_receive('run_actions').and_raise(OSError).times(2)
  601. flexmock(module).should_receive('log_error_records').with_args(
  602. 'Error running actions for repository',
  603. OSError,
  604. levelno=logging.WARNING,
  605. log_command_error_output=True,
  606. ).and_return([flexmock()])
  607. error_logs = [flexmock(), flexmock()]
  608. flexmock(module).should_receive('log_error_records').with_args(
  609. 'Error running actions for repository',
  610. OSError,
  611. ).and_return(error_logs[:1]).ordered()
  612. flexmock(module).should_receive('log_error_records').with_args(
  613. 'Error running configuration',
  614. ).and_return(error_logs[1:]).ordered()
  615. flexmock(module.command).should_receive('filter_hooks')
  616. flexmock(module.command).should_receive('execute_hooks')
  617. config = {'repositories': [{'path': 'foo'}], 'retries': 1}
  618. arguments = {
  619. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  620. 'create': flexmock(),
  621. }
  622. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  623. assert results == error_logs
  624. def test_run_configuration_retries_repositories_in_order():
  625. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  626. flexmock(module).should_receive('get_skip_actions').and_return([])
  627. flexmock(module).should_receive('Monitoring_hooks').and_return(flexmock())
  628. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  629. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  630. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  631. flexmock(module).should_receive('run_actions').and_raise(OSError).times(2)
  632. expected_results = [flexmock(), flexmock(), flexmock()]
  633. flexmock(module).should_receive('log_error_records').with_args(
  634. 'Error running actions for repository',
  635. OSError,
  636. ).and_return(expected_results[:1]).ordered()
  637. flexmock(module).should_receive('log_error_records').with_args(
  638. 'Error running actions for repository',
  639. OSError,
  640. ).and_return(expected_results[1:2]).ordered()
  641. flexmock(module).should_receive('log_error_records').with_args(
  642. 'Error running configuration',
  643. ).and_return(expected_results[2:]).ordered()
  644. flexmock(module.command).should_receive('filter_hooks')
  645. flexmock(module.command).should_receive('execute_hooks')
  646. config = {'repositories': [{'path': 'foo'}, {'path': 'bar'}]}
  647. arguments = {
  648. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  649. 'create': flexmock(),
  650. }
  651. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  652. assert results == expected_results
  653. def test_run_configuration_retries_round_robin():
  654. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  655. flexmock(module).should_receive('get_skip_actions').and_return([])
  656. flexmock(module).should_receive('Monitoring_hooks').and_return(flexmock())
  657. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  658. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  659. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  660. flexmock(module).should_receive('run_actions').and_raise(OSError).times(4)
  661. flexmock(module).should_receive('log_error_records').with_args(
  662. 'Error running actions for repository',
  663. OSError,
  664. levelno=logging.WARNING,
  665. log_command_error_output=True,
  666. ).and_return([flexmock()]).ordered()
  667. flexmock(module).should_receive('log_error_records').with_args(
  668. 'Error running actions for repository',
  669. OSError,
  670. levelno=logging.WARNING,
  671. log_command_error_output=True,
  672. ).and_return([flexmock()]).ordered()
  673. foo_error_logs = [flexmock()]
  674. flexmock(module).should_receive('log_error_records').with_args(
  675. 'Error running actions for repository',
  676. OSError,
  677. ).and_return(foo_error_logs).ordered()
  678. bar_error_logs = [flexmock()]
  679. flexmock(module).should_receive('log_error_records').with_args(
  680. 'Error running actions for repository',
  681. OSError,
  682. ).and_return(bar_error_logs).ordered()
  683. config_error_logs = [flexmock()]
  684. flexmock(module).should_receive('log_error_records').with_args(
  685. 'Error running configuration',
  686. ).and_return(config_error_logs).ordered()
  687. flexmock(module.command).should_receive('filter_hooks')
  688. flexmock(module.command).should_receive('execute_hooks')
  689. config = {
  690. 'repositories': [{'path': 'foo'}, {'path': 'bar'}],
  691. 'retries': 1,
  692. }
  693. arguments = {
  694. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  695. 'create': flexmock(),
  696. }
  697. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  698. assert results == foo_error_logs + bar_error_logs + config_error_logs
  699. def test_run_configuration_with_one_retry():
  700. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  701. flexmock(module).should_receive('get_skip_actions').and_return([])
  702. flexmock(module).should_receive('Monitoring_hooks').and_return(flexmock())
  703. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  704. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  705. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  706. flexmock(module).should_receive('run_actions').and_raise(OSError).and_raise(OSError).and_return(
  707. [],
  708. ).and_raise(OSError).times(4)
  709. flexmock(module).should_receive('log_error_records').with_args(
  710. 'Error running actions for repository',
  711. OSError,
  712. levelno=logging.WARNING,
  713. log_command_error_output=True,
  714. ).and_return([flexmock()]).ordered()
  715. flexmock(module).should_receive('log_error_records').with_args(
  716. 'Error running actions for repository',
  717. OSError,
  718. levelno=logging.WARNING,
  719. log_command_error_output=True,
  720. ).and_return(flexmock()).ordered()
  721. error_logs = [flexmock(), flexmock()]
  722. flexmock(module).should_receive('log_error_records').with_args(
  723. 'Error running actions for repository',
  724. OSError,
  725. ).and_return(error_logs[:1]).ordered()
  726. flexmock(module).should_receive('log_error_records').with_args(
  727. 'Error running configuration',
  728. ).and_return(error_logs[1:]).ordered()
  729. flexmock(module.command).should_receive('filter_hooks')
  730. flexmock(module.command).should_receive('execute_hooks')
  731. config = {
  732. 'repositories': [{'path': 'foo'}, {'path': 'bar'}],
  733. 'retries': 1,
  734. }
  735. arguments = {
  736. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  737. 'create': flexmock(),
  738. }
  739. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  740. assert results == error_logs
  741. def test_run_configuration_with_retry_wait_does_backoff_after_each_retry():
  742. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  743. flexmock(module).should_receive('get_skip_actions').and_return([])
  744. flexmock(module).should_receive('Monitoring_hooks').and_return(flexmock())
  745. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  746. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  747. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  748. flexmock(module).should_receive('run_actions').and_raise(OSError).times(4)
  749. flexmock(module).should_receive('log_error_records').with_args(
  750. 'Error running actions for repository',
  751. OSError,
  752. levelno=logging.WARNING,
  753. log_command_error_output=True,
  754. ).and_return([flexmock()]).ordered()
  755. flexmock(time).should_receive('sleep').with_args(10).and_return().ordered()
  756. flexmock(module).should_receive('log_error_records').with_args(
  757. 'Error running actions for repository',
  758. OSError,
  759. levelno=logging.WARNING,
  760. log_command_error_output=True,
  761. ).and_return([flexmock()]).ordered()
  762. flexmock(time).should_receive('sleep').with_args(20).and_return().ordered()
  763. flexmock(module).should_receive('log_error_records').with_args(
  764. 'Error running actions for repository',
  765. OSError,
  766. levelno=logging.WARNING,
  767. log_command_error_output=True,
  768. ).and_return([flexmock()]).ordered()
  769. flexmock(time).should_receive('sleep').with_args(30).and_return().ordered()
  770. error_logs = [flexmock(), flexmock()]
  771. flexmock(module).should_receive('log_error_records').with_args(
  772. 'Error running actions for repository',
  773. OSError,
  774. ).and_return(error_logs[:1]).ordered()
  775. flexmock(module).should_receive('log_error_records').with_args(
  776. 'Error running configuration',
  777. ).and_return(error_logs[1:]).ordered()
  778. flexmock(module.command).should_receive('filter_hooks')
  779. flexmock(module.command).should_receive('execute_hooks')
  780. config = {
  781. 'repositories': [{'path': 'foo'}],
  782. 'retries': 3,
  783. 'retry_wait': 10,
  784. }
  785. arguments = {
  786. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  787. 'create': flexmock(),
  788. }
  789. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  790. assert results == error_logs
  791. def test_run_configuration_with_multiple_repositories_retries_with_timeout():
  792. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  793. flexmock(module).should_receive('get_skip_actions').and_return([])
  794. flexmock(module).should_receive('Monitoring_hooks').and_return(flexmock())
  795. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  796. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  797. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  798. flexmock(module).should_receive('run_actions').and_raise(OSError).and_raise(OSError).and_return(
  799. [],
  800. ).and_raise(OSError).times(4)
  801. flexmock(module).should_receive('log_error_records').with_args(
  802. 'Error running actions for repository',
  803. OSError,
  804. levelno=logging.WARNING,
  805. log_command_error_output=True,
  806. ).and_return([flexmock()]).ordered()
  807. flexmock(module).should_receive('log_error_records').with_args(
  808. 'Error running actions for repository',
  809. OSError,
  810. levelno=logging.WARNING,
  811. log_command_error_output=True,
  812. ).and_return([flexmock()]).ordered()
  813. # Sleep before retrying foo (and passing)
  814. flexmock(time).should_receive('sleep').with_args(10).and_return().ordered()
  815. # Sleep before retrying bar (and failing)
  816. flexmock(time).should_receive('sleep').with_args(10).and_return().ordered()
  817. error_logs = [flexmock(), flexmock()]
  818. flexmock(module).should_receive('log_error_records').with_args(
  819. 'Error running actions for repository',
  820. OSError,
  821. ).and_return(error_logs[:1]).ordered()
  822. flexmock(module).should_receive('log_error_records').with_args(
  823. 'Error running configuration',
  824. ).and_return(error_logs[1:]).ordered()
  825. flexmock(module.command).should_receive('filter_hooks')
  826. flexmock(module.command).should_receive('execute_hooks')
  827. config = {
  828. 'repositories': [{'path': 'foo'}, {'path': 'bar'}],
  829. 'retries': 1,
  830. 'retry_wait': 10,
  831. }
  832. arguments = {
  833. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  834. 'create': flexmock(),
  835. }
  836. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  837. assert results == error_logs
  838. def test_run_actions_runs_repo_create():
  839. flexmock(module).should_receive('add_custom_log_levels')
  840. flexmock(module).should_receive('get_skip_actions').and_return([])
  841. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  842. flexmock(),
  843. )
  844. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  845. flexmock(borgmatic.actions.repo_create).should_receive('run_repo_create').once()
  846. tuple(
  847. module.run_actions(
  848. arguments={
  849. 'global': flexmock(dry_run=False),
  850. 'repo-create': flexmock(),
  851. },
  852. config_filename=flexmock(),
  853. config={'repositories': []},
  854. config_paths=[],
  855. local_path=flexmock(),
  856. remote_path=flexmock(),
  857. local_borg_version=flexmock(),
  858. repository={'path': 'repo'},
  859. ),
  860. )
  861. def test_run_actions_adds_label_file_to_hook_context():
  862. flexmock(module).should_receive('add_custom_log_levels')
  863. flexmock(module).should_receive('get_skip_actions').and_return([])
  864. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  865. flexmock(),
  866. )
  867. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  868. expected = flexmock()
  869. flexmock(borgmatic.actions.create).should_receive('run_create').with_args(
  870. config_filename=object,
  871. repository={'path': 'repo', 'label': 'my repo'},
  872. config={'repositories': []},
  873. config_paths=[],
  874. local_borg_version=object,
  875. create_arguments=object,
  876. global_arguments=object,
  877. dry_run_label='',
  878. local_path=object,
  879. remote_path=object,
  880. ).once().and_return(expected)
  881. result = tuple(
  882. module.run_actions(
  883. arguments={'global': flexmock(dry_run=False), 'create': flexmock()},
  884. config_filename=flexmock(),
  885. config={'repositories': []},
  886. config_paths=[],
  887. local_path=flexmock(),
  888. remote_path=flexmock(),
  889. local_borg_version=flexmock(),
  890. repository={'path': 'repo', 'label': 'my repo'},
  891. ),
  892. )
  893. assert result == (expected,)
  894. def test_run_actions_adds_log_file_to_hook_context():
  895. flexmock(module).should_receive('add_custom_log_levels')
  896. flexmock(module).should_receive('get_skip_actions').and_return([])
  897. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  898. flexmock(),
  899. )
  900. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  901. expected = flexmock()
  902. flexmock(borgmatic.actions.create).should_receive('run_create').with_args(
  903. config_filename=object,
  904. repository={'path': 'repo'},
  905. config={'repositories': [], 'log_file': 'foo'},
  906. config_paths=[],
  907. local_borg_version=object,
  908. create_arguments=object,
  909. global_arguments=object,
  910. dry_run_label='',
  911. local_path=object,
  912. remote_path=object,
  913. ).once().and_return(expected)
  914. result = tuple(
  915. module.run_actions(
  916. arguments={'global': flexmock(dry_run=False), 'create': flexmock()},
  917. config_filename=flexmock(),
  918. config={'repositories': [], 'log_file': 'foo'},
  919. config_paths=[],
  920. local_path=flexmock(),
  921. remote_path=flexmock(),
  922. local_borg_version=flexmock(),
  923. repository={'path': 'repo'},
  924. ),
  925. )
  926. assert result == (expected,)
  927. def test_run_actions_runs_transfer():
  928. flexmock(module).should_receive('add_custom_log_levels')
  929. flexmock(module).should_receive('get_skip_actions').and_return([])
  930. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  931. flexmock(),
  932. )
  933. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  934. flexmock(borgmatic.actions.transfer).should_receive('run_transfer').once()
  935. tuple(
  936. module.run_actions(
  937. arguments={'global': flexmock(dry_run=False), 'transfer': flexmock()},
  938. config_filename=flexmock(),
  939. config={'repositories': []},
  940. config_paths=[],
  941. local_path=flexmock(),
  942. remote_path=flexmock(),
  943. local_borg_version=flexmock(),
  944. repository={'path': 'repo'},
  945. ),
  946. )
  947. def test_run_actions_runs_create():
  948. flexmock(module).should_receive('add_custom_log_levels')
  949. flexmock(module).should_receive('get_skip_actions').and_return([])
  950. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  951. flexmock(),
  952. )
  953. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  954. expected = flexmock()
  955. flexmock(borgmatic.actions.create).should_receive('run_create').and_yield(expected).once()
  956. result = tuple(
  957. module.run_actions(
  958. arguments={'global': flexmock(dry_run=False), 'create': flexmock()},
  959. config_filename=flexmock(),
  960. config={'repositories': []},
  961. config_paths=[],
  962. local_path=flexmock(),
  963. remote_path=flexmock(),
  964. local_borg_version=flexmock(),
  965. repository={'path': 'repo'},
  966. ),
  967. )
  968. assert result == (expected,)
  969. def test_run_actions_with_skip_actions_does_not_run_action_or_action_command_hooks():
  970. flexmock(module).should_receive('add_custom_log_levels')
  971. flexmock(module).should_receive('get_skip_actions').and_return(['create'])
  972. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  973. flexmock(),
  974. )
  975. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  976. flexmock(module.command).should_receive('Before_after_hooks').with_args(
  977. command_hooks=object,
  978. before_after='action',
  979. umask=object,
  980. working_directory=object,
  981. dry_run=object,
  982. action_names=object,
  983. configuration_filename=object,
  984. repository_label=object,
  985. log_file=object,
  986. repositories=object,
  987. repository=object,
  988. ).never()
  989. flexmock(borgmatic.actions.create).should_receive('run_create').never()
  990. tuple(
  991. module.run_actions(
  992. arguments={'global': flexmock(dry_run=False), 'create': flexmock()},
  993. config_filename=flexmock(),
  994. config={'repositories': [], 'skip_actions': ['create']},
  995. config_paths=[],
  996. local_path=flexmock(),
  997. remote_path=flexmock(),
  998. local_borg_version=flexmock(),
  999. repository={'path': 'repo'},
  1000. ),
  1001. )
  1002. def test_run_actions_runs_recreate():
  1003. flexmock(module).should_receive('add_custom_log_levels')
  1004. flexmock(module).should_receive('get_skip_actions').and_return([])
  1005. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1006. flexmock(),
  1007. )
  1008. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1009. flexmock(borgmatic.actions.recreate).should_receive('run_recreate').once()
  1010. tuple(
  1011. module.run_actions(
  1012. arguments={'global': flexmock(dry_run=False), 'recreate': flexmock()},
  1013. config_filename=flexmock(),
  1014. config={'repositories': []},
  1015. config_paths=[],
  1016. local_path=flexmock(),
  1017. remote_path=flexmock(),
  1018. local_borg_version=flexmock(),
  1019. repository={'path': 'repo'},
  1020. ),
  1021. )
  1022. def test_run_actions_runs_prune():
  1023. flexmock(module).should_receive('add_custom_log_levels')
  1024. flexmock(module).should_receive('get_skip_actions').and_return([])
  1025. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1026. flexmock(),
  1027. )
  1028. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1029. flexmock(borgmatic.actions.prune).should_receive('run_prune').once()
  1030. tuple(
  1031. module.run_actions(
  1032. arguments={'global': flexmock(dry_run=False), 'prune': flexmock()},
  1033. config_filename=flexmock(),
  1034. config={'repositories': []},
  1035. config_paths=[],
  1036. local_path=flexmock(),
  1037. remote_path=flexmock(),
  1038. local_borg_version=flexmock(),
  1039. repository={'path': 'repo'},
  1040. ),
  1041. )
  1042. def test_run_actions_runs_compact():
  1043. flexmock(module).should_receive('add_custom_log_levels')
  1044. flexmock(module).should_receive('get_skip_actions').and_return([])
  1045. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1046. flexmock(),
  1047. )
  1048. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1049. flexmock(borgmatic.actions.compact).should_receive('run_compact').once()
  1050. tuple(
  1051. module.run_actions(
  1052. arguments={'global': flexmock(dry_run=False), 'compact': flexmock()},
  1053. config_filename=flexmock(),
  1054. config={'repositories': []},
  1055. config_paths=[],
  1056. local_path=flexmock(),
  1057. remote_path=flexmock(),
  1058. local_borg_version=flexmock(),
  1059. repository={'path': 'repo'},
  1060. ),
  1061. )
  1062. def test_run_actions_runs_check_when_repository_enabled_for_checks():
  1063. flexmock(module).should_receive('add_custom_log_levels')
  1064. flexmock(module).should_receive('get_skip_actions').and_return([])
  1065. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1066. flexmock(),
  1067. )
  1068. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1069. flexmock(module.checks).should_receive('repository_enabled_for_checks').and_return(True)
  1070. flexmock(borgmatic.actions.check).should_receive('run_check').once()
  1071. tuple(
  1072. module.run_actions(
  1073. arguments={'global': flexmock(dry_run=False), 'check': flexmock()},
  1074. config_filename=flexmock(),
  1075. config={'repositories': []},
  1076. config_paths=[],
  1077. local_path=flexmock(),
  1078. remote_path=flexmock(),
  1079. local_borg_version=flexmock(),
  1080. repository={'path': 'repo'},
  1081. ),
  1082. )
  1083. def test_run_actions_skips_check_when_repository_not_enabled_for_checks():
  1084. flexmock(module).should_receive('add_custom_log_levels')
  1085. flexmock(module).should_receive('get_skip_actions').and_return([])
  1086. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1087. flexmock(),
  1088. )
  1089. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1090. flexmock(module.checks).should_receive('repository_enabled_for_checks').and_return(False)
  1091. flexmock(borgmatic.actions.check).should_receive('run_check').never()
  1092. tuple(
  1093. module.run_actions(
  1094. arguments={'global': flexmock(dry_run=False), 'check': flexmock()},
  1095. config_filename=flexmock(),
  1096. config={'repositories': []},
  1097. config_paths=[],
  1098. local_path=flexmock(),
  1099. remote_path=flexmock(),
  1100. local_borg_version=flexmock(),
  1101. repository={'path': 'repo'},
  1102. ),
  1103. )
  1104. def test_run_actions_runs_extract():
  1105. flexmock(module).should_receive('add_custom_log_levels')
  1106. flexmock(module).should_receive('get_skip_actions').and_return([])
  1107. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1108. flexmock(),
  1109. )
  1110. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1111. flexmock(borgmatic.actions.extract).should_receive('run_extract').once()
  1112. tuple(
  1113. module.run_actions(
  1114. arguments={'global': flexmock(dry_run=False), 'extract': flexmock()},
  1115. config_filename=flexmock(),
  1116. config={'repositories': []},
  1117. config_paths=[],
  1118. local_path=flexmock(),
  1119. remote_path=flexmock(),
  1120. local_borg_version=flexmock(),
  1121. repository={'path': 'repo'},
  1122. ),
  1123. )
  1124. def test_run_actions_runs_export_tar():
  1125. flexmock(module).should_receive('add_custom_log_levels')
  1126. flexmock(module).should_receive('get_skip_actions').and_return([])
  1127. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1128. flexmock(),
  1129. )
  1130. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1131. flexmock(borgmatic.actions.export_tar).should_receive('run_export_tar').once()
  1132. tuple(
  1133. module.run_actions(
  1134. arguments={'global': flexmock(dry_run=False), 'export-tar': flexmock()},
  1135. config_filename=flexmock(),
  1136. config={'repositories': []},
  1137. config_paths=[],
  1138. local_path=flexmock(),
  1139. remote_path=flexmock(),
  1140. local_borg_version=flexmock(),
  1141. repository={'path': 'repo'},
  1142. ),
  1143. )
  1144. def test_run_actions_runs_mount():
  1145. flexmock(module).should_receive('add_custom_log_levels')
  1146. flexmock(module).should_receive('get_skip_actions').and_return([])
  1147. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1148. flexmock(),
  1149. )
  1150. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1151. flexmock(borgmatic.actions.mount).should_receive('run_mount').once()
  1152. tuple(
  1153. module.run_actions(
  1154. arguments={'global': flexmock(dry_run=False), 'mount': flexmock()},
  1155. config_filename=flexmock(),
  1156. config={'repositories': []},
  1157. config_paths=[],
  1158. local_path=flexmock(),
  1159. remote_path=flexmock(),
  1160. local_borg_version=flexmock(),
  1161. repository={'path': 'repo'},
  1162. ),
  1163. )
  1164. def test_run_actions_runs_restore():
  1165. flexmock(module).should_receive('add_custom_log_levels')
  1166. flexmock(module).should_receive('get_skip_actions').and_return([])
  1167. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1168. flexmock(),
  1169. )
  1170. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1171. flexmock(borgmatic.actions.restore).should_receive('run_restore').once()
  1172. tuple(
  1173. module.run_actions(
  1174. arguments={'global': flexmock(dry_run=False), 'restore': flexmock()},
  1175. config_filename=flexmock(),
  1176. config={'repositories': []},
  1177. config_paths=[],
  1178. local_path=flexmock(),
  1179. remote_path=flexmock(),
  1180. local_borg_version=flexmock(),
  1181. repository={'path': 'repo'},
  1182. ),
  1183. )
  1184. def test_run_actions_runs_repo_list():
  1185. flexmock(module).should_receive('add_custom_log_levels')
  1186. flexmock(module).should_receive('get_skip_actions').and_return([])
  1187. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1188. flexmock(),
  1189. )
  1190. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1191. expected = flexmock()
  1192. flexmock(borgmatic.actions.repo_list).should_receive('run_repo_list').and_yield(expected).once()
  1193. result = tuple(
  1194. module.run_actions(
  1195. arguments={'global': flexmock(dry_run=False), 'repo-list': flexmock()},
  1196. config_filename=flexmock(),
  1197. config={'repositories': []},
  1198. config_paths=[],
  1199. local_path=flexmock(),
  1200. remote_path=flexmock(),
  1201. local_borg_version=flexmock(),
  1202. repository={'path': 'repo'},
  1203. ),
  1204. )
  1205. assert result == (expected,)
  1206. def test_run_actions_runs_list():
  1207. flexmock(module).should_receive('add_custom_log_levels')
  1208. flexmock(module).should_receive('get_skip_actions').and_return([])
  1209. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1210. flexmock(),
  1211. )
  1212. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1213. expected = flexmock()
  1214. flexmock(borgmatic.actions.list).should_receive('run_list').and_yield(expected).once()
  1215. result = tuple(
  1216. module.run_actions(
  1217. arguments={'global': flexmock(dry_run=False), 'list': flexmock()},
  1218. config_filename=flexmock(),
  1219. config={'repositories': []},
  1220. config_paths=[],
  1221. local_path=flexmock(),
  1222. remote_path=flexmock(),
  1223. local_borg_version=flexmock(),
  1224. repository={'path': 'repo'},
  1225. ),
  1226. )
  1227. assert result == (expected,)
  1228. def test_run_actions_runs_repo_info():
  1229. flexmock(module).should_receive('add_custom_log_levels')
  1230. flexmock(module).should_receive('get_skip_actions').and_return([])
  1231. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1232. flexmock(),
  1233. )
  1234. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1235. expected = flexmock()
  1236. flexmock(borgmatic.actions.repo_info).should_receive('run_repo_info').and_yield(expected).once()
  1237. result = tuple(
  1238. module.run_actions(
  1239. arguments={'global': flexmock(dry_run=False), 'repo-info': flexmock()},
  1240. config_filename=flexmock(),
  1241. config={'repositories': []},
  1242. config_paths=[],
  1243. local_path=flexmock(),
  1244. remote_path=flexmock(),
  1245. local_borg_version=flexmock(),
  1246. repository={'path': 'repo'},
  1247. ),
  1248. )
  1249. assert result == (expected,)
  1250. def test_run_actions_runs_info():
  1251. flexmock(module).should_receive('add_custom_log_levels')
  1252. flexmock(module).should_receive('get_skip_actions').and_return([])
  1253. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1254. flexmock(),
  1255. )
  1256. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1257. expected = flexmock()
  1258. flexmock(borgmatic.actions.info).should_receive('run_info').and_yield(expected).once()
  1259. result = tuple(
  1260. module.run_actions(
  1261. arguments={'global': flexmock(dry_run=False), 'info': flexmock()},
  1262. config_filename=flexmock(),
  1263. config={'repositories': []},
  1264. config_paths=[],
  1265. local_path=flexmock(),
  1266. remote_path=flexmock(),
  1267. local_borg_version=flexmock(),
  1268. repository={'path': 'repo'},
  1269. ),
  1270. )
  1271. assert result == (expected,)
  1272. def test_run_actions_runs_break_lock():
  1273. flexmock(module).should_receive('add_custom_log_levels')
  1274. flexmock(module).should_receive('get_skip_actions').and_return([])
  1275. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1276. flexmock(),
  1277. )
  1278. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1279. flexmock(borgmatic.actions.break_lock).should_receive('run_break_lock').once()
  1280. tuple(
  1281. module.run_actions(
  1282. arguments={'global': flexmock(dry_run=False), 'break-lock': flexmock()},
  1283. config_filename=flexmock(),
  1284. config={'repositories': []},
  1285. config_paths=[],
  1286. local_path=flexmock(),
  1287. remote_path=flexmock(),
  1288. local_borg_version=flexmock(),
  1289. repository={'path': 'repo'},
  1290. ),
  1291. )
  1292. def test_run_actions_runs_export_key():
  1293. flexmock(module).should_receive('add_custom_log_levels')
  1294. flexmock(module).should_receive('get_skip_actions').and_return([])
  1295. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1296. flexmock(),
  1297. )
  1298. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1299. flexmock(borgmatic.actions.export_key).should_receive('run_export_key').once()
  1300. tuple(
  1301. module.run_actions(
  1302. arguments={'global': flexmock(dry_run=False), 'export': flexmock()},
  1303. config_filename=flexmock(),
  1304. config={'repositories': []},
  1305. config_paths=[],
  1306. local_path=flexmock(),
  1307. remote_path=flexmock(),
  1308. local_borg_version=flexmock(),
  1309. repository={'path': 'repo'},
  1310. ),
  1311. )
  1312. def test_run_actions_runs_import_key():
  1313. flexmock(module).should_receive('add_custom_log_levels')
  1314. flexmock(module).should_receive('get_skip_actions').and_return([])
  1315. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1316. flexmock(),
  1317. )
  1318. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1319. flexmock(borgmatic.actions.import_key).should_receive('run_import_key').once()
  1320. tuple(
  1321. module.run_actions(
  1322. arguments={'global': flexmock(dry_run=False), 'import': flexmock()},
  1323. config_filename=flexmock(),
  1324. config={'repositories': []},
  1325. config_paths=[],
  1326. local_path=flexmock(),
  1327. remote_path=flexmock(),
  1328. local_borg_version=flexmock(),
  1329. repository={'path': 'repo'},
  1330. ),
  1331. )
  1332. def test_run_actions_runs_change_passphrase():
  1333. flexmock(module).should_receive('add_custom_log_levels')
  1334. flexmock(module).should_receive('get_skip_actions').and_return([])
  1335. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1336. flexmock(),
  1337. )
  1338. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1339. flexmock(borgmatic.actions.change_passphrase).should_receive('run_change_passphrase').once()
  1340. tuple(
  1341. module.run_actions(
  1342. arguments={
  1343. 'global': flexmock(dry_run=False),
  1344. 'change-passphrase': flexmock(),
  1345. },
  1346. config_filename=flexmock(),
  1347. config={'repositories': []},
  1348. config_paths=[],
  1349. local_path=flexmock(),
  1350. remote_path=flexmock(),
  1351. local_borg_version=flexmock(),
  1352. repository={'path': 'repo'},
  1353. ),
  1354. )
  1355. def test_run_actions_runs_delete():
  1356. flexmock(module).should_receive('add_custom_log_levels')
  1357. flexmock(module).should_receive('get_skip_actions').and_return([])
  1358. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1359. flexmock(),
  1360. )
  1361. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1362. flexmock(borgmatic.actions.delete).should_receive('run_delete').once()
  1363. tuple(
  1364. module.run_actions(
  1365. arguments={'global': flexmock(dry_run=False), 'delete': flexmock()},
  1366. config_filename=flexmock(),
  1367. config={'repositories': []},
  1368. config_paths=[],
  1369. local_path=flexmock(),
  1370. remote_path=flexmock(),
  1371. local_borg_version=flexmock(),
  1372. repository={'path': 'repo'},
  1373. ),
  1374. )
  1375. def test_run_actions_runs_repo_delete():
  1376. flexmock(module).should_receive('add_custom_log_levels')
  1377. flexmock(module).should_receive('get_skip_actions').and_return([])
  1378. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1379. flexmock(),
  1380. )
  1381. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1382. flexmock(borgmatic.actions.repo_delete).should_receive('run_repo_delete').once()
  1383. tuple(
  1384. module.run_actions(
  1385. arguments={
  1386. 'global': flexmock(dry_run=False),
  1387. 'repo-delete': flexmock(),
  1388. },
  1389. config_filename=flexmock(),
  1390. config={'repositories': []},
  1391. config_paths=[],
  1392. local_path=flexmock(),
  1393. remote_path=flexmock(),
  1394. local_borg_version=flexmock(),
  1395. repository={'path': 'repo'},
  1396. ),
  1397. )
  1398. def test_run_actions_runs_borg():
  1399. flexmock(module).should_receive('add_custom_log_levels')
  1400. flexmock(module).should_receive('get_skip_actions').and_return([])
  1401. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1402. flexmock(),
  1403. )
  1404. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1405. flexmock(borgmatic.actions.borg).should_receive('run_borg').once()
  1406. tuple(
  1407. module.run_actions(
  1408. arguments={'global': flexmock(dry_run=False), 'borg': flexmock()},
  1409. config_filename=flexmock(),
  1410. config={'repositories': []},
  1411. config_paths=[],
  1412. local_path=flexmock(),
  1413. remote_path=flexmock(),
  1414. local_borg_version=flexmock(),
  1415. repository={'path': 'repo'},
  1416. ),
  1417. )
  1418. def test_run_actions_runs_multiple_actions_in_argument_order():
  1419. flexmock(module).should_receive('add_custom_log_levels')
  1420. flexmock(module).should_receive('get_skip_actions').and_return([])
  1421. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1422. flexmock(),
  1423. )
  1424. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1425. flexmock(borgmatic.actions.borg).should_receive('run_borg').once().ordered()
  1426. flexmock(borgmatic.actions.restore).should_receive('run_restore').once().ordered()
  1427. tuple(
  1428. module.run_actions(
  1429. arguments={
  1430. 'global': flexmock(dry_run=False),
  1431. 'borg': flexmock(),
  1432. 'restore': flexmock(),
  1433. },
  1434. config_filename=flexmock(),
  1435. config={'repositories': []},
  1436. config_paths=[],
  1437. local_path=flexmock(),
  1438. remote_path=flexmock(),
  1439. local_borg_version=flexmock(),
  1440. repository={'path': 'repo'},
  1441. ),
  1442. )
  1443. def test_run_actions_runs_action_hooks_for_one_action_at_a_time():
  1444. flexmock(module).should_receive('add_custom_log_levels')
  1445. flexmock(module).should_receive('get_skip_actions').and_return([])
  1446. flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
  1447. flexmock(),
  1448. )
  1449. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  1450. for action_name in ('borg', 'restore'):
  1451. flexmock(module.command).should_receive('Before_after_hooks').with_args(
  1452. command_hooks=object,
  1453. before_after='action',
  1454. umask=object,
  1455. working_directory=object,
  1456. dry_run=object,
  1457. action_names=(action_name,),
  1458. configuration_filename=object,
  1459. repository_label=object,
  1460. log_file=object,
  1461. repositories=object,
  1462. repository=object,
  1463. ).and_return(flexmock()).once()
  1464. flexmock(borgmatic.actions.borg).should_receive('run_borg')
  1465. flexmock(borgmatic.actions.restore).should_receive('run_restore')
  1466. tuple(
  1467. module.run_actions(
  1468. arguments={
  1469. 'global': flexmock(dry_run=False),
  1470. 'borg': flexmock(),
  1471. 'restore': flexmock(),
  1472. },
  1473. config_filename=flexmock(),
  1474. config={'repositories': []},
  1475. config_paths=[],
  1476. local_path=flexmock(),
  1477. remote_path=flexmock(),
  1478. local_borg_version=flexmock(),
  1479. repository={'path': 'repo'},
  1480. ),
  1481. )
  1482. @pytest.mark.parametrize(
  1483. 'resolve_env',
  1484. ((True, False),),
  1485. )
  1486. def test_load_configurations_collects_parsed_configurations_and_logs(resolve_env):
  1487. configuration = flexmock()
  1488. other_configuration = flexmock()
  1489. test_expected_logs = [flexmock(), flexmock()]
  1490. other_expected_logs = [flexmock(), flexmock()]
  1491. flexmock(module.validate).should_receive('parse_configuration').and_return(
  1492. configuration,
  1493. ['/tmp/test.yaml'],
  1494. test_expected_logs,
  1495. ).and_return(other_configuration, ['/tmp/other.yaml'], other_expected_logs)
  1496. configs, config_paths, logs = tuple(
  1497. module.load_configurations(
  1498. ('test.yaml', 'other.yaml'),
  1499. arguments=flexmock(),
  1500. resolve_env=resolve_env,
  1501. ),
  1502. )
  1503. assert configs == {'test.yaml': configuration, 'other.yaml': other_configuration}
  1504. assert config_paths == ['/tmp/other.yaml', '/tmp/test.yaml']
  1505. assert set(logs) >= set(test_expected_logs + other_expected_logs)
  1506. def test_load_configurations_logs_warning_for_permission_error():
  1507. flexmock(module.validate).should_receive('parse_configuration').and_raise(PermissionError)
  1508. configs, config_paths, logs = tuple(
  1509. module.load_configurations(('test.yaml',), arguments=flexmock()),
  1510. )
  1511. assert configs == {}
  1512. assert config_paths == []
  1513. assert max(log.levelno for log in logs) == logging.WARNING
  1514. def test_load_configurations_logs_critical_for_parse_error():
  1515. flexmock(module.validate).should_receive('parse_configuration').and_raise(ValueError)
  1516. configs, config_paths, logs = tuple(
  1517. module.load_configurations(('test.yaml',), arguments=flexmock()),
  1518. )
  1519. assert configs == {}
  1520. assert config_paths == []
  1521. assert max(log.levelno for log in logs) == logging.CRITICAL
  1522. def test_log_record_does_not_raise():
  1523. module.log_record(levelno=1, foo='bar', baz='quux')
  1524. def test_log_record_with_suppress_does_not_raise():
  1525. module.log_record(levelno=1, foo='bar', baz='quux', suppress_log=True)
  1526. def test_log_error_records_generates_output_logs_for_message_only():
  1527. flexmock(module).should_receive('log_record').replace_with(dict).once()
  1528. logs = tuple(module.log_error_records('Error'))
  1529. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  1530. def test_log_error_records_generates_output_logs_for_called_process_error_with_bytes_ouput():
  1531. flexmock(module).should_receive('log_record').replace_with(dict).times(3)
  1532. flexmock(module.logger).should_receive('getEffectiveLevel').and_return(logging.WARNING)
  1533. logs = tuple(
  1534. module.log_error_records('Error', subprocess.CalledProcessError(1, 'ls', b'error output')),
  1535. )
  1536. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  1537. assert any(log for log in logs if 'error output' in str(log))
  1538. def test_log_error_records_generates_output_logs_for_called_process_error_with_string_ouput():
  1539. flexmock(module).should_receive('log_record').replace_with(dict).times(3)
  1540. flexmock(module.logger).should_receive('getEffectiveLevel').and_return(logging.WARNING)
  1541. logs = tuple(
  1542. module.log_error_records('Error', subprocess.CalledProcessError(1, 'ls', 'error output')),
  1543. )
  1544. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  1545. assert any(log for log in logs if 'error output' in str(log))
  1546. def test_log_error_records_generates_work_around_output_logs_for_called_process_error_with_repository_access_aborted_exit_code():
  1547. flexmock(module).should_receive('log_record').replace_with(dict).times(4)
  1548. flexmock(module.logger).should_receive('getEffectiveLevel').and_return(logging.WARNING)
  1549. logs = tuple(
  1550. module.log_error_records(
  1551. 'Error',
  1552. subprocess.CalledProcessError(
  1553. module.BORG_REPOSITORY_ACCESS_ABORTED_EXIT_CODE,
  1554. 'ls',
  1555. 'error output',
  1556. ),
  1557. ),
  1558. )
  1559. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  1560. assert any(log for log in logs if 'error output' in str(log))
  1561. assert any(log for log in logs if 'To work around this' in str(log))
  1562. def test_log_error_records_splits_called_process_error_with_multiline_ouput_into_multiple_logs():
  1563. flexmock(module).should_receive('log_record').replace_with(dict).times(4)
  1564. flexmock(module.logger).should_receive('getEffectiveLevel').and_return(logging.WARNING)
  1565. logs = tuple(
  1566. module.log_error_records(
  1567. 'Error',
  1568. subprocess.CalledProcessError(1, 'ls', 'error output\nanother line'),
  1569. ),
  1570. )
  1571. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  1572. assert any(log for log in logs if 'error output' in str(log))
  1573. def test_log_error_records_generates_logs_for_value_error():
  1574. flexmock(module).should_receive('log_record').replace_with(dict).twice()
  1575. logs = tuple(module.log_error_records('Error', ValueError()))
  1576. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  1577. def test_log_error_records_generates_logs_for_os_error():
  1578. flexmock(module).should_receive('log_record').replace_with(dict).twice()
  1579. logs = tuple(module.log_error_records('Error', OSError()))
  1580. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  1581. def test_log_error_records_generates_nothing_for_other_error():
  1582. flexmock(module).should_receive('log_record').never()
  1583. logs = tuple(module.log_error_records('Error', KeyError()))
  1584. assert logs == ()
  1585. def test_get_local_path_uses_configuration_value():
  1586. assert module.get_local_path({'test.yaml': {'local_path': 'borg1'}}) == 'borg1'
  1587. def test_get_local_path_without_local_path_defaults_to_borg():
  1588. assert module.get_local_path({'test.yaml': {}}) == 'borg'
  1589. def test_collect_highlander_action_summary_logs_info_for_success_with_bootstrap():
  1590. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  1591. flexmock(module.borgmatic.actions.config.bootstrap).should_receive('run_bootstrap')
  1592. arguments = {
  1593. 'bootstrap': flexmock(repository='repo', local_path='borg7'),
  1594. 'global': flexmock(dry_run=False),
  1595. }
  1596. logs = tuple(
  1597. module.collect_highlander_action_summary_logs(
  1598. {'test.yaml': {}},
  1599. arguments=arguments,
  1600. configuration_parse_errors=False,
  1601. ),
  1602. )
  1603. assert {log.levelno for log in logs} == {logging.ANSWER}
  1604. def test_collect_highlander_action_summary_logs_error_on_bootstrap_failure():
  1605. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  1606. flexmock(module.borgmatic.actions.config.bootstrap).should_receive('run_bootstrap').and_raise(
  1607. ValueError,
  1608. )
  1609. arguments = {
  1610. 'bootstrap': flexmock(repository='repo', local_path='borg7'),
  1611. 'global': flexmock(dry_run=False),
  1612. }
  1613. logs = tuple(
  1614. module.collect_highlander_action_summary_logs(
  1615. {'test.yaml': {}},
  1616. arguments=arguments,
  1617. configuration_parse_errors=False,
  1618. ),
  1619. )
  1620. assert {log.levelno for log in logs} == {logging.CRITICAL}
  1621. def test_collect_highlander_action_summary_logs_error_on_bootstrap_local_borg_version_failure():
  1622. flexmock(module.borg_version).should_receive('local_borg_version').and_raise(ValueError)
  1623. flexmock(module.borgmatic.actions.config.bootstrap).should_receive('run_bootstrap').never()
  1624. arguments = {
  1625. 'bootstrap': flexmock(repository='repo', local_path='borg7'),
  1626. 'global': flexmock(dry_run=False),
  1627. }
  1628. logs = tuple(
  1629. module.collect_highlander_action_summary_logs(
  1630. {'test.yaml': {}},
  1631. arguments=arguments,
  1632. configuration_parse_errors=False,
  1633. ),
  1634. )
  1635. assert {log.levelno for log in logs} == {logging.CRITICAL}
  1636. def test_collect_highlander_action_summary_logs_info_for_success_with_generate():
  1637. flexmock(module.borgmatic.actions.config.generate).should_receive('run_generate')
  1638. arguments = {
  1639. 'generate': flexmock(destination='test.yaml'),
  1640. 'global': flexmock(dry_run=False),
  1641. }
  1642. logs = tuple(
  1643. module.collect_highlander_action_summary_logs(
  1644. {'test.yaml': {}},
  1645. arguments=arguments,
  1646. configuration_parse_errors=False,
  1647. ),
  1648. )
  1649. assert {log.levelno for log in logs} == {logging.ANSWER}
  1650. def test_collect_highlander_action_summary_logs_error_on_generate_failure():
  1651. flexmock(module.borgmatic.actions.config.generate).should_receive('run_generate').and_raise(
  1652. ValueError,
  1653. )
  1654. arguments = {
  1655. 'generate': flexmock(destination='test.yaml'),
  1656. 'global': flexmock(dry_run=False),
  1657. }
  1658. logs = tuple(
  1659. module.collect_highlander_action_summary_logs(
  1660. {'test.yaml': {}},
  1661. arguments=arguments,
  1662. configuration_parse_errors=False,
  1663. ),
  1664. )
  1665. assert {log.levelno for log in logs} == {logging.CRITICAL}
  1666. def test_collect_highlander_action_summary_logs_info_for_success_with_validate():
  1667. flexmock(module.borgmatic.actions.config.validate).should_receive('run_validate')
  1668. arguments = {
  1669. 'validate': flexmock(),
  1670. 'global': flexmock(dry_run=False),
  1671. }
  1672. logs = tuple(
  1673. module.collect_highlander_action_summary_logs(
  1674. {'test.yaml': {}},
  1675. arguments=arguments,
  1676. configuration_parse_errors=False,
  1677. ),
  1678. )
  1679. assert {log.levelno for log in logs} == {logging.ANSWER}
  1680. def test_collect_highlander_action_summary_logs_error_on_validate_parse_failure():
  1681. flexmock(module.borgmatic.actions.config.validate).should_receive('run_validate')
  1682. arguments = {
  1683. 'validate': flexmock(),
  1684. 'global': flexmock(dry_run=False),
  1685. }
  1686. logs = tuple(
  1687. module.collect_highlander_action_summary_logs(
  1688. {'test.yaml': {}},
  1689. arguments=arguments,
  1690. configuration_parse_errors=True,
  1691. ),
  1692. )
  1693. assert {log.levelno for log in logs} == {logging.CRITICAL}
  1694. def test_collect_highlander_action_summary_logs_error_on_run_validate_failure():
  1695. flexmock(module.borgmatic.actions.config.validate).should_receive('run_validate').and_raise(
  1696. ValueError,
  1697. )
  1698. arguments = {
  1699. 'validate': flexmock(),
  1700. 'global': flexmock(dry_run=False),
  1701. }
  1702. logs = tuple(
  1703. module.collect_highlander_action_summary_logs(
  1704. {'test.yaml': {}},
  1705. arguments=arguments,
  1706. configuration_parse_errors=False,
  1707. ),
  1708. )
  1709. assert {log.levelno for log in logs} == {logging.CRITICAL}
  1710. def test_collect_configuration_run_summary_logs_info_for_success():
  1711. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1712. flexmock(module.command).should_receive('filter_hooks').with_args(
  1713. object,
  1714. before='everything',
  1715. action_names=object,
  1716. )
  1717. flexmock(module.command).should_receive('filter_hooks').with_args(
  1718. object,
  1719. after='everything',
  1720. action_names=object,
  1721. state_names=['finish'],
  1722. )
  1723. flexmock(module.command).should_receive('execute_hooks')
  1724. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  1725. flexmock(module).should_receive('run_configuration').and_return([])
  1726. arguments = {'global': flexmock(dry_run=False)}
  1727. logs = tuple(
  1728. module.collect_configuration_run_summary_logs(
  1729. {'test.yaml': {}},
  1730. config_paths=['/tmp/test.yaml'],
  1731. arguments=arguments,
  1732. log_file_path=None,
  1733. ),
  1734. )
  1735. assert {log.levelno for log in logs} == {logging.INFO}
  1736. def test_collect_configuration_run_summary_executes_hooks_for_create():
  1737. before_everything_hook = {'before': 'everything', 'run': ['echo hi']}
  1738. after_everything_hook = {'after': 'everything', 'run': ['echo hi']}
  1739. command_hooks = (before_everything_hook, after_everything_hook)
  1740. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1741. flexmock(module.command).should_receive('filter_hooks').with_args(
  1742. command_hooks,
  1743. before='everything',
  1744. action_names=object,
  1745. ).and_return([before_everything_hook])
  1746. flexmock(module.command).should_receive('filter_hooks').with_args(
  1747. command_hooks,
  1748. after='everything',
  1749. action_names=object,
  1750. state_names=['finish'],
  1751. ).and_return([after_everything_hook])
  1752. flexmock(module.command).should_receive('execute_hooks').twice()
  1753. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  1754. flexmock(module).should_receive('run_configuration').and_return([])
  1755. arguments = {
  1756. 'create': flexmock(),
  1757. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  1758. }
  1759. logs = tuple(
  1760. module.collect_configuration_run_summary_logs(
  1761. {'test.yaml': {'commands': command_hooks}},
  1762. config_paths=['/tmp/test.yaml'],
  1763. arguments=arguments,
  1764. log_file_path=None,
  1765. ),
  1766. )
  1767. assert {log.levelno for log in logs} == {logging.INFO}
  1768. def test_collect_configuration_run_summary_deduplicates_everything_hooks_across_config_files():
  1769. before_everything_hook = {'before': 'everything', 'run': ['echo hi']}
  1770. after_everything_hook = {'after': 'everything', 'run': ['echo hi']}
  1771. command_hooks = (before_everything_hook, after_everything_hook)
  1772. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1773. flexmock(module.command).should_receive('filter_hooks').with_args(
  1774. command_hooks,
  1775. before='everything',
  1776. action_names=object,
  1777. ).and_return([before_everything_hook]).once()
  1778. flexmock(module.command).should_receive('filter_hooks').with_args(
  1779. (after_everything_hook,),
  1780. before='everything',
  1781. action_names=object,
  1782. ).and_return([]).once()
  1783. flexmock(module.command).should_receive('filter_hooks').with_args(
  1784. command_hooks,
  1785. after='everything',
  1786. action_names=object,
  1787. state_names=['finish'],
  1788. ).and_return([after_everything_hook]).once()
  1789. flexmock(module.command).should_receive('filter_hooks').with_args(
  1790. (before_everything_hook,),
  1791. after='everything',
  1792. action_names=object,
  1793. state_names=['finish'],
  1794. ).and_return([]).once()
  1795. flexmock(module.command).should_receive('execute_hooks').twice()
  1796. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  1797. flexmock(module).should_receive('run_configuration').and_return([])
  1798. arguments = {
  1799. 'create': flexmock(),
  1800. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  1801. }
  1802. logs = tuple(
  1803. module.collect_configuration_run_summary_logs(
  1804. {'test.yaml': {'commands': command_hooks}, 'other.yaml': {'commands': command_hooks}},
  1805. config_paths=['/tmp/test.yaml', '/tmp/other.yaml'],
  1806. arguments=arguments,
  1807. log_file_path=None,
  1808. ),
  1809. )
  1810. assert {log.levelno for log in logs} == {logging.INFO}
  1811. def test_collect_configuration_run_summary_logs_info_for_success_with_extract():
  1812. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1813. flexmock(module.command).should_receive('filter_hooks').with_args(
  1814. object,
  1815. before='everything',
  1816. action_names=object,
  1817. )
  1818. flexmock(module.command).should_receive('filter_hooks').with_args(
  1819. object,
  1820. after='everything',
  1821. action_names=object,
  1822. state_names=['finish'],
  1823. )
  1824. flexmock(module.command).should_receive('execute_hooks')
  1825. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  1826. flexmock(module).should_receive('run_configuration').and_return([])
  1827. arguments = {
  1828. 'extract': flexmock(repository='repo'),
  1829. 'global': flexmock(dry_run=False),
  1830. }
  1831. logs = tuple(
  1832. module.collect_configuration_run_summary_logs(
  1833. {'test.yaml': {}},
  1834. config_paths=['/tmp/test.yaml'],
  1835. arguments=arguments,
  1836. log_file_path=None,
  1837. ),
  1838. )
  1839. assert {log.levelno for log in logs} == {logging.INFO}
  1840. def test_collect_configuration_run_summary_logs_extract_with_repository_error():
  1841. flexmock(module.validate).should_receive('guard_configuration_contains_repository').and_raise(
  1842. ValueError,
  1843. )
  1844. expected_logs = (flexmock(),)
  1845. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  1846. arguments = {'extract': flexmock(repository='repo')}
  1847. logs = tuple(
  1848. module.collect_configuration_run_summary_logs(
  1849. {'test.yaml': {}},
  1850. config_paths=['/tmp/test.yaml'],
  1851. arguments=arguments,
  1852. log_file_path=None,
  1853. ),
  1854. )
  1855. assert logs == expected_logs
  1856. def test_collect_configuration_run_summary_logs_info_for_success_with_mount():
  1857. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1858. flexmock(module.command).should_receive('filter_hooks').with_args(
  1859. object,
  1860. before='everything',
  1861. action_names=object,
  1862. )
  1863. flexmock(module.command).should_receive('filter_hooks').with_args(
  1864. object,
  1865. after='everything',
  1866. action_names=object,
  1867. state_names=['finish'],
  1868. )
  1869. flexmock(module.command).should_receive('execute_hooks')
  1870. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  1871. flexmock(module).should_receive('run_configuration').and_return([])
  1872. arguments = {
  1873. 'mount': flexmock(repository='repo'),
  1874. 'global': flexmock(dry_run=False),
  1875. }
  1876. logs = tuple(
  1877. module.collect_configuration_run_summary_logs(
  1878. {'test.yaml': {}},
  1879. config_paths=['/tmp/test.yaml'],
  1880. arguments=arguments,
  1881. log_file_path=None,
  1882. ),
  1883. )
  1884. assert {log.levelno for log in logs} == {logging.INFO}
  1885. def test_collect_configuration_run_summary_logs_mount_with_repository_error():
  1886. flexmock(module.validate).should_receive('guard_configuration_contains_repository').and_raise(
  1887. ValueError,
  1888. )
  1889. expected_logs = (flexmock(),)
  1890. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  1891. arguments = {
  1892. 'mount': flexmock(repository='repo'),
  1893. 'global': flexmock(dry_run=False),
  1894. }
  1895. logs = tuple(
  1896. module.collect_configuration_run_summary_logs(
  1897. {'test.yaml': {}},
  1898. config_paths=['/tmp/test.yaml'],
  1899. arguments=arguments,
  1900. log_file_path=None,
  1901. ),
  1902. )
  1903. assert logs == expected_logs
  1904. def test_collect_configuration_run_summary_logs_missing_configs_error():
  1905. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1906. flexmock(module.command).should_receive('filter_hooks').with_args(
  1907. object,
  1908. before='everything',
  1909. action_names=object,
  1910. )
  1911. flexmock(module.command).should_receive('filter_hooks').with_args(
  1912. object,
  1913. after='everything',
  1914. action_names=object,
  1915. state_names=['fail'],
  1916. )
  1917. flexmock(module.command).should_receive('execute_hooks')
  1918. arguments = {'global': flexmock(config_paths=[])}
  1919. expected_logs = (flexmock(),)
  1920. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  1921. logs = tuple(
  1922. module.collect_configuration_run_summary_logs(
  1923. {},
  1924. config_paths=[],
  1925. arguments=arguments,
  1926. log_file_path=None,
  1927. ),
  1928. )
  1929. assert logs == expected_logs
  1930. def test_collect_configuration_run_summary_logs_before_hook_error():
  1931. before_everything_hook = {'before': 'everything', 'run': ['echo hi']}
  1932. after_everything_hook = {'after': 'everything', 'run': ['echo hi']}
  1933. command_hooks = (before_everything_hook, after_everything_hook)
  1934. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1935. flexmock(module.command).should_receive('filter_hooks').with_args(
  1936. object,
  1937. before='everything',
  1938. action_names=object,
  1939. ).and_return([before_everything_hook])
  1940. flexmock(module.command).should_receive('filter_hooks').with_args(
  1941. object,
  1942. after='everything',
  1943. action_names=object,
  1944. state_names=['fail'],
  1945. ).and_return([after_everything_hook])
  1946. flexmock(module.command).should_receive('execute_hooks').and_raise(ValueError)
  1947. expected_logs = (flexmock(),)
  1948. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  1949. arguments = {
  1950. 'create': flexmock(),
  1951. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  1952. }
  1953. logs = tuple(
  1954. module.collect_configuration_run_summary_logs(
  1955. {'test.yaml': {'commands': command_hooks}},
  1956. config_paths=['/tmp/test.yaml'],
  1957. arguments=arguments,
  1958. log_file_path=None,
  1959. ),
  1960. )
  1961. assert logs == expected_logs
  1962. def test_collect_configuration_run_summary_logs_after_hook_error():
  1963. before_everything_hook = {'before': 'everything', 'run': ['echo hi']}
  1964. after_everything_hook = {'after': 'everything', 'run': ['echo hi']}
  1965. command_hooks = (before_everything_hook, after_everything_hook)
  1966. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1967. flexmock(module.command).should_receive('filter_hooks').with_args(
  1968. object,
  1969. before='everything',
  1970. action_names=object,
  1971. ).and_return([before_everything_hook])
  1972. flexmock(module.command).should_receive('filter_hooks').with_args(
  1973. object,
  1974. after='everything',
  1975. action_names=object,
  1976. state_names=['finish'],
  1977. ).and_return([after_everything_hook])
  1978. flexmock(module.command).should_receive('execute_hooks').and_return(None).and_raise(ValueError)
  1979. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  1980. flexmock(module).should_receive('run_configuration').and_return([])
  1981. expected_logs = (flexmock(),)
  1982. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  1983. arguments = {
  1984. 'create': flexmock(),
  1985. 'global': flexmock(monitoring_verbosity=1, dry_run=False),
  1986. }
  1987. logs = tuple(
  1988. module.collect_configuration_run_summary_logs(
  1989. {'test.yaml': {'commands': command_hooks}},
  1990. config_paths=['/tmp/test.yaml'],
  1991. arguments=arguments,
  1992. log_file_path=None,
  1993. ),
  1994. )
  1995. assert expected_logs[0] in logs
  1996. def test_collect_configuration_run_summary_logs_for_list_with_archive_and_repository_error():
  1997. flexmock(module.validate).should_receive('guard_configuration_contains_repository').and_raise(
  1998. ValueError,
  1999. )
  2000. expected_logs = (flexmock(),)
  2001. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  2002. arguments = {
  2003. 'list': flexmock(repository='repo', archive='test'),
  2004. 'global': flexmock(dry_run=False),
  2005. }
  2006. logs = tuple(
  2007. module.collect_configuration_run_summary_logs(
  2008. {'test.yaml': {}},
  2009. config_paths=['/tmp/test.yaml'],
  2010. arguments=arguments,
  2011. log_file_path=None,
  2012. ),
  2013. )
  2014. assert logs == expected_logs
  2015. def test_collect_configuration_run_summary_logs_info_for_success_with_list():
  2016. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  2017. flexmock(module.command).should_receive('filter_hooks').with_args(
  2018. object,
  2019. before='everything',
  2020. action_names=object,
  2021. )
  2022. flexmock(module.command).should_receive('filter_hooks').with_args(
  2023. object,
  2024. after='everything',
  2025. action_names=object,
  2026. state_names=['finish'],
  2027. )
  2028. flexmock(module.command).should_receive('execute_hooks')
  2029. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  2030. flexmock(module).should_receive('run_configuration').and_return([])
  2031. arguments = {
  2032. 'list': flexmock(repository='repo', archive=None),
  2033. 'global': flexmock(dry_run=False),
  2034. }
  2035. logs = tuple(
  2036. module.collect_configuration_run_summary_logs(
  2037. {'test.yaml': {}},
  2038. config_paths=['/tmp/test.yaml'],
  2039. arguments=arguments,
  2040. log_file_path=None,
  2041. ),
  2042. )
  2043. assert {log.levelno for log in logs} == {logging.INFO}
  2044. def test_collect_configuration_run_summary_logs_run_configuration_error_logs():
  2045. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  2046. flexmock(module.command).should_receive('filter_hooks').with_args(
  2047. object,
  2048. before='everything',
  2049. action_names=object,
  2050. )
  2051. flexmock(module.command).should_receive('filter_hooks').with_args(
  2052. object,
  2053. after='everything',
  2054. action_names=object,
  2055. state_names=['fail'],
  2056. )
  2057. flexmock(module.command).should_receive('execute_hooks')
  2058. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  2059. flexmock(module).should_receive('run_configuration').and_return(
  2060. [logging.makeLogRecord(dict(levelno=logging.CRITICAL, levelname='CRITICAL', msg='Error'))],
  2061. )
  2062. flexmock(module).should_receive('log_error_records').and_return([])
  2063. arguments = {'global': flexmock(dry_run=False)}
  2064. logs = tuple(
  2065. module.collect_configuration_run_summary_logs(
  2066. {'test.yaml': {}},
  2067. config_paths=['/tmp/test.yaml'],
  2068. arguments=arguments,
  2069. log_file_path=None,
  2070. ),
  2071. )
  2072. assert {log.levelno for log in logs} == {logging.CRITICAL}
  2073. def test_collect_configuration_run_summary_logs_run_umount_error():
  2074. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  2075. flexmock(module.command).should_receive('filter_hooks').with_args(
  2076. object,
  2077. before='everything',
  2078. action_names=object,
  2079. )
  2080. flexmock(module.command).should_receive('filter_hooks').with_args(
  2081. object,
  2082. after='everything',
  2083. action_names=object,
  2084. state_names=['fail'],
  2085. )
  2086. flexmock(module.command).should_receive('execute_hooks')
  2087. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  2088. flexmock(module).should_receive('run_configuration').and_return([])
  2089. flexmock(module.borg_umount).should_receive('unmount_archive').and_raise(OSError)
  2090. flexmock(module).should_receive('log_error_records').and_return(
  2091. [logging.makeLogRecord(dict(levelno=logging.CRITICAL, levelname='CRITICAL', msg='Error'))],
  2092. )
  2093. arguments = {
  2094. 'umount': flexmock(mount_point='/mnt'),
  2095. 'global': flexmock(dry_run=False),
  2096. }
  2097. logs = tuple(
  2098. module.collect_configuration_run_summary_logs(
  2099. {'test.yaml': {}},
  2100. config_paths=['/tmp/test.yaml'],
  2101. arguments=arguments,
  2102. log_file_path=None,
  2103. ),
  2104. )
  2105. assert {log.levelno for log in logs} == {logging.INFO, logging.CRITICAL}
  2106. def test_collect_configuration_run_summary_logs_outputs_merged_json_results():
  2107. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  2108. flexmock(module.command).should_receive('filter_hooks').with_args(
  2109. object,
  2110. before='everything',
  2111. action_names=object,
  2112. )
  2113. flexmock(module.command).should_receive('filter_hooks').with_args(
  2114. object,
  2115. after='everything',
  2116. action_names=object,
  2117. state_names=['finish'],
  2118. )
  2119. flexmock(module.command).should_receive('execute_hooks')
  2120. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  2121. flexmock(module).should_receive('run_configuration').and_return(['foo', 'bar']).and_return(
  2122. ['baz'],
  2123. )
  2124. stdout = flexmock()
  2125. stdout.should_receive('write').with_args('["foo", "bar", "baz"]').once()
  2126. flexmock(module.sys).stdout = stdout
  2127. arguments = {'global': flexmock(dry_run=False)}
  2128. tuple(
  2129. module.collect_configuration_run_summary_logs(
  2130. {'test.yaml': {}, 'test2.yaml': {}},
  2131. config_paths=['/tmp/test.yaml', '/tmp/test2.yaml'],
  2132. arguments=arguments,
  2133. log_file_path=None,
  2134. ),
  2135. )
  2136. def test_check_and_show_help_on_no_args_shows_help_when_no_args_and_default_actions_false():
  2137. flexmock(module.sys).should_receive('argv').and_return(['borgmatic'])
  2138. flexmock(module).should_receive('parse_arguments').with_args('--help').once()
  2139. flexmock(module.sys).should_receive('exit').with_args(0).once()
  2140. module.check_and_show_help_on_no_args({'test.yaml': {'default_actions': False}})
  2141. def test_check_and_show_help_on_no_args_does_not_show_help_when_no_args_and_default_actions_true():
  2142. flexmock(module.sys).should_receive('argv').and_return(['borgmatic'])
  2143. flexmock(module).should_receive('parse_arguments').never()
  2144. flexmock(module.sys).should_receive('exit').never()
  2145. module.check_and_show_help_on_no_args({'test.yaml': {'default_actions': True}})
  2146. def test_check_and_show_help_on_no_args_does_not_show_help_when_args_provided():
  2147. flexmock(module.sys).should_receive('argv').and_return(['borgmatic', '--create'])
  2148. flexmock(module).should_receive('parse_arguments').never()
  2149. flexmock(module.sys).should_receive('exit').never()
  2150. module.check_and_show_help_on_no_args({'test.yaml': {'default_actions': False}})
  2151. def test_check_and_show_help_on_no_args_with_no_default_actions_in_all_configs():
  2152. flexmock(module.sys).should_receive('argv').and_return(['borgmatic'])
  2153. # Both configs have default_actions set to False, so help should be shown
  2154. configs = {
  2155. 'config1.yaml': {'default_actions': False},
  2156. 'config2.yaml': {'default_actions': False},
  2157. }
  2158. # Expect help to be shown
  2159. flexmock(module).should_receive('parse_arguments').with_args('--help').once()
  2160. flexmock(module.sys).should_receive('exit').with_args(0).once()
  2161. module.check_and_show_help_on_no_args(configs)
  2162. def test_check_and_show_help_on_no_args_with_conflicting_configs():
  2163. flexmock(module.sys).should_receive('argv').and_return(['borgmatic'])
  2164. # Simulate two config files with conflicting 'default_actions' values
  2165. configs = {
  2166. 'config1.yaml': {'default_actions': True},
  2167. 'config2.yaml': {'default_actions': False},
  2168. }
  2169. # Expect help not to be shown because at least one config enables default actions
  2170. flexmock(module).should_receive('parse_arguments').never()
  2171. flexmock(module.sys).should_receive('exit').never()
  2172. module.check_and_show_help_on_no_args(configs)
  2173. def test_get_singular_option_value_with_conflicting_values_exits():
  2174. flexmock(module).should_receive('configure_logging')
  2175. flexmock(module).should_receive('exit_with_help_link').once()
  2176. module.get_singular_option_value(
  2177. configs={
  2178. 'test1.yaml': {'foo': 1, 'bar': 'baz'},
  2179. 'test2.yaml': {'foo': 2, 'bar': 'baz'},
  2180. },
  2181. option_name='foo',
  2182. )
  2183. def test_get_singular_option_value_with_same_value_returns_it():
  2184. flexmock(module).should_receive('configure_logging').never()
  2185. flexmock(module).should_receive('exit_with_help_link').never()
  2186. assert (
  2187. module.get_singular_option_value(
  2188. configs={
  2189. 'test1.yaml': {'foo': 1, 'bar': 'baz'},
  2190. 'test2.yaml': {'foo': 1, 'bar': 'baz'},
  2191. },
  2192. option_name='foo',
  2193. )
  2194. == 1
  2195. )
  2196. def test_get_singular_option_value_with_no_values_returns_none():
  2197. flexmock(module).should_receive('configure_logging').never()
  2198. flexmock(module).should_receive('exit_with_help_link').never()
  2199. assert (
  2200. module.get_singular_option_value(
  2201. configs={
  2202. 'test1.yaml': {'bar': 'baz'},
  2203. 'test2.yaml': {'bar': 'baz'},
  2204. },
  2205. option_name='foo',
  2206. )
  2207. is None
  2208. )
  2209. def test_get_singular_option_value_with_no_config_returns_none():
  2210. flexmock(module).should_receive('configure_logging').never()
  2211. flexmock(module).should_receive('exit_with_help_link').never()
  2212. assert (
  2213. module.get_singular_option_value(
  2214. configs={},
  2215. option_name='foo',
  2216. )
  2217. is None
  2218. )