test_create.py 16 KB

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