test_create.py 61 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478
  1. import logging
  2. import pytest
  3. from flexmock import flexmock
  4. from borgmatic.borg import create as module
  5. from ..test_verbosity import insert_logging_mock
  6. def test_expand_directory_with_basic_path_passes_it_through():
  7. flexmock(module.os.path).should_receive('expanduser').and_return('foo')
  8. flexmock(module.glob).should_receive('glob').and_return([])
  9. paths = module._expand_directory('foo')
  10. assert paths == ['foo']
  11. def test_expand_directory_with_glob_expands():
  12. flexmock(module.os.path).should_receive('expanduser').and_return('foo*')
  13. flexmock(module.glob).should_receive('glob').and_return(['foo', 'food'])
  14. paths = module._expand_directory('foo*')
  15. assert paths == ['foo', 'food']
  16. def test_expand_directories_flattens_expanded_directories():
  17. flexmock(module).should_receive('_expand_directory').with_args('~/foo').and_return(
  18. ['/root/foo']
  19. )
  20. flexmock(module).should_receive('_expand_directory').with_args('bar*').and_return(
  21. ['bar', 'barf']
  22. )
  23. paths = module._expand_directories(('~/foo', 'bar*'))
  24. assert paths == ('/root/foo', 'bar', 'barf')
  25. def test_expand_directories_considers_none_as_no_directories():
  26. paths = module._expand_directories(None)
  27. assert paths == ()
  28. def test_expand_home_directories_expands_tildes():
  29. flexmock(module.os.path).should_receive('expanduser').with_args('~/bar').and_return('/foo/bar')
  30. flexmock(module.os.path).should_receive('expanduser').with_args('baz').and_return('baz')
  31. paths = module._expand_home_directories(('~/bar', 'baz'))
  32. assert paths == ('/foo/bar', 'baz')
  33. def test_expand_home_directories_considers_none_as_no_directories():
  34. paths = module._expand_home_directories(None)
  35. assert paths == ()
  36. def test_map_directories_to_devices_gives_device_id_per_path():
  37. flexmock(module.os).should_receive('stat').with_args('/foo').and_return(flexmock(st_dev=55))
  38. flexmock(module.os).should_receive('stat').with_args('/bar').and_return(flexmock(st_dev=66))
  39. device_map = module.map_directories_to_devices(('/foo', '/bar'))
  40. assert device_map == {
  41. '/foo': 55,
  42. '/bar': 66,
  43. }
  44. def test_map_directories_to_devices_with_missing_path_does_not_error():
  45. flexmock(module.os).should_receive('stat').with_args('/foo').and_return(flexmock(st_dev=55))
  46. flexmock(module.os).should_receive('stat').with_args('/bar').and_raise(FileNotFoundError)
  47. device_map = module.map_directories_to_devices(('/foo', '/bar'))
  48. assert device_map == {
  49. '/foo': 55,
  50. '/bar': None,
  51. }
  52. @pytest.mark.parametrize(
  53. 'directories,expected_directories',
  54. (
  55. ({'/': 1, '/root': 1}, ('/',)),
  56. ({'/': 1, '/root/': 1}, ('/',)),
  57. ({'/': 1, '/root': 2}, ('/', '/root')),
  58. ({'/root': 1, '/': 1}, ('/',)),
  59. ({'/root': 1, '/root/foo': 1}, ('/root',)),
  60. ({'/root/': 1, '/root/foo': 1}, ('/root/',)),
  61. ({'/root': 1, '/root/foo/': 1}, ('/root',)),
  62. ({'/root': 1, '/root/foo': 2}, ('/root', '/root/foo')),
  63. ({'/root/foo': 1, '/root': 1}, ('/root',)),
  64. ({'/root': None, '/root/foo': None}, ('/root', '/root/foo')),
  65. ({'/root': 1, '/etc': 1, '/root/foo/bar': 1}, ('/etc', '/root')),
  66. ({'/root': 1, '/root/foo': 1, '/root/foo/bar': 1}, ('/root',)),
  67. ({'/dup': 1, '/dup': 1}, ('/dup',)),
  68. ({'/foo': 1, '/bar': 1}, ('/bar', '/foo')),
  69. ({'/foo': 1, '/bar': 2}, ('/bar', '/foo')),
  70. ),
  71. )
  72. def test_deduplicate_directories_removes_child_paths_on_the_same_filesystem(
  73. directories, expected_directories
  74. ):
  75. assert module.deduplicate_directories(directories) == expected_directories
  76. def test_write_pattern_file_does_not_raise():
  77. temporary_file = flexmock(name='filename', write=lambda mode: None, flush=lambda: None)
  78. flexmock(module.tempfile).should_receive('NamedTemporaryFile').and_return(temporary_file)
  79. module._write_pattern_file(['exclude'])
  80. def test_write_pattern_file_with_empty_exclude_patterns_does_not_raise():
  81. module._write_pattern_file([])
  82. def test_make_pattern_flags_includes_pattern_filename_when_given():
  83. pattern_flags = module._make_pattern_flags(
  84. location_config={'patterns': ['R /', '- /var']}, pattern_filename='/tmp/patterns'
  85. )
  86. assert pattern_flags == ('--patterns-from', '/tmp/patterns')
  87. def test_make_pattern_flags_includes_patterns_from_filenames_when_in_config():
  88. pattern_flags = module._make_pattern_flags(
  89. location_config={'patterns_from': ['patterns', 'other']}
  90. )
  91. assert pattern_flags == ('--patterns-from', 'patterns', '--patterns-from', 'other')
  92. def test_make_pattern_flags_includes_both_filenames_when_patterns_given_and_patterns_from_in_config():
  93. pattern_flags = module._make_pattern_flags(
  94. location_config={'patterns_from': ['patterns']}, pattern_filename='/tmp/patterns'
  95. )
  96. assert pattern_flags == ('--patterns-from', 'patterns', '--patterns-from', '/tmp/patterns')
  97. def test_make_pattern_flags_considers_none_patterns_from_filenames_as_empty():
  98. pattern_flags = module._make_pattern_flags(location_config={'patterns_from': None})
  99. assert pattern_flags == ()
  100. def test_make_exclude_flags_includes_exclude_patterns_filename_when_given():
  101. exclude_flags = module._make_exclude_flags(
  102. location_config={'exclude_patterns': ['*.pyc', '/var']}, exclude_filename='/tmp/excludes'
  103. )
  104. assert exclude_flags == ('--exclude-from', '/tmp/excludes')
  105. def test_make_exclude_flags_includes_exclude_from_filenames_when_in_config():
  106. exclude_flags = module._make_exclude_flags(
  107. location_config={'exclude_from': ['excludes', 'other']}
  108. )
  109. assert exclude_flags == ('--exclude-from', 'excludes', '--exclude-from', 'other')
  110. def test_make_exclude_flags_includes_both_filenames_when_patterns_given_and_exclude_from_in_config():
  111. exclude_flags = module._make_exclude_flags(
  112. location_config={'exclude_from': ['excludes']}, exclude_filename='/tmp/excludes'
  113. )
  114. assert exclude_flags == ('--exclude-from', 'excludes', '--exclude-from', '/tmp/excludes')
  115. def test_make_exclude_flags_considers_none_exclude_from_filenames_as_empty():
  116. exclude_flags = module._make_exclude_flags(location_config={'exclude_from': None})
  117. assert exclude_flags == ()
  118. def test_make_exclude_flags_includes_exclude_caches_when_true_in_config():
  119. exclude_flags = module._make_exclude_flags(location_config={'exclude_caches': True})
  120. assert exclude_flags == ('--exclude-caches',)
  121. def test_make_exclude_flags_does_not_include_exclude_caches_when_false_in_config():
  122. exclude_flags = module._make_exclude_flags(location_config={'exclude_caches': False})
  123. assert exclude_flags == ()
  124. def test_make_exclude_flags_includes_exclude_if_present_when_in_config():
  125. exclude_flags = module._make_exclude_flags(
  126. location_config={'exclude_if_present': ['exclude_me', 'also_me']}
  127. )
  128. assert exclude_flags == (
  129. '--exclude-if-present',
  130. 'exclude_me',
  131. '--exclude-if-present',
  132. 'also_me',
  133. )
  134. def test_make_exclude_flags_includes_keep_exclude_tags_when_true_in_config():
  135. exclude_flags = module._make_exclude_flags(location_config={'keep_exclude_tags': True})
  136. assert exclude_flags == ('--keep-exclude-tags',)
  137. def test_make_exclude_flags_does_not_include_keep_exclude_tags_when_false_in_config():
  138. exclude_flags = module._make_exclude_flags(location_config={'keep_exclude_tags': False})
  139. assert exclude_flags == ()
  140. def test_make_exclude_flags_includes_exclude_nodump_when_true_in_config():
  141. exclude_flags = module._make_exclude_flags(location_config={'exclude_nodump': True})
  142. assert exclude_flags == ('--exclude-nodump',)
  143. def test_make_exclude_flags_does_not_include_exclude_nodump_when_false_in_config():
  144. exclude_flags = module._make_exclude_flags(location_config={'exclude_nodump': False})
  145. assert exclude_flags == ()
  146. def test_make_exclude_flags_is_empty_when_config_has_no_excludes():
  147. exclude_flags = module._make_exclude_flags(location_config={})
  148. assert exclude_flags == ()
  149. def test_borgmatic_source_directories_set_when_directory_exists():
  150. flexmock(module.os.path).should_receive('exists').and_return(True)
  151. flexmock(module.os.path).should_receive('expanduser')
  152. assert module.borgmatic_source_directories('/tmp') == ['/tmp']
  153. def test_borgmatic_source_directories_empty_when_directory_does_not_exist():
  154. flexmock(module.os.path).should_receive('exists').and_return(False)
  155. flexmock(module.os.path).should_receive('expanduser')
  156. assert module.borgmatic_source_directories('/tmp') == []
  157. def test_borgmatic_source_directories_defaults_when_directory_not_given():
  158. flexmock(module.os.path).should_receive('exists').and_return(True)
  159. flexmock(module.os.path).should_receive('expanduser')
  160. assert module.borgmatic_source_directories(None) == [module.DEFAULT_BORGMATIC_SOURCE_DIRECTORY]
  161. DEFAULT_ARCHIVE_NAME = '{hostname}-{now:%Y-%m-%dT%H:%M:%S.%f}'
  162. ARCHIVE_WITH_PATHS = ('repo::{}'.format(DEFAULT_ARCHIVE_NAME), 'foo', 'bar')
  163. def test_create_archive_calls_borg_with_parameters():
  164. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  165. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  166. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  167. flexmock(module).should_receive('_expand_directories').and_return(())
  168. flexmock(module).should_receive('_expand_home_directories').and_return(())
  169. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  170. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  171. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  172. flexmock(module).should_receive('execute_command').with_args(
  173. ('borg', 'create') + ARCHIVE_WITH_PATHS,
  174. output_log_level=logging.INFO,
  175. output_file=None,
  176. borg_local_path='borg',
  177. working_directory=None,
  178. )
  179. module.create_archive(
  180. dry_run=False,
  181. repository='repo',
  182. location_config={
  183. 'source_directories': ['foo', 'bar'],
  184. 'repositories': ['repo'],
  185. 'exclude_patterns': None,
  186. },
  187. storage_config={},
  188. )
  189. def test_create_archive_with_patterns_calls_borg_with_patterns():
  190. pattern_flags = ('--patterns-from', 'patterns')
  191. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  192. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  193. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  194. flexmock(module).should_receive('_expand_directories').and_return(())
  195. flexmock(module).should_receive('_expand_home_directories').and_return(())
  196. flexmock(module).should_receive('_write_pattern_file').and_return(
  197. flexmock(name='/tmp/patterns')
  198. ).and_return(None)
  199. flexmock(module).should_receive('_make_pattern_flags').and_return(pattern_flags)
  200. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  201. flexmock(module).should_receive('execute_command').with_args(
  202. ('borg', 'create') + pattern_flags + ARCHIVE_WITH_PATHS,
  203. output_log_level=logging.INFO,
  204. output_file=None,
  205. borg_local_path='borg',
  206. working_directory=None,
  207. )
  208. module.create_archive(
  209. dry_run=False,
  210. repository='repo',
  211. location_config={
  212. 'source_directories': ['foo', 'bar'],
  213. 'repositories': ['repo'],
  214. 'patterns': ['pattern'],
  215. },
  216. storage_config={},
  217. )
  218. def test_create_archive_with_exclude_patterns_calls_borg_with_excludes():
  219. exclude_flags = ('--exclude-from', 'excludes')
  220. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  221. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  222. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  223. flexmock(module).should_receive('_expand_directories').and_return(())
  224. flexmock(module).should_receive('_expand_home_directories').and_return(('exclude',))
  225. flexmock(module).should_receive('_write_pattern_file').and_return(None).and_return(
  226. flexmock(name='/tmp/excludes')
  227. )
  228. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  229. flexmock(module).should_receive('_make_exclude_flags').and_return(exclude_flags)
  230. flexmock(module).should_receive('execute_command').with_args(
  231. ('borg', 'create') + exclude_flags + ARCHIVE_WITH_PATHS,
  232. output_log_level=logging.INFO,
  233. output_file=None,
  234. borg_local_path='borg',
  235. working_directory=None,
  236. )
  237. module.create_archive(
  238. dry_run=False,
  239. repository='repo',
  240. location_config={
  241. 'source_directories': ['foo', 'bar'],
  242. 'repositories': ['repo'],
  243. 'exclude_patterns': ['exclude'],
  244. },
  245. storage_config={},
  246. )
  247. def test_create_archive_with_log_info_calls_borg_with_info_parameter():
  248. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  249. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  250. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  251. flexmock(module).should_receive('_expand_directories').and_return(())
  252. flexmock(module).should_receive('_expand_home_directories').and_return(())
  253. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  254. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  255. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  256. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  257. flexmock(module).should_receive('execute_command').with_args(
  258. ('borg', 'create', '--info') + ARCHIVE_WITH_PATHS,
  259. output_log_level=logging.INFO,
  260. output_file=None,
  261. borg_local_path='borg',
  262. working_directory=None,
  263. )
  264. insert_logging_mock(logging.INFO)
  265. module.create_archive(
  266. dry_run=False,
  267. repository='repo',
  268. location_config={
  269. 'source_directories': ['foo', 'bar'],
  270. 'repositories': ['repo'],
  271. 'exclude_patterns': None,
  272. },
  273. storage_config={},
  274. )
  275. def test_create_archive_with_log_info_and_json_suppresses_most_borg_output():
  276. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  277. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  278. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  279. flexmock(module).should_receive('_expand_directories').and_return(())
  280. flexmock(module).should_receive('_expand_home_directories').and_return(())
  281. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  282. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  283. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  284. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  285. flexmock(module).should_receive('execute_command').with_args(
  286. ('borg', 'create', '--json') + ARCHIVE_WITH_PATHS,
  287. output_log_level=None,
  288. output_file=None,
  289. borg_local_path='borg',
  290. working_directory=None,
  291. )
  292. insert_logging_mock(logging.INFO)
  293. module.create_archive(
  294. dry_run=False,
  295. repository='repo',
  296. location_config={
  297. 'source_directories': ['foo', 'bar'],
  298. 'repositories': ['repo'],
  299. 'exclude_patterns': None,
  300. },
  301. storage_config={},
  302. json=True,
  303. )
  304. def test_create_archive_with_log_debug_calls_borg_with_debug_parameter():
  305. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  306. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  307. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  308. flexmock(module).should_receive('_expand_directories').and_return(())
  309. flexmock(module).should_receive('_expand_home_directories').and_return(())
  310. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  311. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  312. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  313. flexmock(module).should_receive('execute_command').with_args(
  314. ('borg', 'create', '--debug', '--show-rc') + ARCHIVE_WITH_PATHS,
  315. output_log_level=logging.INFO,
  316. output_file=None,
  317. borg_local_path='borg',
  318. working_directory=None,
  319. )
  320. insert_logging_mock(logging.DEBUG)
  321. module.create_archive(
  322. dry_run=False,
  323. repository='repo',
  324. location_config={
  325. 'source_directories': ['foo', 'bar'],
  326. 'repositories': ['repo'],
  327. 'exclude_patterns': None,
  328. },
  329. storage_config={},
  330. )
  331. def test_create_archive_with_log_debug_and_json_suppresses_most_borg_output():
  332. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  333. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  334. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  335. flexmock(module).should_receive('_expand_directories').and_return(())
  336. flexmock(module).should_receive('_expand_home_directories').and_return(())
  337. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  338. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  339. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  340. flexmock(module).should_receive('execute_command').with_args(
  341. ('borg', 'create', '--json') + ARCHIVE_WITH_PATHS,
  342. output_log_level=None,
  343. output_file=None,
  344. borg_local_path='borg',
  345. working_directory=None,
  346. )
  347. insert_logging_mock(logging.DEBUG)
  348. module.create_archive(
  349. dry_run=False,
  350. repository='repo',
  351. location_config={
  352. 'source_directories': ['foo', 'bar'],
  353. 'repositories': ['repo'],
  354. 'exclude_patterns': None,
  355. },
  356. storage_config={},
  357. json=True,
  358. )
  359. def test_create_archive_with_dry_run_calls_borg_with_dry_run_parameter():
  360. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  361. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  362. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  363. flexmock(module).should_receive('_expand_directories').and_return(())
  364. flexmock(module).should_receive('_expand_home_directories').and_return(())
  365. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  366. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  367. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  368. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  369. flexmock(module).should_receive('execute_command').with_args(
  370. ('borg', 'create', '--dry-run') + ARCHIVE_WITH_PATHS,
  371. output_log_level=logging.INFO,
  372. output_file=None,
  373. borg_local_path='borg',
  374. working_directory=None,
  375. )
  376. module.create_archive(
  377. dry_run=True,
  378. repository='repo',
  379. location_config={
  380. 'source_directories': ['foo', 'bar'],
  381. 'repositories': ['repo'],
  382. 'exclude_patterns': None,
  383. },
  384. storage_config={},
  385. )
  386. def test_create_archive_with_stats_and_dry_run_calls_borg_without_stats_parameter():
  387. # --dry-run and --stats are mutually exclusive, see:
  388. # https://borgbackup.readthedocs.io/en/stable/usage/create.html#description
  389. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  390. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  391. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  392. flexmock(module).should_receive('_expand_directories').and_return(())
  393. flexmock(module).should_receive('_expand_home_directories').and_return(())
  394. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  395. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  396. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  397. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  398. flexmock(module).should_receive('execute_command').with_args(
  399. ('borg', 'create', '--info', '--dry-run') + ARCHIVE_WITH_PATHS,
  400. output_log_level=logging.INFO,
  401. output_file=None,
  402. borg_local_path='borg',
  403. working_directory=None,
  404. )
  405. insert_logging_mock(logging.INFO)
  406. module.create_archive(
  407. dry_run=True,
  408. repository='repo',
  409. location_config={
  410. 'source_directories': ['foo', 'bar'],
  411. 'repositories': ['repo'],
  412. 'exclude_patterns': None,
  413. },
  414. storage_config={},
  415. stats=True,
  416. )
  417. def test_create_archive_with_checkpoint_interval_calls_borg_with_checkpoint_interval_parameters():
  418. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  419. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  420. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  421. flexmock(module).should_receive('_expand_directories').and_return(())
  422. flexmock(module).should_receive('_expand_home_directories').and_return(())
  423. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  424. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  425. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  426. flexmock(module).should_receive('execute_command').with_args(
  427. ('borg', 'create', '--checkpoint-interval', '600') + ARCHIVE_WITH_PATHS,
  428. output_log_level=logging.INFO,
  429. output_file=None,
  430. borg_local_path='borg',
  431. working_directory=None,
  432. )
  433. module.create_archive(
  434. dry_run=False,
  435. repository='repo',
  436. location_config={
  437. 'source_directories': ['foo', 'bar'],
  438. 'repositories': ['repo'],
  439. 'exclude_patterns': None,
  440. },
  441. storage_config={'checkpoint_interval': 600},
  442. )
  443. def test_create_archive_with_chunker_params_calls_borg_with_chunker_params_parameters():
  444. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  445. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  446. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  447. flexmock(module).should_receive('_expand_directories').and_return(())
  448. flexmock(module).should_receive('_expand_home_directories').and_return(())
  449. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  450. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  451. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  452. flexmock(module).should_receive('execute_command').with_args(
  453. ('borg', 'create', '--chunker-params', '1,2,3,4') + ARCHIVE_WITH_PATHS,
  454. output_log_level=logging.INFO,
  455. output_file=None,
  456. borg_local_path='borg',
  457. working_directory=None,
  458. )
  459. module.create_archive(
  460. dry_run=False,
  461. repository='repo',
  462. location_config={
  463. 'source_directories': ['foo', 'bar'],
  464. 'repositories': ['repo'],
  465. 'exclude_patterns': None,
  466. },
  467. storage_config={'chunker_params': '1,2,3,4'},
  468. )
  469. def test_create_archive_with_compression_calls_borg_with_compression_parameters():
  470. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  471. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  472. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  473. flexmock(module).should_receive('_expand_directories').and_return(())
  474. flexmock(module).should_receive('_expand_home_directories').and_return(())
  475. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  476. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  477. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  478. flexmock(module).should_receive('execute_command').with_args(
  479. ('borg', 'create', '--compression', 'rle') + ARCHIVE_WITH_PATHS,
  480. output_log_level=logging.INFO,
  481. output_file=None,
  482. borg_local_path='borg',
  483. working_directory=None,
  484. )
  485. module.create_archive(
  486. dry_run=False,
  487. repository='repo',
  488. location_config={
  489. 'source_directories': ['foo', 'bar'],
  490. 'repositories': ['repo'],
  491. 'exclude_patterns': None,
  492. },
  493. storage_config={'compression': 'rle'},
  494. )
  495. def test_create_archive_with_remote_rate_limit_calls_borg_with_remote_ratelimit_parameters():
  496. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  497. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  498. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  499. flexmock(module).should_receive('_expand_directories').and_return(())
  500. flexmock(module).should_receive('_expand_home_directories').and_return(())
  501. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  502. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  503. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  504. flexmock(module).should_receive('execute_command').with_args(
  505. ('borg', 'create', '--remote-ratelimit', '100') + ARCHIVE_WITH_PATHS,
  506. output_log_level=logging.INFO,
  507. output_file=None,
  508. borg_local_path='borg',
  509. working_directory=None,
  510. )
  511. module.create_archive(
  512. dry_run=False,
  513. repository='repo',
  514. location_config={
  515. 'source_directories': ['foo', 'bar'],
  516. 'repositories': ['repo'],
  517. 'exclude_patterns': None,
  518. },
  519. storage_config={'remote_rate_limit': 100},
  520. )
  521. def test_create_archive_with_one_file_system_calls_borg_with_one_file_system_parameter():
  522. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  523. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  524. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  525. flexmock(module).should_receive('_expand_directories').and_return(())
  526. flexmock(module).should_receive('_expand_home_directories').and_return(())
  527. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  528. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  529. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  530. flexmock(module).should_receive('execute_command').with_args(
  531. ('borg', 'create', '--one-file-system') + ARCHIVE_WITH_PATHS,
  532. output_log_level=logging.INFO,
  533. output_file=None,
  534. borg_local_path='borg',
  535. working_directory=None,
  536. )
  537. module.create_archive(
  538. dry_run=False,
  539. repository='repo',
  540. location_config={
  541. 'source_directories': ['foo', 'bar'],
  542. 'repositories': ['repo'],
  543. 'one_file_system': True,
  544. 'exclude_patterns': None,
  545. },
  546. storage_config={},
  547. )
  548. def test_create_archive_with_numeric_owner_calls_borg_with_numeric_owner_parameter():
  549. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  550. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  551. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  552. flexmock(module).should_receive('_expand_directories').and_return(())
  553. flexmock(module).should_receive('_expand_home_directories').and_return(())
  554. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  555. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  556. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  557. flexmock(module).should_receive('execute_command').with_args(
  558. ('borg', 'create', '--numeric-owner') + ARCHIVE_WITH_PATHS,
  559. output_log_level=logging.INFO,
  560. output_file=None,
  561. borg_local_path='borg',
  562. working_directory=None,
  563. )
  564. module.create_archive(
  565. dry_run=False,
  566. repository='repo',
  567. location_config={
  568. 'source_directories': ['foo', 'bar'],
  569. 'repositories': ['repo'],
  570. 'numeric_owner': True,
  571. 'exclude_patterns': None,
  572. },
  573. storage_config={},
  574. )
  575. def test_create_archive_with_read_special_calls_borg_with_read_special_parameter():
  576. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  577. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  578. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  579. flexmock(module).should_receive('_expand_directories').and_return(())
  580. flexmock(module).should_receive('_expand_home_directories').and_return(())
  581. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  582. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  583. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  584. flexmock(module).should_receive('execute_command').with_args(
  585. ('borg', 'create', '--read-special') + ARCHIVE_WITH_PATHS,
  586. output_log_level=logging.INFO,
  587. output_file=None,
  588. borg_local_path='borg',
  589. working_directory=None,
  590. )
  591. module.create_archive(
  592. dry_run=False,
  593. repository='repo',
  594. location_config={
  595. 'source_directories': ['foo', 'bar'],
  596. 'repositories': ['repo'],
  597. 'read_special': True,
  598. 'exclude_patterns': None,
  599. },
  600. storage_config={},
  601. )
  602. @pytest.mark.parametrize('option_name', ('atime', 'ctime', 'birthtime', 'bsd_flags'))
  603. def test_create_archive_with_option_true_calls_borg_without_corresponding_parameter(option_name):
  604. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  605. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  606. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  607. flexmock(module).should_receive('_expand_directories').and_return(())
  608. flexmock(module).should_receive('_expand_home_directories').and_return(())
  609. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  610. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  611. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  612. flexmock(module).should_receive('execute_command').with_args(
  613. ('borg', 'create') + ARCHIVE_WITH_PATHS,
  614. output_log_level=logging.INFO,
  615. output_file=None,
  616. borg_local_path='borg',
  617. working_directory=None,
  618. )
  619. module.create_archive(
  620. dry_run=False,
  621. repository='repo',
  622. location_config={
  623. 'source_directories': ['foo', 'bar'],
  624. 'repositories': ['repo'],
  625. option_name: True,
  626. 'exclude_patterns': None,
  627. },
  628. storage_config={},
  629. )
  630. @pytest.mark.parametrize('option_name', ('atime', 'ctime', 'birthtime', 'bsd_flags'))
  631. def test_create_archive_with_option_false_calls_borg_with_corresponding_parameter(option_name):
  632. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  633. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  634. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  635. flexmock(module).should_receive('_expand_directories').and_return(())
  636. flexmock(module).should_receive('_expand_home_directories').and_return(())
  637. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  638. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  639. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  640. flexmock(module).should_receive('execute_command').with_args(
  641. ('borg', 'create', '--no' + option_name.replace('_', '')) + ARCHIVE_WITH_PATHS,
  642. output_log_level=logging.INFO,
  643. output_file=None,
  644. borg_local_path='borg',
  645. working_directory=None,
  646. )
  647. module.create_archive(
  648. dry_run=False,
  649. repository='repo',
  650. location_config={
  651. 'source_directories': ['foo', 'bar'],
  652. 'repositories': ['repo'],
  653. option_name: False,
  654. 'exclude_patterns': None,
  655. },
  656. storage_config={},
  657. )
  658. def test_create_archive_with_files_cache_calls_borg_with_files_cache_parameters():
  659. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  660. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  661. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  662. flexmock(module).should_receive('_expand_directories').and_return(())
  663. flexmock(module).should_receive('_expand_home_directories').and_return(())
  664. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  665. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  666. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  667. flexmock(module).should_receive('execute_command').with_args(
  668. ('borg', 'create', '--files-cache', 'ctime,size') + ARCHIVE_WITH_PATHS,
  669. output_log_level=logging.INFO,
  670. output_file=None,
  671. borg_local_path='borg',
  672. working_directory=None,
  673. )
  674. module.create_archive(
  675. dry_run=False,
  676. repository='repo',
  677. location_config={
  678. 'source_directories': ['foo', 'bar'],
  679. 'repositories': ['repo'],
  680. 'files_cache': 'ctime,size',
  681. 'exclude_patterns': None,
  682. },
  683. storage_config={},
  684. )
  685. def test_create_archive_with_local_path_calls_borg_via_local_path():
  686. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  687. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  688. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  689. flexmock(module).should_receive('_expand_directories').and_return(())
  690. flexmock(module).should_receive('_expand_home_directories').and_return(())
  691. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  692. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  693. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  694. flexmock(module).should_receive('execute_command').with_args(
  695. ('borg1', 'create') + ARCHIVE_WITH_PATHS,
  696. output_log_level=logging.INFO,
  697. output_file=None,
  698. borg_local_path='borg1',
  699. working_directory=None,
  700. )
  701. module.create_archive(
  702. dry_run=False,
  703. repository='repo',
  704. location_config={
  705. 'source_directories': ['foo', 'bar'],
  706. 'repositories': ['repo'],
  707. 'exclude_patterns': None,
  708. },
  709. storage_config={},
  710. local_path='borg1',
  711. )
  712. def test_create_archive_with_remote_path_calls_borg_with_remote_path_parameters():
  713. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  714. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  715. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  716. flexmock(module).should_receive('_expand_directories').and_return(())
  717. flexmock(module).should_receive('_expand_home_directories').and_return(())
  718. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  719. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  720. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  721. flexmock(module).should_receive('execute_command').with_args(
  722. ('borg', 'create', '--remote-path', 'borg1') + ARCHIVE_WITH_PATHS,
  723. output_log_level=logging.INFO,
  724. output_file=None,
  725. borg_local_path='borg',
  726. working_directory=None,
  727. )
  728. module.create_archive(
  729. dry_run=False,
  730. repository='repo',
  731. location_config={
  732. 'source_directories': ['foo', 'bar'],
  733. 'repositories': ['repo'],
  734. 'exclude_patterns': None,
  735. },
  736. storage_config={},
  737. remote_path='borg1',
  738. )
  739. def test_create_archive_with_umask_calls_borg_with_umask_parameters():
  740. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  741. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  742. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  743. flexmock(module).should_receive('_expand_directories').and_return(())
  744. flexmock(module).should_receive('_expand_home_directories').and_return(())
  745. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  746. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  747. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  748. flexmock(module).should_receive('execute_command').with_args(
  749. ('borg', 'create', '--umask', '740') + ARCHIVE_WITH_PATHS,
  750. output_log_level=logging.INFO,
  751. output_file=None,
  752. borg_local_path='borg',
  753. working_directory=None,
  754. )
  755. module.create_archive(
  756. dry_run=False,
  757. repository='repo',
  758. location_config={
  759. 'source_directories': ['foo', 'bar'],
  760. 'repositories': ['repo'],
  761. 'exclude_patterns': None,
  762. },
  763. storage_config={'umask': 740},
  764. )
  765. def test_create_archive_with_lock_wait_calls_borg_with_lock_wait_parameters():
  766. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  767. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  768. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  769. flexmock(module).should_receive('_expand_directories').and_return(())
  770. flexmock(module).should_receive('_expand_home_directories').and_return(())
  771. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  772. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  773. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  774. flexmock(module).should_receive('execute_command').with_args(
  775. ('borg', 'create', '--lock-wait', '5') + ARCHIVE_WITH_PATHS,
  776. output_log_level=logging.INFO,
  777. output_file=None,
  778. borg_local_path='borg',
  779. working_directory=None,
  780. )
  781. module.create_archive(
  782. dry_run=False,
  783. repository='repo',
  784. location_config={
  785. 'source_directories': ['foo', 'bar'],
  786. 'repositories': ['repo'],
  787. 'exclude_patterns': None,
  788. },
  789. storage_config={'lock_wait': 5},
  790. )
  791. def test_create_archive_with_stats_calls_borg_with_stats_parameter_and_warning_output_log_level():
  792. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  793. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  794. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  795. flexmock(module).should_receive('_expand_directories').and_return(())
  796. flexmock(module).should_receive('_expand_home_directories').and_return(())
  797. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  798. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  799. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  800. flexmock(module).should_receive('execute_command').with_args(
  801. ('borg', 'create', '--stats') + ARCHIVE_WITH_PATHS,
  802. output_log_level=logging.WARNING,
  803. output_file=None,
  804. borg_local_path='borg',
  805. working_directory=None,
  806. )
  807. module.create_archive(
  808. dry_run=False,
  809. repository='repo',
  810. location_config={
  811. 'source_directories': ['foo', 'bar'],
  812. 'repositories': ['repo'],
  813. 'exclude_patterns': None,
  814. },
  815. storage_config={},
  816. stats=True,
  817. )
  818. def test_create_archive_with_stats_and_log_info_calls_borg_with_stats_parameter_and_info_output_log_level():
  819. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  820. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  821. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  822. flexmock(module).should_receive('_expand_directories').and_return(())
  823. flexmock(module).should_receive('_expand_home_directories').and_return(())
  824. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  825. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  826. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  827. flexmock(module).should_receive('execute_command').with_args(
  828. ('borg', 'create', '--info', '--stats') + ARCHIVE_WITH_PATHS,
  829. output_log_level=logging.INFO,
  830. output_file=None,
  831. borg_local_path='borg',
  832. working_directory=None,
  833. )
  834. insert_logging_mock(logging.INFO)
  835. module.create_archive(
  836. dry_run=False,
  837. repository='repo',
  838. location_config={
  839. 'source_directories': ['foo', 'bar'],
  840. 'repositories': ['repo'],
  841. 'exclude_patterns': None,
  842. },
  843. storage_config={},
  844. stats=True,
  845. )
  846. def test_create_archive_with_files_calls_borg_with_list_parameter_and_warning_output_log_level():
  847. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  848. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  849. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  850. flexmock(module).should_receive('_expand_directories').and_return(())
  851. flexmock(module).should_receive('_expand_home_directories').and_return(())
  852. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  853. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  854. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  855. flexmock(module).should_receive('execute_command').with_args(
  856. ('borg', 'create', '--list', '--filter', 'AME-') + ARCHIVE_WITH_PATHS,
  857. output_log_level=logging.WARNING,
  858. output_file=None,
  859. borg_local_path='borg',
  860. working_directory=None,
  861. )
  862. module.create_archive(
  863. dry_run=False,
  864. repository='repo',
  865. location_config={
  866. 'source_directories': ['foo', 'bar'],
  867. 'repositories': ['repo'],
  868. 'exclude_patterns': None,
  869. },
  870. storage_config={},
  871. files=True,
  872. )
  873. def test_create_archive_with_files_and_log_info_calls_borg_with_list_parameter_and_info_output_log_level():
  874. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  875. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  876. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  877. flexmock(module).should_receive('_expand_directories').and_return(())
  878. flexmock(module).should_receive('_expand_home_directories').and_return(())
  879. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  880. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  881. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  882. flexmock(module).should_receive('execute_command').with_args(
  883. ('borg', 'create', '--list', '--filter', 'AME-', '--info') + ARCHIVE_WITH_PATHS,
  884. output_log_level=logging.INFO,
  885. output_file=None,
  886. borg_local_path='borg',
  887. working_directory=None,
  888. )
  889. insert_logging_mock(logging.INFO)
  890. module.create_archive(
  891. dry_run=False,
  892. repository='repo',
  893. location_config={
  894. 'source_directories': ['foo', 'bar'],
  895. 'repositories': ['repo'],
  896. 'exclude_patterns': None,
  897. },
  898. storage_config={},
  899. files=True,
  900. )
  901. def test_create_archive_with_progress_and_log_info_calls_borg_with_progress_parameter_and_no_list():
  902. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  903. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  904. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  905. flexmock(module).should_receive('_expand_directories').and_return(())
  906. flexmock(module).should_receive('_expand_home_directories').and_return(())
  907. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  908. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  909. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  910. flexmock(module).should_receive('execute_command').with_args(
  911. ('borg', 'create', '--info', '--progress') + ARCHIVE_WITH_PATHS,
  912. output_log_level=logging.INFO,
  913. output_file=module.DO_NOT_CAPTURE,
  914. borg_local_path='borg',
  915. working_directory=None,
  916. )
  917. insert_logging_mock(logging.INFO)
  918. module.create_archive(
  919. dry_run=False,
  920. repository='repo',
  921. location_config={
  922. 'source_directories': ['foo', 'bar'],
  923. 'repositories': ['repo'],
  924. 'exclude_patterns': None,
  925. },
  926. storage_config={},
  927. progress=True,
  928. )
  929. def test_create_archive_with_progress_calls_borg_with_progress_parameter():
  930. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  931. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  932. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  933. flexmock(module).should_receive('_expand_directories').and_return(())
  934. flexmock(module).should_receive('_expand_home_directories').and_return(())
  935. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  936. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  937. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  938. flexmock(module).should_receive('execute_command').with_args(
  939. ('borg', 'create', '--progress') + ARCHIVE_WITH_PATHS,
  940. output_log_level=logging.INFO,
  941. output_file=module.DO_NOT_CAPTURE,
  942. borg_local_path='borg',
  943. working_directory=None,
  944. )
  945. module.create_archive(
  946. dry_run=False,
  947. repository='repo',
  948. location_config={
  949. 'source_directories': ['foo', 'bar'],
  950. 'repositories': ['repo'],
  951. 'exclude_patterns': None,
  952. },
  953. storage_config={},
  954. progress=True,
  955. )
  956. def test_create_archive_with_progress_and_stream_processes_calls_borg_with_progress_parameter():
  957. processes = flexmock()
  958. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  959. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  960. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  961. flexmock(module).should_receive('_expand_directories').and_return(())
  962. flexmock(module).should_receive('_expand_home_directories').and_return(())
  963. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  964. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  965. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  966. flexmock(module).should_receive('execute_command_with_processes').with_args(
  967. ('borg', 'create', '--one-file-system', '--read-special', '--progress')
  968. + ARCHIVE_WITH_PATHS,
  969. processes=processes,
  970. output_log_level=logging.INFO,
  971. output_file=module.DO_NOT_CAPTURE,
  972. borg_local_path='borg',
  973. working_directory=None,
  974. )
  975. module.create_archive(
  976. dry_run=False,
  977. repository='repo',
  978. location_config={
  979. 'source_directories': ['foo', 'bar'],
  980. 'repositories': ['repo'],
  981. 'exclude_patterns': None,
  982. },
  983. storage_config={},
  984. progress=True,
  985. stream_processes=processes,
  986. )
  987. def test_create_archive_with_json_calls_borg_with_json_parameter():
  988. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  989. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  990. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  991. flexmock(module).should_receive('_expand_directories').and_return(())
  992. flexmock(module).should_receive('_expand_home_directories').and_return(())
  993. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  994. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  995. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  996. flexmock(module).should_receive('execute_command').with_args(
  997. ('borg', 'create', '--json') + ARCHIVE_WITH_PATHS,
  998. output_log_level=None,
  999. output_file=None,
  1000. borg_local_path='borg',
  1001. working_directory=None,
  1002. ).and_return('[]')
  1003. json_output = module.create_archive(
  1004. dry_run=False,
  1005. repository='repo',
  1006. location_config={
  1007. 'source_directories': ['foo', 'bar'],
  1008. 'repositories': ['repo'],
  1009. 'exclude_patterns': None,
  1010. },
  1011. storage_config={},
  1012. json=True,
  1013. )
  1014. assert json_output == '[]'
  1015. def test_create_archive_with_stats_and_json_calls_borg_without_stats_parameter():
  1016. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  1017. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  1018. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  1019. flexmock(module).should_receive('_expand_directories').and_return(())
  1020. flexmock(module).should_receive('_expand_home_directories').and_return(())
  1021. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  1022. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  1023. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  1024. flexmock(module).should_receive('execute_command').with_args(
  1025. ('borg', 'create', '--json') + ARCHIVE_WITH_PATHS,
  1026. output_log_level=None,
  1027. output_file=None,
  1028. borg_local_path='borg',
  1029. working_directory=None,
  1030. ).and_return('[]')
  1031. json_output = module.create_archive(
  1032. dry_run=False,
  1033. repository='repo',
  1034. location_config={
  1035. 'source_directories': ['foo', 'bar'],
  1036. 'repositories': ['repo'],
  1037. 'exclude_patterns': None,
  1038. },
  1039. storage_config={},
  1040. json=True,
  1041. stats=True,
  1042. )
  1043. assert json_output == '[]'
  1044. def test_create_archive_with_source_directories_glob_expands():
  1045. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  1046. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'food'))
  1047. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  1048. flexmock(module).should_receive('_expand_directories').and_return(())
  1049. flexmock(module).should_receive('_expand_home_directories').and_return(())
  1050. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  1051. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  1052. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  1053. flexmock(module).should_receive('execute_command').with_args(
  1054. ('borg', 'create', 'repo::{}'.format(DEFAULT_ARCHIVE_NAME), 'foo', 'food'),
  1055. output_log_level=logging.INFO,
  1056. output_file=None,
  1057. borg_local_path='borg',
  1058. working_directory=None,
  1059. )
  1060. flexmock(module.glob).should_receive('glob').with_args('foo*').and_return(['foo', 'food'])
  1061. module.create_archive(
  1062. dry_run=False,
  1063. repository='repo',
  1064. location_config={
  1065. 'source_directories': ['foo*'],
  1066. 'repositories': ['repo'],
  1067. 'exclude_patterns': None,
  1068. },
  1069. storage_config={},
  1070. )
  1071. def test_create_archive_with_non_matching_source_directories_glob_passes_through():
  1072. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  1073. flexmock(module).should_receive('deduplicate_directories').and_return(('foo*',))
  1074. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  1075. flexmock(module).should_receive('_expand_directories').and_return(())
  1076. flexmock(module).should_receive('_expand_home_directories').and_return(())
  1077. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  1078. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  1079. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  1080. flexmock(module).should_receive('execute_command').with_args(
  1081. ('borg', 'create', 'repo::{}'.format(DEFAULT_ARCHIVE_NAME), 'foo*'),
  1082. output_log_level=logging.INFO,
  1083. output_file=None,
  1084. borg_local_path='borg',
  1085. working_directory=None,
  1086. )
  1087. flexmock(module.glob).should_receive('glob').with_args('foo*').and_return([])
  1088. module.create_archive(
  1089. dry_run=False,
  1090. repository='repo',
  1091. location_config={
  1092. 'source_directories': ['foo*'],
  1093. 'repositories': ['repo'],
  1094. 'exclude_patterns': None,
  1095. },
  1096. storage_config={},
  1097. )
  1098. def test_create_archive_with_glob_calls_borg_with_expanded_directories():
  1099. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  1100. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'food'))
  1101. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  1102. flexmock(module).should_receive('_expand_directories').and_return(())
  1103. flexmock(module).should_receive('_expand_home_directories').and_return(())
  1104. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  1105. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  1106. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  1107. flexmock(module).should_receive('execute_command').with_args(
  1108. ('borg', 'create', 'repo::{}'.format(DEFAULT_ARCHIVE_NAME), 'foo', 'food'),
  1109. output_log_level=logging.INFO,
  1110. output_file=None,
  1111. borg_local_path='borg',
  1112. working_directory=None,
  1113. )
  1114. module.create_archive(
  1115. dry_run=False,
  1116. repository='repo',
  1117. location_config={
  1118. 'source_directories': ['foo*'],
  1119. 'repositories': ['repo'],
  1120. 'exclude_patterns': None,
  1121. },
  1122. storage_config={},
  1123. )
  1124. def test_create_archive_with_archive_name_format_calls_borg_with_archive_name():
  1125. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  1126. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  1127. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  1128. flexmock(module).should_receive('_expand_directories').and_return(())
  1129. flexmock(module).should_receive('_expand_home_directories').and_return(())
  1130. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  1131. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  1132. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  1133. flexmock(module).should_receive('execute_command').with_args(
  1134. ('borg', 'create', 'repo::ARCHIVE_NAME', 'foo', 'bar'),
  1135. output_log_level=logging.INFO,
  1136. output_file=None,
  1137. borg_local_path='borg',
  1138. working_directory=None,
  1139. )
  1140. module.create_archive(
  1141. dry_run=False,
  1142. repository='repo',
  1143. location_config={
  1144. 'source_directories': ['foo', 'bar'],
  1145. 'repositories': ['repo'],
  1146. 'exclude_patterns': None,
  1147. },
  1148. storage_config={'archive_name_format': 'ARCHIVE_NAME'},
  1149. )
  1150. def test_create_archive_with_archive_name_format_accepts_borg_placeholders():
  1151. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  1152. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  1153. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  1154. flexmock(module).should_receive('_expand_directories').and_return(())
  1155. flexmock(module).should_receive('_expand_home_directories').and_return(())
  1156. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  1157. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  1158. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  1159. flexmock(module).should_receive('execute_command').with_args(
  1160. ('borg', 'create', 'repo::Documents_{hostname}-{now}', 'foo', 'bar'),
  1161. output_log_level=logging.INFO,
  1162. output_file=None,
  1163. borg_local_path='borg',
  1164. working_directory=None,
  1165. )
  1166. module.create_archive(
  1167. dry_run=False,
  1168. repository='repo',
  1169. location_config={
  1170. 'source_directories': ['foo', 'bar'],
  1171. 'repositories': ['repo'],
  1172. 'exclude_patterns': None,
  1173. },
  1174. storage_config={'archive_name_format': 'Documents_{hostname}-{now}'},
  1175. )
  1176. def test_create_archive_with_repository_accepts_borg_placeholders():
  1177. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  1178. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  1179. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  1180. flexmock(module).should_receive('_expand_directories').and_return(())
  1181. flexmock(module).should_receive('_expand_home_directories').and_return(())
  1182. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  1183. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  1184. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  1185. flexmock(module).should_receive('execute_command').with_args(
  1186. ('borg', 'create', '{fqdn}::Documents_{hostname}-{now}', 'foo', 'bar'),
  1187. output_log_level=logging.INFO,
  1188. output_file=None,
  1189. borg_local_path='borg',
  1190. working_directory=None,
  1191. )
  1192. module.create_archive(
  1193. dry_run=False,
  1194. repository='{fqdn}',
  1195. location_config={
  1196. 'source_directories': ['foo', 'bar'],
  1197. 'repositories': ['{fqdn}'],
  1198. 'exclude_patterns': None,
  1199. },
  1200. storage_config={'archive_name_format': 'Documents_{hostname}-{now}'},
  1201. )
  1202. def test_create_archive_with_extra_borg_options_calls_borg_with_extra_options():
  1203. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  1204. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  1205. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  1206. flexmock(module).should_receive('_expand_directories').and_return(())
  1207. flexmock(module).should_receive('_expand_home_directories').and_return(())
  1208. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  1209. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  1210. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  1211. flexmock(module).should_receive('execute_command').with_args(
  1212. ('borg', 'create', '--extra', '--options') + ARCHIVE_WITH_PATHS,
  1213. output_log_level=logging.INFO,
  1214. output_file=None,
  1215. borg_local_path='borg',
  1216. working_directory=None,
  1217. )
  1218. module.create_archive(
  1219. dry_run=False,
  1220. repository='repo',
  1221. location_config={
  1222. 'source_directories': ['foo', 'bar'],
  1223. 'repositories': ['repo'],
  1224. 'exclude_patterns': None,
  1225. },
  1226. storage_config={'extra_borg_options': {'create': '--extra --options'}},
  1227. )
  1228. def test_create_archive_with_stream_processes_calls_borg_with_processes():
  1229. processes = flexmock()
  1230. flexmock(module).should_receive('borgmatic_source_directories').and_return([])
  1231. flexmock(module).should_receive('deduplicate_directories').and_return(('foo', 'bar'))
  1232. flexmock(module).should_receive('map_directories_to_devices').and_return({})
  1233. flexmock(module).should_receive('_expand_directories').and_return(())
  1234. flexmock(module).should_receive('_expand_home_directories').and_return(())
  1235. flexmock(module).should_receive('_write_pattern_file').and_return(None)
  1236. flexmock(module).should_receive('_make_pattern_flags').and_return(())
  1237. flexmock(module).should_receive('_make_exclude_flags').and_return(())
  1238. flexmock(module).should_receive('execute_command_with_processes').with_args(
  1239. ('borg', 'create', '--one-file-system', '--read-special') + ARCHIVE_WITH_PATHS,
  1240. processes=processes,
  1241. output_log_level=logging.INFO,
  1242. output_file=None,
  1243. borg_local_path='borg',
  1244. working_directory=None,
  1245. )
  1246. module.create_archive(
  1247. dry_run=False,
  1248. repository='repo',
  1249. location_config={
  1250. 'source_directories': ['foo', 'bar'],
  1251. 'repositories': ['repo'],
  1252. 'exclude_patterns': None,
  1253. },
  1254. storage_config={},
  1255. stream_processes=processes,
  1256. )