test_create.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. import json
  2. import os
  3. import pytest
  4. from flexmock import flexmock
  5. from borgmatic.actions import create as module
  6. def test_run_create_executes_and_calls_hooks_for_configured_repository():
  7. flexmock(module.logger).answer = lambda message: None
  8. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').never()
  9. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  10. flexmock()
  11. )
  12. flexmock(module.borgmatic.borg.create).should_receive('create_archive').once()
  13. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').and_return({})
  14. flexmock(module.borgmatic.hooks.dispatch).should_receive(
  15. 'call_hooks_even_if_unconfigured'
  16. ).and_return({})
  17. flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
  18. flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
  19. flexmock(os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
  20. create_arguments = flexmock(
  21. repository=None,
  22. progress=flexmock(),
  23. statistics=flexmock(),
  24. json=False,
  25. list_details=flexmock(),
  26. )
  27. global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
  28. list(
  29. module.run_create(
  30. config_filename='test.yaml',
  31. repository={'path': 'repo'},
  32. config={},
  33. config_paths=['/tmp/test.yaml'],
  34. local_borg_version=None,
  35. create_arguments=create_arguments,
  36. global_arguments=global_arguments,
  37. dry_run_label='',
  38. local_path=None,
  39. remote_path=None,
  40. )
  41. )
  42. def test_run_create_runs_with_selected_repository():
  43. flexmock(module.logger).answer = lambda message: None
  44. flexmock(module.borgmatic.config.validate).should_receive(
  45. 'repositories_match'
  46. ).once().and_return(True)
  47. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  48. flexmock()
  49. )
  50. flexmock(module.borgmatic.borg.create).should_receive('create_archive').once()
  51. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').and_return({})
  52. flexmock(module.borgmatic.hooks.dispatch).should_receive(
  53. 'call_hooks_even_if_unconfigured'
  54. ).and_return({})
  55. flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
  56. flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
  57. flexmock(os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
  58. create_arguments = flexmock(
  59. repository=flexmock(),
  60. progress=flexmock(),
  61. statistics=flexmock(),
  62. json=False,
  63. list_details=flexmock(),
  64. )
  65. global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
  66. list(
  67. module.run_create(
  68. config_filename='test.yaml',
  69. repository={'path': 'repo'},
  70. config={},
  71. config_paths=['/tmp/test.yaml'],
  72. local_borg_version=None,
  73. create_arguments=create_arguments,
  74. global_arguments=global_arguments,
  75. dry_run_label='',
  76. local_path=None,
  77. remote_path=None,
  78. )
  79. )
  80. def test_run_create_bails_if_repository_does_not_match():
  81. flexmock(module.logger).answer = lambda message: None
  82. flexmock(module.borgmatic.config.validate).should_receive(
  83. 'repositories_match'
  84. ).once().and_return(False)
  85. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').never()
  86. flexmock(module.borgmatic.borg.create).should_receive('create_archive').never()
  87. create_arguments = flexmock(
  88. repository=flexmock(),
  89. progress=flexmock(),
  90. statistics=flexmock(),
  91. json=False,
  92. list_details=flexmock(),
  93. )
  94. global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
  95. list(
  96. module.run_create(
  97. config_filename='test.yaml',
  98. repository='repo',
  99. config={},
  100. config_paths=['/tmp/test.yaml'],
  101. local_borg_version=None,
  102. create_arguments=create_arguments,
  103. global_arguments=global_arguments,
  104. dry_run_label='',
  105. local_path=None,
  106. remote_path=None,
  107. )
  108. )
  109. def test_run_create_with_both_list_and_json_errors():
  110. flexmock(module.logger).answer = lambda message: None
  111. flexmock(module.borgmatic.config.validate).should_receive(
  112. 'repositories_match'
  113. ).once().and_return(True)
  114. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').never()
  115. flexmock(module.borgmatic.borg.create).should_receive('create_archive').never()
  116. create_arguments = flexmock(
  117. repository=flexmock(),
  118. progress=flexmock(),
  119. statistics=flexmock(),
  120. json=True,
  121. list_details=flexmock(),
  122. )
  123. global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
  124. with pytest.raises(ValueError):
  125. list(
  126. module.run_create(
  127. config_filename='test.yaml',
  128. repository={'path': 'repo'},
  129. config={'list_details': True},
  130. config_paths=['/tmp/test.yaml'],
  131. local_borg_version=None,
  132. create_arguments=create_arguments,
  133. global_arguments=global_arguments,
  134. dry_run_label='',
  135. local_path=None,
  136. remote_path=None,
  137. )
  138. )
  139. def test_run_create_with_both_list_and_progress_errors():
  140. flexmock(module.logger).answer = lambda message: None
  141. flexmock(module.borgmatic.config.validate).should_receive(
  142. 'repositories_match'
  143. ).once().and_return(True)
  144. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').never()
  145. flexmock(module.borgmatic.borg.create).should_receive('create_archive').never()
  146. create_arguments = flexmock(
  147. repository=flexmock(),
  148. progress=flexmock(),
  149. statistics=flexmock(),
  150. json=False,
  151. list_details=flexmock(),
  152. )
  153. global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
  154. with pytest.raises(ValueError):
  155. list(
  156. module.run_create(
  157. config_filename='test.yaml',
  158. repository={'path': 'repo'},
  159. config={'list_details': True, 'progress': True},
  160. config_paths=['/tmp/test.yaml'],
  161. local_borg_version=None,
  162. create_arguments=create_arguments,
  163. global_arguments=global_arguments,
  164. dry_run_label='',
  165. local_path=None,
  166. remote_path=None,
  167. )
  168. )
  169. def test_run_create_produces_json():
  170. flexmock(module.logger).answer = lambda message: None
  171. flexmock(module.borgmatic.config.validate).should_receive(
  172. 'repositories_match'
  173. ).once().and_return(True)
  174. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  175. flexmock()
  176. )
  177. flexmock(module.borgmatic.borg.create).should_receive('create_archive').once().and_return(
  178. flexmock()
  179. )
  180. parsed_json = flexmock()
  181. flexmock(module.borgmatic.actions.json).should_receive('parse_json').and_return(parsed_json)
  182. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').and_return({})
  183. flexmock(module.borgmatic.hooks.dispatch).should_receive(
  184. 'call_hooks_even_if_unconfigured'
  185. ).and_return({})
  186. flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
  187. flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
  188. flexmock(os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
  189. create_arguments = flexmock(
  190. repository=flexmock(),
  191. progress=flexmock(),
  192. statistics=flexmock(),
  193. json=True,
  194. list_details=flexmock(),
  195. )
  196. global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
  197. assert list(
  198. module.run_create(
  199. config_filename='test.yaml',
  200. repository={'path': 'repo'},
  201. config={},
  202. config_paths=['/tmp/test.yaml'],
  203. local_borg_version=None,
  204. create_arguments=create_arguments,
  205. global_arguments=global_arguments,
  206. dry_run_label='',
  207. local_path=None,
  208. remote_path=None,
  209. )
  210. ) == [parsed_json]
  211. def test_run_create_with_active_dumps_roundtrips_via_checkpoint_archive():
  212. mock_dump_process = flexmock()
  213. mock_dump_process.should_receive('poll').and_return(None).and_return(0)
  214. flexmock(module.logger).answer = lambda message: None
  215. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').never()
  216. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  217. flexmock()
  218. )
  219. flexmock(module.borgmatic.borg.create).should_receive('create_archive').once()
  220. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').and_return(
  221. {'dump': mock_dump_process}
  222. )
  223. flexmock(module.borgmatic.hooks.dispatch).should_receive(
  224. 'call_hooks_even_if_unconfigured'
  225. ).and_return({})
  226. flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
  227. flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
  228. flexmock(os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
  229. flexmock(module.borgmatic.borg.repo_list).should_receive('get_latest_archive').and_return(
  230. {'id': 'id1', 'name': 'archive.checkpoint'}
  231. )
  232. global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
  233. flexmock(module).should_receive('rename_checkpoint_archive').with_args(
  234. repository_path='repo',
  235. global_arguments=global_arguments,
  236. config={},
  237. local_borg_version=None,
  238. local_path=None,
  239. remote_path=None,
  240. ).once()
  241. create_arguments = flexmock(
  242. repository=None,
  243. progress=flexmock(),
  244. statistics=flexmock(),
  245. json=False,
  246. list_details=flexmock(),
  247. )
  248. list(
  249. module.run_create(
  250. config_filename='test.yaml',
  251. repository={'path': 'repo'},
  252. config={},
  253. config_paths=['/tmp/test.yaml'],
  254. local_borg_version=None,
  255. create_arguments=create_arguments,
  256. global_arguments=global_arguments,
  257. dry_run_label='',
  258. local_path=None,
  259. remote_path=None,
  260. )
  261. )
  262. def test_run_create_with_active_dumps_json_updates_archive_info():
  263. mock_dump_process = flexmock()
  264. mock_dump_process.should_receive('poll').and_return(None).and_return(0)
  265. borg_create_result = {
  266. 'archive': {
  267. 'command_line': ['foo'],
  268. 'name': 'archive.checkpoint',
  269. 'id': 'id1',
  270. },
  271. 'cache': {},
  272. 'repository': {
  273. 'id': 'repo-id',
  274. },
  275. }
  276. expected_create_result = {
  277. 'archive': {
  278. 'command_line': ['foo'],
  279. 'name': 'archive',
  280. 'id': 'id2',
  281. },
  282. 'cache': {},
  283. 'repository': {'id': 'repo-id', 'label': ''},
  284. }
  285. flexmock(module.logger).answer = lambda message: None
  286. flexmock(module.borgmatic.config.validate).should_receive('repositories_match').never()
  287. flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
  288. flexmock()
  289. )
  290. flexmock(module.borgmatic.borg.create).should_receive('create_archive').and_return(
  291. json.dumps(borg_create_result)
  292. ).once()
  293. flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').and_return(
  294. {'dump': mock_dump_process}
  295. )
  296. flexmock(module.borgmatic.hooks.dispatch).should_receive(
  297. 'call_hooks_even_if_unconfigured'
  298. ).and_return({})
  299. flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(())
  300. flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').and_return([])
  301. flexmock(os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
  302. global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
  303. flexmock(module).should_receive('rename_checkpoint_archive').with_args(
  304. repository_path='repo',
  305. global_arguments=global_arguments,
  306. config={},
  307. local_borg_version=None,
  308. local_path=None,
  309. remote_path=None,
  310. ).once()
  311. flexmock(module.borgmatic.borg.repo_list).should_receive('get_latest_archive').and_return(
  312. {'id': 'id2', 'name': 'archive'},
  313. )
  314. create_arguments = flexmock(
  315. repository=None,
  316. progress=flexmock(),
  317. statistics=flexmock(),
  318. json=True,
  319. list_details=flexmock(),
  320. )
  321. assert list(
  322. module.run_create(
  323. config_filename='test.yaml',
  324. repository={'path': 'repo'},
  325. config={},
  326. config_paths=['/tmp/test.yaml'],
  327. local_borg_version=None,
  328. create_arguments=create_arguments,
  329. global_arguments=global_arguments,
  330. dry_run_label='',
  331. local_path=None,
  332. remote_path=None,
  333. )
  334. ) == [expected_create_result]
  335. def test_rename_checkpoint_archive_renames_archive():
  336. global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
  337. flexmock(module.borgmatic.borg.repo_list).should_receive('get_latest_archive').and_return(
  338. {'id': 'id1', 'name': 'archive.checkpoint'}
  339. )
  340. flexmock(module.borgmatic.borg.rename).should_receive('rename_archive').with_args(
  341. repository_name='path',
  342. old_archive_name='archive.checkpoint',
  343. new_archive_name='archive',
  344. dry_run=False,
  345. config={},
  346. local_borg_version=None,
  347. local_path=None,
  348. remote_path=None,
  349. )
  350. module.rename_checkpoint_archive(
  351. repository_path='path',
  352. global_arguments=global_arguments,
  353. config={},
  354. local_borg_version=None,
  355. local_path=None,
  356. remote_path=None,
  357. )
  358. def test_rename_checkpoint_archive_checks_suffix():
  359. global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
  360. flexmock(module.borgmatic.borg.repo_list).should_receive('get_latest_archive').and_return(
  361. {'id': 'id1', 'name': 'unexpected-archive'}
  362. )
  363. with pytest.raises(
  364. ValueError,
  365. match='Latest archive did not have a .checkpoint suffix. Got: unexpected-archive',
  366. ):
  367. module.rename_checkpoint_archive(
  368. repository_path='path',
  369. global_arguments=global_arguments,
  370. config={},
  371. local_borg_version=None,
  372. local_path=None,
  373. remote_path=None,
  374. )