test_borgmatic.py 71 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691
  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_run_configuration_runs_actions_for_each_repository():
  25. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  26. flexmock(module).should_receive('get_skip_actions').and_return([])
  27. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  28. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  29. expected_results = [flexmock(), flexmock()]
  30. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  31. flexmock(module).should_receive('run_actions').and_return(expected_results[:1]).and_return(
  32. expected_results[1:]
  33. )
  34. config = {'repositories': [{'path': 'foo'}, {'path': 'bar'}]}
  35. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False)}
  36. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  37. assert results == expected_results
  38. def test_run_configuration_with_skip_actions_does_not_raise():
  39. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  40. flexmock(module).should_receive('get_skip_actions').and_return(['compact'])
  41. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  42. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  43. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  44. flexmock(module).should_receive('run_actions').and_return(flexmock()).and_return(flexmock())
  45. config = {'repositories': [{'path': 'foo'}, {'path': 'bar'}], 'skip_actions': ['compact']}
  46. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False)}
  47. list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  48. def test_run_configuration_with_invalid_borg_version_errors():
  49. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  50. flexmock(module).should_receive('get_skip_actions').and_return([])
  51. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  52. flexmock(module.borg_version).should_receive('local_borg_version').and_raise(ValueError)
  53. flexmock(module.dispatch).should_receive('call_hooks').never()
  54. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  55. flexmock(module).should_receive('run_actions').never()
  56. config = {'repositories': [{'path': 'foo'}]}
  57. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'prune': flexmock()}
  58. list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  59. def test_run_configuration_logs_monitor_start_error():
  60. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  61. flexmock(module).should_receive('get_skip_actions').and_return([])
  62. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  63. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  64. flexmock(module.dispatch).should_receive('call_hooks').and_raise(OSError).and_return(
  65. None
  66. ).and_return(None).and_return(None)
  67. expected_results = [flexmock()]
  68. flexmock(module).should_receive('log_error_records').and_return(expected_results)
  69. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  70. flexmock(module).should_receive('run_actions').never()
  71. flexmock(module.command).should_receive('filter_hooks')
  72. flexmock(module.command).should_receive('execute_hooks')
  73. config = {'repositories': [{'path': 'foo'}]}
  74. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  75. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  76. assert results == expected_results
  77. def test_run_configuration_bails_for_monitor_start_soft_failure():
  78. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  79. flexmock(module).should_receive('get_skip_actions').and_return([])
  80. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  81. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  82. error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
  83. flexmock(module.dispatch).should_receive('call_hooks').and_raise(error).and_return(None)
  84. flexmock(module).should_receive('log_error_records').never()
  85. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  86. flexmock(module).should_receive('run_actions').never()
  87. config = {'repositories': [{'path': 'foo'}, {'path': 'bar'}]}
  88. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  89. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  90. assert results == []
  91. def test_run_configuration_logs_actions_error():
  92. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  93. flexmock(module).should_receive('get_skip_actions').and_return([])
  94. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  95. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  96. flexmock(module.dispatch).should_receive('call_hooks')
  97. expected_results = [flexmock()]
  98. flexmock(module).should_receive('log_error_records').and_return(expected_results)
  99. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  100. flexmock(module).should_receive('run_actions').and_raise(OSError)
  101. flexmock(module.command).should_receive('filter_hooks')
  102. flexmock(module.command).should_receive('execute_hooks')
  103. config = {'repositories': [{'path': 'foo'}]}
  104. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False)}
  105. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  106. assert results == expected_results
  107. def test_run_configuration_skips_remaining_actions_for_actions_soft_failure_but_still_runs_next_repository_actions():
  108. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  109. flexmock(module).should_receive('get_skip_actions').and_return([])
  110. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  111. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  112. flexmock(module.dispatch).should_receive('call_hooks').times(5)
  113. error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
  114. log = flexmock()
  115. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  116. flexmock(module).should_receive('run_actions').twice().and_raise(error).and_yield(log)
  117. flexmock(module).should_receive('log_error_records').never()
  118. flexmock(module.command).should_receive('considered_soft_failure').and_return(True)
  119. config = {'repositories': [{'path': 'foo'}, {'path': 'bar'}]}
  120. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  121. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  122. assert results == [log]
  123. def test_run_configuration_logs_monitor_log_error():
  124. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  125. flexmock(module).should_receive('get_skip_actions').and_return([])
  126. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  127. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  128. flexmock(module.dispatch).should_receive('call_hooks').and_return(None).and_return(
  129. None
  130. ).and_raise(OSError)
  131. expected_results = [flexmock()]
  132. flexmock(module).should_receive('log_error_records').and_return(expected_results)
  133. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  134. flexmock(module).should_receive('run_actions').and_return([])
  135. flexmock(module.command).should_receive('filter_hooks')
  136. flexmock(module.command).should_receive('execute_hooks')
  137. config = {'repositories': [{'path': 'foo'}]}
  138. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  139. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  140. assert results == expected_results
  141. def test_run_configuration_still_pings_monitor_for_monitor_log_soft_failure():
  142. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  143. flexmock(module).should_receive('get_skip_actions').and_return([])
  144. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  145. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  146. error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
  147. flexmock(module.dispatch).should_receive('call_hooks').and_return(None).and_return(
  148. None
  149. ).and_raise(error).and_return(None).and_return(None).times(5)
  150. flexmock(module).should_receive('log_error_records').never()
  151. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  152. flexmock(module).should_receive('run_actions').and_return([])
  153. flexmock(module.command).should_receive('considered_soft_failure').and_return(True)
  154. config = {'repositories': [{'path': 'foo'}]}
  155. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  156. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  157. assert results == []
  158. def test_run_configuration_logs_monitor_finish_error():
  159. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  160. flexmock(module).should_receive('get_skip_actions').and_return([])
  161. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  162. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  163. flexmock(module.dispatch).should_receive('call_hooks').and_return(None).and_return(
  164. None
  165. ).and_return(None).and_raise(OSError)
  166. expected_results = [flexmock()]
  167. flexmock(module).should_receive('log_error_records').and_return(expected_results)
  168. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  169. flexmock(module).should_receive('run_actions').and_return([])
  170. flexmock(module.command).should_receive('filter_hooks')
  171. flexmock(module.command).should_receive('execute_hooks')
  172. config = {'repositories': [{'path': 'foo'}]}
  173. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  174. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  175. assert results == expected_results
  176. def test_run_configuration_bails_for_monitor_finish_soft_failure():
  177. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  178. flexmock(module).should_receive('get_skip_actions').and_return([])
  179. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  180. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  181. error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
  182. flexmock(module.dispatch).should_receive('call_hooks').and_return(None).and_return(
  183. None
  184. ).and_raise(None).and_raise(error)
  185. flexmock(module).should_receive('log_error_records').never()
  186. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  187. flexmock(module).should_receive('run_actions').and_return([])
  188. flexmock(module.command).should_receive('considered_soft_failure').and_return(True)
  189. config = {'repositories': [{'path': 'foo'}]}
  190. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  191. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  192. assert results == []
  193. def test_run_configuration_does_not_call_monitoring_hooks_if_monitoring_hooks_are_disabled():
  194. flexmock(module).should_receive('verbosity_to_log_level').and_return(module.DISABLED)
  195. flexmock(module).should_receive('get_skip_actions').and_return([])
  196. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  197. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  198. flexmock(module.dispatch).should_receive('call_hooks').never()
  199. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  200. flexmock(module).should_receive('run_actions').and_return([])
  201. config = {'repositories': [{'path': 'foo'}]}
  202. arguments = {'global': flexmock(monitoring_verbosity=-2, dry_run=False), 'create': flexmock()}
  203. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  204. assert results == []
  205. def test_run_configuration_logs_on_error_hook_error():
  206. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  207. flexmock(module).should_receive('get_skip_actions').and_return([])
  208. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  209. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  210. flexmock(module.command).should_receive('filter_hooks')
  211. flexmock(module.command).should_receive('execute_hooks').and_raise(OSError)
  212. expected_results = [flexmock(), flexmock()]
  213. flexmock(module).should_receive('log_error_records').and_return(
  214. expected_results[:1]
  215. ).and_return(expected_results[1:])
  216. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  217. flexmock(module).should_receive('run_actions').and_raise(OSError)
  218. config = {'repositories': [{'path': 'foo'}]}
  219. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  220. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  221. assert results == expected_results
  222. def test_run_configuration_bails_for_on_error_hook_soft_failure():
  223. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  224. flexmock(module).should_receive('get_skip_actions').and_return([])
  225. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  226. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  227. error = subprocess.CalledProcessError(borgmatic.hooks.command.SOFT_FAIL_EXIT_CODE, 'try again')
  228. flexmock(module.command).should_receive('filter_hooks')
  229. flexmock(module.command).should_receive('execute_hooks').and_raise(error)
  230. expected_results = [flexmock()]
  231. flexmock(module).should_receive('log_error_records').and_return(expected_results)
  232. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  233. flexmock(module).should_receive('run_actions').and_raise(OSError)
  234. config = {'repositories': [{'path': 'foo'}]}
  235. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  236. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  237. assert results == expected_results
  238. def test_run_configuration_retries_soft_error():
  239. # Run action first fails, second passes.
  240. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  241. flexmock(module).should_receive('get_skip_actions').and_return([])
  242. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  243. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  244. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  245. flexmock(module).should_receive('run_actions').and_raise(OSError).and_return([])
  246. flexmock(module).should_receive('log_error_records').and_return([flexmock()]).once()
  247. flexmock(module.command).should_receive('filter_hooks').never()
  248. flexmock(module.command).should_receive('execute_hooks').never()
  249. config = {'repositories': [{'path': 'foo'}], 'retries': 1}
  250. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  251. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  252. assert results == []
  253. def test_run_configuration_retries_hard_error():
  254. # Run action fails twice.
  255. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  256. flexmock(module).should_receive('get_skip_actions').and_return([])
  257. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  258. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  259. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  260. flexmock(module).should_receive('run_actions').and_raise(OSError).times(2)
  261. flexmock(module).should_receive('log_error_records').with_args(
  262. 'Error running actions for repository',
  263. OSError,
  264. levelno=logging.WARNING,
  265. log_command_error_output=True,
  266. ).and_return([flexmock()])
  267. error_logs = [flexmock()]
  268. flexmock(module).should_receive('log_error_records').with_args(
  269. 'Error running actions for repository',
  270. OSError,
  271. ).and_return(error_logs)
  272. flexmock(module.command).should_receive('filter_hooks')
  273. flexmock(module.command).should_receive('execute_hooks')
  274. config = {'repositories': [{'path': 'foo'}], 'retries': 1}
  275. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  276. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  277. assert results == error_logs
  278. def test_run_configuration_retries_repositories_in_order():
  279. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  280. flexmock(module).should_receive('get_skip_actions').and_return([])
  281. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  282. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  283. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  284. flexmock(module).should_receive('run_actions').and_raise(OSError).times(2)
  285. expected_results = [flexmock(), flexmock()]
  286. flexmock(module).should_receive('log_error_records').with_args(
  287. 'Error running actions for repository', OSError
  288. ).and_return(expected_results[:1]).ordered()
  289. flexmock(module).should_receive('log_error_records').with_args(
  290. 'Error running actions for repository', OSError
  291. ).and_return(expected_results[1:]).ordered()
  292. flexmock(module.command).should_receive('filter_hooks')
  293. flexmock(module.command).should_receive('execute_hooks')
  294. config = {'repositories': [{'path': 'foo'}, {'path': 'bar'}]}
  295. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  296. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  297. assert results == expected_results
  298. def test_run_configuration_retries_round_robin():
  299. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  300. flexmock(module).should_receive('get_skip_actions').and_return([])
  301. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  302. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  303. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  304. flexmock(module).should_receive('run_actions').and_raise(OSError).times(4)
  305. flexmock(module).should_receive('log_error_records').with_args(
  306. 'Error running actions for repository',
  307. OSError,
  308. levelno=logging.WARNING,
  309. log_command_error_output=True,
  310. ).and_return([flexmock()]).ordered()
  311. flexmock(module).should_receive('log_error_records').with_args(
  312. 'Error running actions for repository',
  313. OSError,
  314. levelno=logging.WARNING,
  315. log_command_error_output=True,
  316. ).and_return([flexmock()]).ordered()
  317. foo_error_logs = [flexmock()]
  318. flexmock(module).should_receive('log_error_records').with_args(
  319. 'Error running actions for repository', OSError
  320. ).and_return(foo_error_logs).ordered()
  321. bar_error_logs = [flexmock()]
  322. flexmock(module).should_receive('log_error_records').with_args(
  323. 'Error running actions for repository', OSError
  324. ).and_return(bar_error_logs).ordered()
  325. flexmock(module.command).should_receive('filter_hooks')
  326. flexmock(module.command).should_receive('execute_hooks')
  327. config = {
  328. 'repositories': [{'path': 'foo'}, {'path': 'bar'}],
  329. 'retries': 1,
  330. }
  331. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  332. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  333. assert results == foo_error_logs + bar_error_logs
  334. def test_run_configuration_with_one_retry():
  335. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  336. flexmock(module).should_receive('get_skip_actions').and_return([])
  337. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  338. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  339. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  340. flexmock(module).should_receive('run_actions').and_raise(OSError).and_raise(OSError).and_return(
  341. []
  342. ).and_raise(OSError).times(4)
  343. flexmock(module).should_receive('log_error_records').with_args(
  344. 'Error running actions for repository',
  345. OSError,
  346. levelno=logging.WARNING,
  347. log_command_error_output=True,
  348. ).and_return([flexmock()]).ordered()
  349. flexmock(module).should_receive('log_error_records').with_args(
  350. 'Error running actions for repository',
  351. OSError,
  352. levelno=logging.WARNING,
  353. log_command_error_output=True,
  354. ).and_return(flexmock()).ordered()
  355. error_logs = [flexmock()]
  356. flexmock(module).should_receive('log_error_records').with_args(
  357. 'Error running actions for repository', OSError
  358. ).and_return(error_logs).ordered()
  359. flexmock(module.command).should_receive('filter_hooks')
  360. flexmock(module.command).should_receive('execute_hooks')
  361. config = {
  362. 'repositories': [{'path': 'foo'}, {'path': 'bar'}],
  363. 'retries': 1,
  364. }
  365. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  366. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  367. assert results == error_logs
  368. def test_run_configuration_with_retry_wait_does_backoff_after_each_retry():
  369. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  370. flexmock(module).should_receive('get_skip_actions').and_return([])
  371. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  372. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  373. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  374. flexmock(module).should_receive('run_actions').and_raise(OSError).times(4)
  375. flexmock(module).should_receive('log_error_records').with_args(
  376. 'Error running actions for repository',
  377. OSError,
  378. levelno=logging.WARNING,
  379. log_command_error_output=True,
  380. ).and_return([flexmock()]).ordered()
  381. flexmock(time).should_receive('sleep').with_args(10).and_return().ordered()
  382. flexmock(module).should_receive('log_error_records').with_args(
  383. 'Error running actions for repository',
  384. OSError,
  385. levelno=logging.WARNING,
  386. log_command_error_output=True,
  387. ).and_return([flexmock()]).ordered()
  388. flexmock(time).should_receive('sleep').with_args(20).and_return().ordered()
  389. flexmock(module).should_receive('log_error_records').with_args(
  390. 'Error running actions for repository',
  391. OSError,
  392. levelno=logging.WARNING,
  393. log_command_error_output=True,
  394. ).and_return([flexmock()]).ordered()
  395. flexmock(time).should_receive('sleep').with_args(30).and_return().ordered()
  396. error_logs = [flexmock()]
  397. flexmock(module).should_receive('log_error_records').with_args(
  398. 'Error running actions for repository', OSError
  399. ).and_return(error_logs).ordered()
  400. flexmock(module.command).should_receive('filter_hooks')
  401. flexmock(module.command).should_receive('execute_hooks')
  402. config = {
  403. 'repositories': [{'path': 'foo'}],
  404. 'retries': 3,
  405. 'retry_wait': 10,
  406. }
  407. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  408. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  409. assert results == error_logs
  410. def test_run_configuration_with_multiple_repositories_retries_with_timeout():
  411. flexmock(module).should_receive('verbosity_to_log_level').and_return(logging.INFO)
  412. flexmock(module).should_receive('get_skip_actions').and_return([])
  413. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  414. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  415. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  416. flexmock(module).should_receive('run_actions').and_raise(OSError).and_raise(OSError).and_return(
  417. []
  418. ).and_raise(OSError).times(4)
  419. flexmock(module).should_receive('log_error_records').with_args(
  420. 'Error running actions for repository',
  421. OSError,
  422. levelno=logging.WARNING,
  423. log_command_error_output=True,
  424. ).and_return([flexmock()]).ordered()
  425. flexmock(module).should_receive('log_error_records').with_args(
  426. 'Error running actions for repository',
  427. OSError,
  428. levelno=logging.WARNING,
  429. log_command_error_output=True,
  430. ).and_return([flexmock()]).ordered()
  431. # Sleep before retrying foo (and passing)
  432. flexmock(time).should_receive('sleep').with_args(10).and_return().ordered()
  433. # Sleep before retrying bar (and failing)
  434. flexmock(time).should_receive('sleep').with_args(10).and_return().ordered()
  435. error_logs = [flexmock()]
  436. flexmock(module).should_receive('log_error_records').with_args(
  437. 'Error running actions for repository', OSError
  438. ).and_return(error_logs).ordered()
  439. flexmock(module.command).should_receive('filter_hooks')
  440. flexmock(module.command).should_receive('execute_hooks')
  441. config = {
  442. 'repositories': [{'path': 'foo'}, {'path': 'bar'}],
  443. 'retries': 1,
  444. 'retry_wait': 10,
  445. }
  446. arguments = {'global': flexmock(monitoring_verbosity=1, dry_run=False), 'create': flexmock()}
  447. results = list(module.run_configuration('test.yaml', config, ['/tmp/test.yaml'], arguments))
  448. assert results == error_logs
  449. def test_run_actions_runs_repo_create():
  450. flexmock(module).should_receive('add_custom_log_levels')
  451. flexmock(module).should_receive('get_skip_actions').and_return([])
  452. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  453. flexmock(borgmatic.actions.repo_create).should_receive('run_repo_create').once()
  454. tuple(
  455. module.run_actions(
  456. arguments={
  457. 'global': flexmock(dry_run=False, log_file='foo'),
  458. 'repo-create': flexmock(),
  459. },
  460. config_filename=flexmock(),
  461. config={'repositories': []},
  462. config_paths=[],
  463. local_path=flexmock(),
  464. remote_path=flexmock(),
  465. local_borg_version=flexmock(),
  466. repository={'path': 'repo'},
  467. )
  468. )
  469. def test_run_actions_adds_label_file_to_hook_context():
  470. flexmock(module).should_receive('add_custom_log_levels')
  471. flexmock(module).should_receive('get_skip_actions').and_return([])
  472. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  473. expected = flexmock()
  474. flexmock(borgmatic.actions.create).should_receive('run_create').with_args(
  475. config_filename=object,
  476. repository={'path': 'repo', 'label': 'my repo'},
  477. config={'repositories': []},
  478. config_paths=[],
  479. local_borg_version=object,
  480. create_arguments=object,
  481. global_arguments=object,
  482. dry_run_label='',
  483. local_path=object,
  484. remote_path=object,
  485. ).once().and_return(expected)
  486. result = tuple(
  487. module.run_actions(
  488. arguments={'global': flexmock(dry_run=False, log_file=None), 'create': flexmock()},
  489. config_filename=flexmock(),
  490. config={'repositories': []},
  491. config_paths=[],
  492. local_path=flexmock(),
  493. remote_path=flexmock(),
  494. local_borg_version=flexmock(),
  495. repository={'path': 'repo', 'label': 'my repo'},
  496. )
  497. )
  498. assert result == (expected,)
  499. def test_run_actions_adds_log_file_to_hook_context():
  500. flexmock(module).should_receive('add_custom_log_levels')
  501. flexmock(module).should_receive('get_skip_actions').and_return([])
  502. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  503. expected = flexmock()
  504. flexmock(borgmatic.actions.create).should_receive('run_create').with_args(
  505. config_filename=object,
  506. repository={'path': 'repo'},
  507. config={'repositories': []},
  508. config_paths=[],
  509. local_borg_version=object,
  510. create_arguments=object,
  511. global_arguments=object,
  512. dry_run_label='',
  513. local_path=object,
  514. remote_path=object,
  515. ).once().and_return(expected)
  516. result = tuple(
  517. module.run_actions(
  518. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'create': flexmock()},
  519. config_filename=flexmock(),
  520. config={'repositories': []},
  521. config_paths=[],
  522. local_path=flexmock(),
  523. remote_path=flexmock(),
  524. local_borg_version=flexmock(),
  525. repository={'path': 'repo'},
  526. )
  527. )
  528. assert result == (expected,)
  529. def test_run_actions_runs_transfer():
  530. flexmock(module).should_receive('add_custom_log_levels')
  531. flexmock(module).should_receive('get_skip_actions').and_return([])
  532. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  533. flexmock(borgmatic.actions.transfer).should_receive('run_transfer').once()
  534. tuple(
  535. module.run_actions(
  536. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'transfer': flexmock()},
  537. config_filename=flexmock(),
  538. config={'repositories': []},
  539. config_paths=[],
  540. local_path=flexmock(),
  541. remote_path=flexmock(),
  542. local_borg_version=flexmock(),
  543. repository={'path': 'repo'},
  544. )
  545. )
  546. def test_run_actions_runs_create():
  547. flexmock(module).should_receive('add_custom_log_levels')
  548. flexmock(module).should_receive('get_skip_actions').and_return([])
  549. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  550. expected = flexmock()
  551. flexmock(borgmatic.actions.create).should_receive('run_create').and_yield(expected).once()
  552. result = tuple(
  553. module.run_actions(
  554. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'create': flexmock()},
  555. config_filename=flexmock(),
  556. config={'repositories': []},
  557. config_paths=[],
  558. local_path=flexmock(),
  559. remote_path=flexmock(),
  560. local_borg_version=flexmock(),
  561. repository={'path': 'repo'},
  562. )
  563. )
  564. assert result == (expected,)
  565. def test_run_actions_with_skip_actions_skips_create():
  566. flexmock(module).should_receive('add_custom_log_levels')
  567. flexmock(module).should_receive('get_skip_actions').and_return(['create'])
  568. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  569. flexmock(borgmatic.actions.create).should_receive('run_create').never()
  570. tuple(
  571. module.run_actions(
  572. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'create': flexmock()},
  573. config_filename=flexmock(),
  574. config={'repositories': [], 'skip_actions': ['create']},
  575. config_paths=[],
  576. local_path=flexmock(),
  577. remote_path=flexmock(),
  578. local_borg_version=flexmock(),
  579. repository={'path': 'repo'},
  580. )
  581. )
  582. def test_run_actions_runs_prune():
  583. flexmock(module).should_receive('add_custom_log_levels')
  584. flexmock(module).should_receive('get_skip_actions').and_return([])
  585. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  586. flexmock(borgmatic.actions.prune).should_receive('run_prune').once()
  587. tuple(
  588. module.run_actions(
  589. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'prune': flexmock()},
  590. config_filename=flexmock(),
  591. config={'repositories': []},
  592. config_paths=[],
  593. local_path=flexmock(),
  594. remote_path=flexmock(),
  595. local_borg_version=flexmock(),
  596. repository={'path': 'repo'},
  597. )
  598. )
  599. def test_run_actions_with_skip_actions_skips_prune():
  600. flexmock(module).should_receive('add_custom_log_levels')
  601. flexmock(module).should_receive('get_skip_actions').and_return(['prune'])
  602. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  603. flexmock(borgmatic.actions.prune).should_receive('run_prune').never()
  604. tuple(
  605. module.run_actions(
  606. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'prune': flexmock()},
  607. config_filename=flexmock(),
  608. config={'repositories': [], 'skip_actions': ['prune']},
  609. config_paths=[],
  610. local_path=flexmock(),
  611. remote_path=flexmock(),
  612. local_borg_version=flexmock(),
  613. repository={'path': 'repo'},
  614. )
  615. )
  616. def test_run_actions_runs_compact():
  617. flexmock(module).should_receive('add_custom_log_levels')
  618. flexmock(module).should_receive('get_skip_actions').and_return([])
  619. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  620. flexmock(borgmatic.actions.compact).should_receive('run_compact').once()
  621. tuple(
  622. module.run_actions(
  623. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'compact': flexmock()},
  624. config_filename=flexmock(),
  625. config={'repositories': []},
  626. config_paths=[],
  627. local_path=flexmock(),
  628. remote_path=flexmock(),
  629. local_borg_version=flexmock(),
  630. repository={'path': 'repo'},
  631. )
  632. )
  633. def test_run_actions_with_skip_actions_skips_compact():
  634. flexmock(module).should_receive('add_custom_log_levels')
  635. flexmock(module).should_receive('get_skip_actions').and_return(['compact'])
  636. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  637. flexmock(borgmatic.actions.compact).should_receive('run_compact').never()
  638. tuple(
  639. module.run_actions(
  640. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'compact': flexmock()},
  641. config_filename=flexmock(),
  642. config={'repositories': [], 'skip_actions': ['compact']},
  643. config_paths=[],
  644. local_path=flexmock(),
  645. remote_path=flexmock(),
  646. local_borg_version=flexmock(),
  647. repository={'path': 'repo'},
  648. )
  649. )
  650. def test_run_actions_runs_check_when_repository_enabled_for_checks():
  651. flexmock(module).should_receive('add_custom_log_levels')
  652. flexmock(module).should_receive('get_skip_actions').and_return([])
  653. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  654. flexmock(module.checks).should_receive('repository_enabled_for_checks').and_return(True)
  655. flexmock(borgmatic.actions.check).should_receive('run_check').once()
  656. tuple(
  657. module.run_actions(
  658. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'check': flexmock()},
  659. config_filename=flexmock(),
  660. config={'repositories': []},
  661. config_paths=[],
  662. local_path=flexmock(),
  663. remote_path=flexmock(),
  664. local_borg_version=flexmock(),
  665. repository={'path': 'repo'},
  666. )
  667. )
  668. def test_run_actions_skips_check_when_repository_not_enabled_for_checks():
  669. flexmock(module).should_receive('add_custom_log_levels')
  670. flexmock(module).should_receive('get_skip_actions').and_return([])
  671. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  672. flexmock(module.checks).should_receive('repository_enabled_for_checks').and_return(False)
  673. flexmock(borgmatic.actions.check).should_receive('run_check').never()
  674. tuple(
  675. module.run_actions(
  676. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'check': flexmock()},
  677. config_filename=flexmock(),
  678. config={'repositories': []},
  679. config_paths=[],
  680. local_path=flexmock(),
  681. remote_path=flexmock(),
  682. local_borg_version=flexmock(),
  683. repository={'path': 'repo'},
  684. )
  685. )
  686. def test_run_actions_with_skip_actions_skips_check():
  687. flexmock(module).should_receive('add_custom_log_levels')
  688. flexmock(module).should_receive('get_skip_actions').and_return(['check'])
  689. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  690. flexmock(module.checks).should_receive('repository_enabled_for_checks').and_return(True)
  691. flexmock(borgmatic.actions.check).should_receive('run_check').never()
  692. tuple(
  693. module.run_actions(
  694. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'check': flexmock()},
  695. config_filename=flexmock(),
  696. config={'repositories': [], 'skip_actions': ['check']},
  697. config_paths=[],
  698. local_path=flexmock(),
  699. remote_path=flexmock(),
  700. local_borg_version=flexmock(),
  701. repository={'path': 'repo'},
  702. )
  703. )
  704. def test_run_actions_runs_extract():
  705. flexmock(module).should_receive('add_custom_log_levels')
  706. flexmock(module).should_receive('get_skip_actions').and_return([])
  707. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  708. flexmock(borgmatic.actions.extract).should_receive('run_extract').once()
  709. tuple(
  710. module.run_actions(
  711. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'extract': flexmock()},
  712. config_filename=flexmock(),
  713. config={'repositories': []},
  714. config_paths=[],
  715. local_path=flexmock(),
  716. remote_path=flexmock(),
  717. local_borg_version=flexmock(),
  718. repository={'path': 'repo'},
  719. )
  720. )
  721. def test_run_actions_runs_export_tar():
  722. flexmock(module).should_receive('add_custom_log_levels')
  723. flexmock(module).should_receive('get_skip_actions').and_return([])
  724. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  725. flexmock(borgmatic.actions.export_tar).should_receive('run_export_tar').once()
  726. tuple(
  727. module.run_actions(
  728. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'export-tar': flexmock()},
  729. config_filename=flexmock(),
  730. config={'repositories': []},
  731. config_paths=[],
  732. local_path=flexmock(),
  733. remote_path=flexmock(),
  734. local_borg_version=flexmock(),
  735. repository={'path': 'repo'},
  736. )
  737. )
  738. def test_run_actions_runs_mount():
  739. flexmock(module).should_receive('add_custom_log_levels')
  740. flexmock(module).should_receive('get_skip_actions').and_return([])
  741. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  742. flexmock(borgmatic.actions.mount).should_receive('run_mount').once()
  743. tuple(
  744. module.run_actions(
  745. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'mount': flexmock()},
  746. config_filename=flexmock(),
  747. config={'repositories': []},
  748. config_paths=[],
  749. local_path=flexmock(),
  750. remote_path=flexmock(),
  751. local_borg_version=flexmock(),
  752. repository={'path': 'repo'},
  753. )
  754. )
  755. def test_run_actions_runs_restore():
  756. flexmock(module).should_receive('add_custom_log_levels')
  757. flexmock(module).should_receive('get_skip_actions').and_return([])
  758. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  759. flexmock(borgmatic.actions.restore).should_receive('run_restore').once()
  760. tuple(
  761. module.run_actions(
  762. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'restore': flexmock()},
  763. config_filename=flexmock(),
  764. config={'repositories': []},
  765. config_paths=[],
  766. local_path=flexmock(),
  767. remote_path=flexmock(),
  768. local_borg_version=flexmock(),
  769. repository={'path': 'repo'},
  770. )
  771. )
  772. def test_run_actions_runs_repo_list():
  773. flexmock(module).should_receive('add_custom_log_levels')
  774. flexmock(module).should_receive('get_skip_actions').and_return([])
  775. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  776. expected = flexmock()
  777. flexmock(borgmatic.actions.repo_list).should_receive('run_repo_list').and_yield(expected).once()
  778. result = tuple(
  779. module.run_actions(
  780. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'repo-list': flexmock()},
  781. config_filename=flexmock(),
  782. config={'repositories': []},
  783. config_paths=[],
  784. local_path=flexmock(),
  785. remote_path=flexmock(),
  786. local_borg_version=flexmock(),
  787. repository={'path': 'repo'},
  788. )
  789. )
  790. assert result == (expected,)
  791. def test_run_actions_runs_list():
  792. flexmock(module).should_receive('add_custom_log_levels')
  793. flexmock(module).should_receive('get_skip_actions').and_return([])
  794. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  795. expected = flexmock()
  796. flexmock(borgmatic.actions.list).should_receive('run_list').and_yield(expected).once()
  797. result = tuple(
  798. module.run_actions(
  799. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'list': flexmock()},
  800. config_filename=flexmock(),
  801. config={'repositories': []},
  802. config_paths=[],
  803. local_path=flexmock(),
  804. remote_path=flexmock(),
  805. local_borg_version=flexmock(),
  806. repository={'path': 'repo'},
  807. )
  808. )
  809. assert result == (expected,)
  810. def test_run_actions_runs_repo_info():
  811. flexmock(module).should_receive('add_custom_log_levels')
  812. flexmock(module).should_receive('get_skip_actions').and_return([])
  813. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  814. expected = flexmock()
  815. flexmock(borgmatic.actions.repo_info).should_receive('run_repo_info').and_yield(expected).once()
  816. result = tuple(
  817. module.run_actions(
  818. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'repo-info': flexmock()},
  819. config_filename=flexmock(),
  820. config={'repositories': []},
  821. config_paths=[],
  822. local_path=flexmock(),
  823. remote_path=flexmock(),
  824. local_borg_version=flexmock(),
  825. repository={'path': 'repo'},
  826. )
  827. )
  828. assert result == (expected,)
  829. def test_run_actions_runs_info():
  830. flexmock(module).should_receive('add_custom_log_levels')
  831. flexmock(module).should_receive('get_skip_actions').and_return([])
  832. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  833. expected = flexmock()
  834. flexmock(borgmatic.actions.info).should_receive('run_info').and_yield(expected).once()
  835. result = tuple(
  836. module.run_actions(
  837. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'info': flexmock()},
  838. config_filename=flexmock(),
  839. config={'repositories': []},
  840. config_paths=[],
  841. local_path=flexmock(),
  842. remote_path=flexmock(),
  843. local_borg_version=flexmock(),
  844. repository={'path': 'repo'},
  845. )
  846. )
  847. assert result == (expected,)
  848. def test_run_actions_runs_break_lock():
  849. flexmock(module).should_receive('add_custom_log_levels')
  850. flexmock(module).should_receive('get_skip_actions').and_return([])
  851. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  852. flexmock(borgmatic.actions.break_lock).should_receive('run_break_lock').once()
  853. tuple(
  854. module.run_actions(
  855. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'break-lock': flexmock()},
  856. config_filename=flexmock(),
  857. config={'repositories': []},
  858. config_paths=[],
  859. local_path=flexmock(),
  860. remote_path=flexmock(),
  861. local_borg_version=flexmock(),
  862. repository={'path': 'repo'},
  863. )
  864. )
  865. def test_run_actions_runs_export_key():
  866. flexmock(module).should_receive('add_custom_log_levels')
  867. flexmock(module).should_receive('get_skip_actions').and_return([])
  868. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  869. flexmock(borgmatic.actions.export_key).should_receive('run_export_key').once()
  870. tuple(
  871. module.run_actions(
  872. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'export': flexmock()},
  873. config_filename=flexmock(),
  874. config={'repositories': []},
  875. config_paths=[],
  876. local_path=flexmock(),
  877. remote_path=flexmock(),
  878. local_borg_version=flexmock(),
  879. repository={'path': 'repo'},
  880. )
  881. )
  882. def test_run_actions_runs_change_passphrase():
  883. flexmock(module).should_receive('add_custom_log_levels')
  884. flexmock(module).should_receive('get_skip_actions').and_return([])
  885. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  886. flexmock(borgmatic.actions.change_passphrase).should_receive('run_change_passphrase').once()
  887. tuple(
  888. module.run_actions(
  889. arguments={
  890. 'global': flexmock(dry_run=False, log_file='foo'),
  891. 'change-passphrase': flexmock(),
  892. },
  893. config_filename=flexmock(),
  894. config={'repositories': []},
  895. config_paths=[],
  896. local_path=flexmock(),
  897. remote_path=flexmock(),
  898. local_borg_version=flexmock(),
  899. repository={'path': 'repo'},
  900. )
  901. )
  902. def test_run_actions_runs_delete():
  903. flexmock(module).should_receive('add_custom_log_levels')
  904. flexmock(module).should_receive('get_skip_actions').and_return([])
  905. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  906. flexmock(borgmatic.actions.delete).should_receive('run_delete').once()
  907. tuple(
  908. module.run_actions(
  909. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'delete': flexmock()},
  910. config_filename=flexmock(),
  911. config={'repositories': []},
  912. config_paths=[],
  913. local_path=flexmock(),
  914. remote_path=flexmock(),
  915. local_borg_version=flexmock(),
  916. repository={'path': 'repo'},
  917. )
  918. )
  919. def test_run_actions_runs_repo_delete():
  920. flexmock(module).should_receive('add_custom_log_levels')
  921. flexmock(module).should_receive('get_skip_actions').and_return([])
  922. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  923. flexmock(borgmatic.actions.repo_delete).should_receive('run_repo_delete').once()
  924. tuple(
  925. module.run_actions(
  926. arguments={
  927. 'global': flexmock(dry_run=False, log_file='foo'),
  928. 'repo-delete': flexmock(),
  929. },
  930. config_filename=flexmock(),
  931. config={'repositories': []},
  932. config_paths=[],
  933. local_path=flexmock(),
  934. remote_path=flexmock(),
  935. local_borg_version=flexmock(),
  936. repository={'path': 'repo'},
  937. )
  938. )
  939. def test_run_actions_runs_borg():
  940. flexmock(module).should_receive('add_custom_log_levels')
  941. flexmock(module).should_receive('get_skip_actions').and_return([])
  942. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  943. flexmock(borgmatic.actions.borg).should_receive('run_borg').once()
  944. tuple(
  945. module.run_actions(
  946. arguments={'global': flexmock(dry_run=False, log_file='foo'), 'borg': flexmock()},
  947. config_filename=flexmock(),
  948. config={'repositories': []},
  949. config_paths=[],
  950. local_path=flexmock(),
  951. remote_path=flexmock(),
  952. local_borg_version=flexmock(),
  953. repository={'path': 'repo'},
  954. )
  955. )
  956. def test_run_actions_runs_multiple_actions_in_argument_order():
  957. flexmock(module).should_receive('add_custom_log_levels')
  958. flexmock(module).should_receive('get_skip_actions').and_return([])
  959. flexmock(module.command).should_receive('Before_after_hooks').and_return(flexmock())
  960. flexmock(borgmatic.actions.borg).should_receive('run_borg').once().ordered()
  961. flexmock(borgmatic.actions.restore).should_receive('run_restore').once().ordered()
  962. tuple(
  963. module.run_actions(
  964. arguments={
  965. 'global': flexmock(dry_run=False, log_file='foo'),
  966. 'borg': flexmock(),
  967. 'restore': flexmock(),
  968. },
  969. config_filename=flexmock(),
  970. config={'repositories': []},
  971. config_paths=[],
  972. local_path=flexmock(),
  973. remote_path=flexmock(),
  974. local_borg_version=flexmock(),
  975. repository={'path': 'repo'},
  976. )
  977. )
  978. @pytest.mark.parametrize(
  979. 'resolve_env',
  980. ((True, False),),
  981. )
  982. def test_load_configurations_collects_parsed_configurations_and_logs(resolve_env):
  983. configuration = flexmock()
  984. other_configuration = flexmock()
  985. test_expected_logs = [flexmock(), flexmock()]
  986. other_expected_logs = [flexmock(), flexmock()]
  987. flexmock(module.validate).should_receive('parse_configuration').and_return(
  988. configuration, ['/tmp/test.yaml'], test_expected_logs
  989. ).and_return(other_configuration, ['/tmp/other.yaml'], other_expected_logs)
  990. configs, config_paths, logs = tuple(
  991. module.load_configurations(
  992. ('test.yaml', 'other.yaml'),
  993. resolve_env=resolve_env,
  994. )
  995. )
  996. assert configs == {'test.yaml': configuration, 'other.yaml': other_configuration}
  997. assert config_paths == ['/tmp/other.yaml', '/tmp/test.yaml']
  998. assert set(logs) >= set(test_expected_logs + other_expected_logs)
  999. def test_load_configurations_logs_warning_for_permission_error():
  1000. flexmock(module.validate).should_receive('parse_configuration').and_raise(PermissionError)
  1001. configs, config_paths, logs = tuple(module.load_configurations(('test.yaml',)))
  1002. assert configs == {}
  1003. assert config_paths == []
  1004. assert max(log.levelno for log in logs) == logging.WARNING
  1005. def test_load_configurations_logs_critical_for_parse_error():
  1006. flexmock(module.validate).should_receive('parse_configuration').and_raise(ValueError)
  1007. configs, config_paths, logs = tuple(module.load_configurations(('test.yaml',)))
  1008. assert configs == {}
  1009. assert config_paths == []
  1010. assert max(log.levelno for log in logs) == logging.CRITICAL
  1011. def test_log_record_does_not_raise():
  1012. module.log_record(levelno=1, foo='bar', baz='quux')
  1013. def test_log_record_with_suppress_does_not_raise():
  1014. module.log_record(levelno=1, foo='bar', baz='quux', suppress_log=True)
  1015. def test_log_error_records_generates_output_logs_for_message_only():
  1016. flexmock(module).should_receive('log_record').replace_with(dict).once()
  1017. logs = tuple(module.log_error_records('Error'))
  1018. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  1019. def test_log_error_records_generates_output_logs_for_called_process_error_with_bytes_ouput():
  1020. flexmock(module).should_receive('log_record').replace_with(dict).times(3)
  1021. flexmock(module.logger).should_receive('getEffectiveLevel').and_return(logging.WARNING)
  1022. logs = tuple(
  1023. module.log_error_records('Error', subprocess.CalledProcessError(1, 'ls', b'error output'))
  1024. )
  1025. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  1026. assert any(log for log in logs if 'error output' in str(log))
  1027. def test_log_error_records_generates_output_logs_for_called_process_error_with_string_ouput():
  1028. flexmock(module).should_receive('log_record').replace_with(dict).times(3)
  1029. flexmock(module.logger).should_receive('getEffectiveLevel').and_return(logging.WARNING)
  1030. logs = tuple(
  1031. module.log_error_records('Error', subprocess.CalledProcessError(1, 'ls', 'error output'))
  1032. )
  1033. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  1034. assert any(log for log in logs if 'error output' in str(log))
  1035. def test_log_error_records_generates_work_around_output_logs_for_called_process_error_with_repository_access_aborted_exit_code():
  1036. flexmock(module).should_receive('log_record').replace_with(dict).times(4)
  1037. flexmock(module.logger).should_receive('getEffectiveLevel').and_return(logging.WARNING)
  1038. logs = tuple(
  1039. module.log_error_records(
  1040. 'Error',
  1041. subprocess.CalledProcessError(
  1042. module.BORG_REPOSITORY_ACCESS_ABORTED_EXIT_CODE, 'ls', 'error output'
  1043. ),
  1044. )
  1045. )
  1046. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  1047. assert any(log for log in logs if 'error output' in str(log))
  1048. assert any(log for log in logs if 'To work around this' in str(log))
  1049. def test_log_error_records_splits_called_process_error_with_multiline_ouput_into_multiple_logs():
  1050. flexmock(module).should_receive('log_record').replace_with(dict).times(4)
  1051. flexmock(module.logger).should_receive('getEffectiveLevel').and_return(logging.WARNING)
  1052. logs = tuple(
  1053. module.log_error_records(
  1054. 'Error', subprocess.CalledProcessError(1, 'ls', 'error output\nanother line')
  1055. )
  1056. )
  1057. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  1058. assert any(log for log in logs if 'error output' in str(log))
  1059. def test_log_error_records_generates_logs_for_value_error():
  1060. flexmock(module).should_receive('log_record').replace_with(dict).twice()
  1061. logs = tuple(module.log_error_records('Error', ValueError()))
  1062. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  1063. def test_log_error_records_generates_logs_for_os_error():
  1064. flexmock(module).should_receive('log_record').replace_with(dict).twice()
  1065. logs = tuple(module.log_error_records('Error', OSError()))
  1066. assert {log['levelno'] for log in logs} == {logging.CRITICAL}
  1067. def test_log_error_records_generates_nothing_for_other_error():
  1068. flexmock(module).should_receive('log_record').never()
  1069. logs = tuple(module.log_error_records('Error', KeyError()))
  1070. assert logs == ()
  1071. def test_get_local_path_uses_configuration_value():
  1072. assert module.get_local_path({'test.yaml': {'local_path': 'borg1'}}) == 'borg1'
  1073. def test_get_local_path_without_local_path_defaults_to_borg():
  1074. assert module.get_local_path({'test.yaml': {}}) == 'borg'
  1075. def test_collect_highlander_action_summary_logs_info_for_success_with_bootstrap():
  1076. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  1077. flexmock(module.borgmatic.actions.config.bootstrap).should_receive('run_bootstrap')
  1078. arguments = {
  1079. 'bootstrap': flexmock(repository='repo', local_path='borg7'),
  1080. 'global': flexmock(dry_run=False),
  1081. }
  1082. logs = tuple(
  1083. module.collect_highlander_action_summary_logs(
  1084. {'test.yaml': {}}, arguments=arguments, configuration_parse_errors=False
  1085. )
  1086. )
  1087. assert {log.levelno for log in logs} == {logging.ANSWER}
  1088. def test_collect_highlander_action_summary_logs_error_on_bootstrap_failure():
  1089. flexmock(module.borg_version).should_receive('local_borg_version').and_return(flexmock())
  1090. flexmock(module.borgmatic.actions.config.bootstrap).should_receive('run_bootstrap').and_raise(
  1091. ValueError
  1092. )
  1093. arguments = {
  1094. 'bootstrap': flexmock(repository='repo', local_path='borg7'),
  1095. 'global': flexmock(dry_run=False),
  1096. }
  1097. logs = tuple(
  1098. module.collect_highlander_action_summary_logs(
  1099. {'test.yaml': {}}, arguments=arguments, configuration_parse_errors=False
  1100. )
  1101. )
  1102. assert {log.levelno for log in logs} == {logging.CRITICAL}
  1103. def test_collect_highlander_action_summary_logs_error_on_bootstrap_local_borg_version_failure():
  1104. flexmock(module.borg_version).should_receive('local_borg_version').and_raise(ValueError)
  1105. flexmock(module.borgmatic.actions.config.bootstrap).should_receive('run_bootstrap').never()
  1106. arguments = {
  1107. 'bootstrap': flexmock(repository='repo', local_path='borg7'),
  1108. 'global': flexmock(dry_run=False),
  1109. }
  1110. logs = tuple(
  1111. module.collect_highlander_action_summary_logs(
  1112. {'test.yaml': {}}, arguments=arguments, configuration_parse_errors=False
  1113. )
  1114. )
  1115. assert {log.levelno for log in logs} == {logging.CRITICAL}
  1116. def test_collect_highlander_action_summary_logs_info_for_success_with_generate():
  1117. flexmock(module.borgmatic.actions.config.generate).should_receive('run_generate')
  1118. arguments = {
  1119. 'generate': flexmock(destination='test.yaml'),
  1120. 'global': flexmock(dry_run=False),
  1121. }
  1122. logs = tuple(
  1123. module.collect_highlander_action_summary_logs(
  1124. {'test.yaml': {}}, arguments=arguments, configuration_parse_errors=False
  1125. )
  1126. )
  1127. assert {log.levelno for log in logs} == {logging.ANSWER}
  1128. def test_collect_highlander_action_summary_logs_error_on_generate_failure():
  1129. flexmock(module.borgmatic.actions.config.generate).should_receive('run_generate').and_raise(
  1130. ValueError
  1131. )
  1132. arguments = {
  1133. 'generate': flexmock(destination='test.yaml'),
  1134. 'global': flexmock(dry_run=False),
  1135. }
  1136. logs = tuple(
  1137. module.collect_highlander_action_summary_logs(
  1138. {'test.yaml': {}}, arguments=arguments, configuration_parse_errors=False
  1139. )
  1140. )
  1141. assert {log.levelno for log in logs} == {logging.CRITICAL}
  1142. def test_collect_highlander_action_summary_logs_info_for_success_with_validate():
  1143. flexmock(module.borgmatic.actions.config.validate).should_receive('run_validate')
  1144. arguments = {
  1145. 'validate': flexmock(),
  1146. 'global': flexmock(dry_run=False),
  1147. }
  1148. logs = tuple(
  1149. module.collect_highlander_action_summary_logs(
  1150. {'test.yaml': {}}, arguments=arguments, configuration_parse_errors=False
  1151. )
  1152. )
  1153. assert {log.levelno for log in logs} == {logging.ANSWER}
  1154. def test_collect_highlander_action_summary_logs_error_on_validate_parse_failure():
  1155. flexmock(module.borgmatic.actions.config.validate).should_receive('run_validate')
  1156. arguments = {
  1157. 'validate': flexmock(),
  1158. 'global': flexmock(dry_run=False),
  1159. }
  1160. logs = tuple(
  1161. module.collect_highlander_action_summary_logs(
  1162. {'test.yaml': {}}, arguments=arguments, configuration_parse_errors=True
  1163. )
  1164. )
  1165. assert {log.levelno for log in logs} == {logging.CRITICAL}
  1166. def test_collect_highlander_action_summary_logs_error_on_run_validate_failure():
  1167. flexmock(module.borgmatic.actions.config.validate).should_receive('run_validate').and_raise(
  1168. ValueError
  1169. )
  1170. arguments = {
  1171. 'validate': flexmock(),
  1172. 'global': flexmock(dry_run=False),
  1173. }
  1174. logs = tuple(
  1175. module.collect_highlander_action_summary_logs(
  1176. {'test.yaml': {}}, arguments=arguments, configuration_parse_errors=False
  1177. )
  1178. )
  1179. assert {log.levelno for log in logs} == {logging.CRITICAL}
  1180. def test_collect_configuration_run_summary_logs_info_for_success():
  1181. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1182. flexmock(module.command).should_receive('filter_hooks')
  1183. flexmock(module.command).should_receive('execute_hooks')
  1184. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  1185. flexmock(module).should_receive('run_configuration').and_return([])
  1186. arguments = {'global': flexmock(dry_run=False)}
  1187. logs = tuple(
  1188. module.collect_configuration_run_summary_logs(
  1189. {'test.yaml': {}}, config_paths=['/tmp/test.yaml'], arguments=arguments
  1190. )
  1191. )
  1192. assert {log.levelno for log in logs} == {logging.INFO}
  1193. def test_collect_configuration_run_summary_executes_hooks_for_create():
  1194. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1195. flexmock(module.command).should_receive('filter_hooks')
  1196. flexmock(module.command).should_receive('execute_hooks')
  1197. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  1198. flexmock(module).should_receive('run_configuration').and_return([])
  1199. arguments = {'create': flexmock(), 'global': flexmock(monitoring_verbosity=1, dry_run=False)}
  1200. logs = tuple(
  1201. module.collect_configuration_run_summary_logs(
  1202. {'test.yaml': {}}, config_paths=['/tmp/test.yaml'], arguments=arguments
  1203. )
  1204. )
  1205. assert {log.levelno for log in logs} == {logging.INFO}
  1206. def test_collect_configuration_run_summary_logs_info_for_success_with_extract():
  1207. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1208. flexmock(module.command).should_receive('filter_hooks')
  1209. flexmock(module.command).should_receive('execute_hooks')
  1210. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  1211. flexmock(module).should_receive('run_configuration').and_return([])
  1212. arguments = {'extract': flexmock(repository='repo'), 'global': flexmock(dry_run=False)}
  1213. logs = tuple(
  1214. module.collect_configuration_run_summary_logs(
  1215. {'test.yaml': {}}, config_paths=['/tmp/test.yaml'], arguments=arguments
  1216. )
  1217. )
  1218. assert {log.levelno for log in logs} == {logging.INFO}
  1219. def test_collect_configuration_run_summary_logs_extract_with_repository_error():
  1220. flexmock(module.validate).should_receive('guard_configuration_contains_repository').and_raise(
  1221. ValueError
  1222. )
  1223. expected_logs = (flexmock(),)
  1224. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  1225. arguments = {'extract': flexmock(repository='repo')}
  1226. logs = tuple(
  1227. module.collect_configuration_run_summary_logs(
  1228. {'test.yaml': {}}, config_paths=['/tmp/test.yaml'], arguments=arguments
  1229. )
  1230. )
  1231. assert logs == expected_logs
  1232. def test_collect_configuration_run_summary_logs_info_for_success_with_mount():
  1233. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1234. flexmock(module.command).should_receive('filter_hooks')
  1235. flexmock(module.command).should_receive('execute_hooks')
  1236. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  1237. flexmock(module).should_receive('run_configuration').and_return([])
  1238. arguments = {'mount': flexmock(repository='repo'), 'global': flexmock(dry_run=False)}
  1239. logs = tuple(
  1240. module.collect_configuration_run_summary_logs(
  1241. {'test.yaml': {}}, config_paths=['/tmp/test.yaml'], arguments=arguments
  1242. )
  1243. )
  1244. assert {log.levelno for log in logs} == {logging.INFO}
  1245. def test_collect_configuration_run_summary_logs_mount_with_repository_error():
  1246. flexmock(module.validate).should_receive('guard_configuration_contains_repository').and_raise(
  1247. ValueError
  1248. )
  1249. expected_logs = (flexmock(),)
  1250. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  1251. arguments = {'mount': flexmock(repository='repo'), 'global': flexmock(dry_run=False)}
  1252. logs = tuple(
  1253. module.collect_configuration_run_summary_logs(
  1254. {'test.yaml': {}}, config_paths=['/tmp/test.yaml'], arguments=arguments
  1255. )
  1256. )
  1257. assert logs == expected_logs
  1258. def test_collect_configuration_run_summary_logs_missing_configs_error():
  1259. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1260. flexmock(module.command).should_receive('filter_hooks')
  1261. flexmock(module.command).should_receive('execute_hooks')
  1262. arguments = {'global': flexmock(config_paths=[])}
  1263. expected_logs = (flexmock(),)
  1264. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  1265. logs = tuple(
  1266. module.collect_configuration_run_summary_logs({}, config_paths=[], arguments=arguments)
  1267. )
  1268. assert logs == expected_logs
  1269. def test_collect_configuration_run_summary_logs_pre_hook_error():
  1270. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1271. flexmock(module.command).should_receive('filter_hooks')
  1272. flexmock(module.command).should_receive('execute_hooks').and_raise(ValueError)
  1273. expected_logs = (flexmock(),)
  1274. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  1275. arguments = {'create': flexmock(), 'global': flexmock(monitoring_verbosity=1, dry_run=False)}
  1276. logs = tuple(
  1277. module.collect_configuration_run_summary_logs(
  1278. {'test.yaml': {}}, config_paths=['/tmp/test.yaml'], arguments=arguments
  1279. )
  1280. )
  1281. assert logs == expected_logs
  1282. def test_collect_configuration_run_summary_logs_post_hook_error():
  1283. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1284. flexmock(module.command).should_receive('filter_hooks')
  1285. flexmock(module.command).should_receive('execute_hooks').and_return(None).and_raise(ValueError)
  1286. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  1287. flexmock(module).should_receive('run_configuration').and_return([])
  1288. expected_logs = (flexmock(),)
  1289. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  1290. arguments = {'create': flexmock(), 'global': flexmock(monitoring_verbosity=1, dry_run=False)}
  1291. logs = tuple(
  1292. module.collect_configuration_run_summary_logs(
  1293. {'test.yaml': {}}, config_paths=['/tmp/test.yaml'], arguments=arguments
  1294. )
  1295. )
  1296. assert expected_logs[0] in logs
  1297. def test_collect_configuration_run_summary_logs_for_list_with_archive_and_repository_error():
  1298. flexmock(module.validate).should_receive('guard_configuration_contains_repository').and_raise(
  1299. ValueError
  1300. )
  1301. expected_logs = (flexmock(),)
  1302. flexmock(module).should_receive('log_error_records').and_return(expected_logs)
  1303. arguments = {
  1304. 'list': flexmock(repository='repo', archive='test'),
  1305. 'global': flexmock(dry_run=False),
  1306. }
  1307. logs = tuple(
  1308. module.collect_configuration_run_summary_logs(
  1309. {'test.yaml': {}}, config_paths=['/tmp/test.yaml'], arguments=arguments
  1310. )
  1311. )
  1312. assert logs == expected_logs
  1313. def test_collect_configuration_run_summary_logs_info_for_success_with_list():
  1314. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1315. flexmock(module.command).should_receive('filter_hooks')
  1316. flexmock(module.command).should_receive('execute_hooks')
  1317. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  1318. flexmock(module).should_receive('run_configuration').and_return([])
  1319. arguments = {
  1320. 'list': flexmock(repository='repo', archive=None),
  1321. 'global': flexmock(dry_run=False),
  1322. }
  1323. logs = tuple(
  1324. module.collect_configuration_run_summary_logs(
  1325. {'test.yaml': {}}, config_paths=['/tmp/test.yaml'], arguments=arguments
  1326. )
  1327. )
  1328. assert {log.levelno for log in logs} == {logging.INFO}
  1329. def test_collect_configuration_run_summary_logs_run_configuration_error():
  1330. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1331. flexmock(module.command).should_receive('filter_hooks')
  1332. flexmock(module.command).should_receive('execute_hooks')
  1333. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  1334. flexmock(module).should_receive('run_configuration').and_return(
  1335. [logging.makeLogRecord(dict(levelno=logging.CRITICAL, levelname='CRITICAL', msg='Error'))]
  1336. )
  1337. flexmock(module).should_receive('log_error_records').and_return([])
  1338. arguments = {'global': flexmock(dry_run=False)}
  1339. logs = tuple(
  1340. module.collect_configuration_run_summary_logs(
  1341. {'test.yaml': {}}, config_paths=['/tmp/test.yaml'], arguments=arguments
  1342. )
  1343. )
  1344. assert {log.levelno for log in logs} == {logging.CRITICAL}
  1345. def test_collect_configuration_run_summary_logs_run_umount_error():
  1346. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1347. flexmock(module.command).should_receive('filter_hooks')
  1348. flexmock(module.command).should_receive('execute_hooks')
  1349. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  1350. flexmock(module).should_receive('run_configuration').and_return([])
  1351. flexmock(module.borg_umount).should_receive('unmount_archive').and_raise(OSError)
  1352. flexmock(module).should_receive('log_error_records').and_return(
  1353. [logging.makeLogRecord(dict(levelno=logging.CRITICAL, levelname='CRITICAL', msg='Error'))]
  1354. )
  1355. arguments = {'umount': flexmock(mount_point='/mnt'), 'global': flexmock(dry_run=False)}
  1356. logs = tuple(
  1357. module.collect_configuration_run_summary_logs(
  1358. {'test.yaml': {}}, config_paths=['/tmp/test.yaml'], arguments=arguments
  1359. )
  1360. )
  1361. assert {log.levelno for log in logs} == {logging.INFO, logging.CRITICAL}
  1362. def test_collect_configuration_run_summary_logs_outputs_merged_json_results():
  1363. flexmock(module.validate).should_receive('guard_configuration_contains_repository')
  1364. flexmock(module.command).should_receive('filter_hooks')
  1365. flexmock(module.command).should_receive('execute_hooks')
  1366. flexmock(module).should_receive('Log_prefix').and_return(flexmock())
  1367. flexmock(module).should_receive('run_configuration').and_return(['foo', 'bar']).and_return(
  1368. ['baz']
  1369. )
  1370. stdout = flexmock()
  1371. stdout.should_receive('write').with_args('["foo", "bar", "baz"]').once()
  1372. flexmock(module.sys).stdout = stdout
  1373. arguments = {'global': flexmock(dry_run=False)}
  1374. tuple(
  1375. module.collect_configuration_run_summary_logs(
  1376. {'test.yaml': {}, 'test2.yaml': {}},
  1377. config_paths=['/tmp/test.yaml', '/tmp/test2.yaml'],
  1378. arguments=arguments,
  1379. )
  1380. )