test_create.py 15 KB

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