2
0

test_repo_create.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. import logging
  2. import subprocess
  3. import pytest
  4. from flexmock import flexmock
  5. from borgmatic.borg import repo_create as module
  6. from ..test_verbosity import insert_logging_mock
  7. REPO_INFO_SOME_UNKNOWN_EXIT_CODE = -999
  8. REPO_CREATE_COMMAND = ('borg', 'repo-create', '--encryption', 'repokey')
  9. def insert_repo_info_command_found_mock():
  10. flexmock(module.repo_info).should_receive('display_repository_info').and_return(
  11. '{"encryption": {"mode": "repokey"}}'
  12. )
  13. def insert_repo_info_command_not_found_mock():
  14. flexmock(module.repo_info).should_receive('display_repository_info').and_raise(
  15. subprocess.CalledProcessError(
  16. sorted(module.REPO_INFO_REPOSITORY_NOT_FOUND_EXIT_CODES)[0], []
  17. )
  18. )
  19. def insert_repo_create_command_mock(
  20. repo_create_command, working_directory=None, borg_exit_codes=None, **kwargs
  21. ):
  22. flexmock(module.environment).should_receive('make_environment')
  23. flexmock(module.borgmatic.config.options).should_receive('get_working_directory').and_return(
  24. working_directory,
  25. )
  26. flexmock(module).should_receive('execute_command').with_args(
  27. repo_create_command,
  28. output_file=module.DO_NOT_CAPTURE,
  29. extra_environment=None,
  30. working_directory=working_directory,
  31. borg_local_path=repo_create_command[0],
  32. borg_exit_codes=borg_exit_codes,
  33. ).once()
  34. def test_create_repository_calls_borg_with_flags():
  35. insert_repo_info_command_not_found_mock()
  36. insert_repo_create_command_mock(REPO_CREATE_COMMAND + ('--repo', 'repo'))
  37. flexmock(module.feature).should_receive('available').and_return(True)
  38. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  39. (
  40. '--repo',
  41. 'repo',
  42. )
  43. )
  44. module.create_repository(
  45. dry_run=False,
  46. repository_path='repo',
  47. config={},
  48. local_borg_version='2.3.4',
  49. global_arguments=flexmock(log_json=False),
  50. encryption_mode='repokey',
  51. )
  52. def test_create_repository_with_dry_run_skips_borg_call():
  53. insert_repo_info_command_not_found_mock()
  54. flexmock(module).should_receive('execute_command').never()
  55. flexmock(module.feature).should_receive('available').and_return(True)
  56. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  57. (
  58. '--repo',
  59. 'repo',
  60. )
  61. )
  62. module.create_repository(
  63. dry_run=True,
  64. repository_path='repo',
  65. config={},
  66. local_borg_version='2.3.4',
  67. global_arguments=flexmock(log_json=False),
  68. encryption_mode='repokey',
  69. )
  70. def test_create_repository_raises_for_borg_repo_create_error():
  71. insert_repo_info_command_not_found_mock()
  72. flexmock(module.feature).should_receive('available').and_return(True)
  73. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  74. (
  75. '--repo',
  76. 'repo',
  77. )
  78. )
  79. flexmock(module.environment).should_receive('make_environment')
  80. flexmock(module.borgmatic.config.options).should_receive('get_working_directory').and_return(
  81. None
  82. )
  83. flexmock(module).should_receive('execute_command').and_raise(
  84. module.subprocess.CalledProcessError(2, 'borg repo_create')
  85. )
  86. with pytest.raises(subprocess.CalledProcessError):
  87. module.create_repository(
  88. dry_run=False,
  89. repository_path='repo',
  90. config={},
  91. local_borg_version='2.3.4',
  92. global_arguments=flexmock(log_json=False),
  93. encryption_mode='repokey',
  94. )
  95. def test_create_repository_skips_creation_when_repository_already_exists():
  96. insert_repo_info_command_found_mock()
  97. flexmock(module.feature).should_receive('available').and_return(True)
  98. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  99. (
  100. '--repo',
  101. 'repo',
  102. )
  103. )
  104. module.create_repository(
  105. dry_run=False,
  106. repository_path='repo',
  107. config={},
  108. local_borg_version='2.3.4',
  109. global_arguments=flexmock(log_json=False),
  110. encryption_mode='repokey',
  111. )
  112. def test_create_repository_errors_when_repository_with_differing_encryption_mode_already_exists():
  113. insert_repo_info_command_found_mock()
  114. flexmock(module.feature).should_receive('available').and_return(True)
  115. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  116. (
  117. '--repo',
  118. 'repo',
  119. )
  120. )
  121. with pytest.raises(ValueError):
  122. module.create_repository(
  123. dry_run=False,
  124. repository_path='repo',
  125. config={},
  126. local_borg_version='2.3.4',
  127. global_arguments=flexmock(log_json=False),
  128. encryption_mode='repokey-blake2',
  129. )
  130. def test_create_repository_raises_for_unknown_repo_info_command_error():
  131. flexmock(module.repo_info).should_receive('display_repository_info').and_raise(
  132. subprocess.CalledProcessError(REPO_INFO_SOME_UNKNOWN_EXIT_CODE, [])
  133. )
  134. with pytest.raises(subprocess.CalledProcessError):
  135. module.create_repository(
  136. dry_run=False,
  137. repository_path='repo',
  138. config={},
  139. local_borg_version='2.3.4',
  140. global_arguments=flexmock(log_json=False),
  141. encryption_mode='repokey',
  142. )
  143. def test_create_repository_with_source_repository_calls_borg_with_other_repo_flag():
  144. insert_repo_info_command_not_found_mock()
  145. insert_repo_create_command_mock(
  146. REPO_CREATE_COMMAND + ('--other-repo', 'other.borg', '--repo', 'repo')
  147. )
  148. flexmock(module.feature).should_receive('available').and_return(True)
  149. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  150. (
  151. '--repo',
  152. 'repo',
  153. )
  154. )
  155. module.create_repository(
  156. dry_run=False,
  157. repository_path='repo',
  158. config={},
  159. local_borg_version='2.3.4',
  160. global_arguments=flexmock(log_json=False),
  161. encryption_mode='repokey',
  162. source_repository='other.borg',
  163. )
  164. def test_create_repository_with_copy_crypt_key_calls_borg_with_copy_crypt_key_flag():
  165. insert_repo_info_command_not_found_mock()
  166. insert_repo_create_command_mock(REPO_CREATE_COMMAND + ('--copy-crypt-key', '--repo', 'repo'))
  167. flexmock(module.feature).should_receive('available').and_return(True)
  168. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  169. (
  170. '--repo',
  171. 'repo',
  172. )
  173. )
  174. module.create_repository(
  175. dry_run=False,
  176. repository_path='repo',
  177. config={},
  178. local_borg_version='2.3.4',
  179. global_arguments=flexmock(log_json=False),
  180. encryption_mode='repokey',
  181. copy_crypt_key=True,
  182. )
  183. def test_create_repository_with_append_only_calls_borg_with_append_only_flag():
  184. insert_repo_info_command_not_found_mock()
  185. insert_repo_create_command_mock(REPO_CREATE_COMMAND + ('--append-only', '--repo', 'repo'))
  186. flexmock(module.feature).should_receive('available').and_return(True)
  187. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  188. (
  189. '--repo',
  190. 'repo',
  191. )
  192. )
  193. module.create_repository(
  194. dry_run=False,
  195. repository_path='repo',
  196. config={},
  197. local_borg_version='2.3.4',
  198. global_arguments=flexmock(log_json=False),
  199. encryption_mode='repokey',
  200. append_only=True,
  201. )
  202. def test_create_repository_with_storage_quota_calls_borg_with_storage_quota_flag():
  203. insert_repo_info_command_not_found_mock()
  204. insert_repo_create_command_mock(
  205. REPO_CREATE_COMMAND + ('--storage-quota', '5G', '--repo', 'repo')
  206. )
  207. flexmock(module.feature).should_receive('available').and_return(True)
  208. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  209. (
  210. '--repo',
  211. 'repo',
  212. )
  213. )
  214. module.create_repository(
  215. dry_run=False,
  216. repository_path='repo',
  217. config={},
  218. local_borg_version='2.3.4',
  219. global_arguments=flexmock(log_json=False),
  220. encryption_mode='repokey',
  221. storage_quota='5G',
  222. )
  223. def test_create_repository_with_make_parent_dirs_calls_borg_with_make_parent_dirs_flag():
  224. insert_repo_info_command_not_found_mock()
  225. insert_repo_create_command_mock(REPO_CREATE_COMMAND + ('--make-parent-dirs', '--repo', 'repo'))
  226. flexmock(module.feature).should_receive('available').and_return(True)
  227. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  228. (
  229. '--repo',
  230. 'repo',
  231. )
  232. )
  233. module.create_repository(
  234. dry_run=False,
  235. repository_path='repo',
  236. config={},
  237. local_borg_version='2.3.4',
  238. global_arguments=flexmock(log_json=False),
  239. encryption_mode='repokey',
  240. make_parent_dirs=True,
  241. )
  242. def test_create_repository_with_log_info_calls_borg_with_info_flag():
  243. insert_repo_info_command_not_found_mock()
  244. insert_repo_create_command_mock(REPO_CREATE_COMMAND + ('--info', '--repo', 'repo'))
  245. insert_logging_mock(logging.INFO)
  246. flexmock(module.feature).should_receive('available').and_return(True)
  247. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  248. (
  249. '--repo',
  250. 'repo',
  251. )
  252. )
  253. module.create_repository(
  254. dry_run=False,
  255. repository_path='repo',
  256. config={},
  257. local_borg_version='2.3.4',
  258. global_arguments=flexmock(log_json=False),
  259. encryption_mode='repokey',
  260. )
  261. def test_create_repository_with_log_debug_calls_borg_with_debug_flag():
  262. insert_repo_info_command_not_found_mock()
  263. insert_repo_create_command_mock(REPO_CREATE_COMMAND + ('--debug', '--repo', 'repo'))
  264. insert_logging_mock(logging.DEBUG)
  265. flexmock(module.feature).should_receive('available').and_return(True)
  266. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  267. (
  268. '--repo',
  269. 'repo',
  270. )
  271. )
  272. module.create_repository(
  273. dry_run=False,
  274. repository_path='repo',
  275. config={},
  276. local_borg_version='2.3.4',
  277. global_arguments=flexmock(log_json=False),
  278. encryption_mode='repokey',
  279. )
  280. def test_create_repository_with_log_json_calls_borg_with_log_json_flag():
  281. insert_repo_info_command_not_found_mock()
  282. insert_repo_create_command_mock(REPO_CREATE_COMMAND + ('--log-json', '--repo', 'repo'))
  283. flexmock(module.feature).should_receive('available').and_return(True)
  284. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  285. (
  286. '--repo',
  287. 'repo',
  288. )
  289. )
  290. module.create_repository(
  291. dry_run=False,
  292. repository_path='repo',
  293. config={},
  294. local_borg_version='2.3.4',
  295. global_arguments=flexmock(log_json=True),
  296. encryption_mode='repokey',
  297. )
  298. def test_create_repository_with_lock_wait_calls_borg_with_lock_wait_flag():
  299. insert_repo_info_command_not_found_mock()
  300. insert_repo_create_command_mock(REPO_CREATE_COMMAND + ('--lock-wait', '5', '--repo', 'repo'))
  301. flexmock(module.feature).should_receive('available').and_return(True)
  302. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  303. (
  304. '--repo',
  305. 'repo',
  306. )
  307. )
  308. module.create_repository(
  309. dry_run=False,
  310. repository_path='repo',
  311. config={'lock_wait': 5},
  312. local_borg_version='2.3.4',
  313. global_arguments=flexmock(log_json=False),
  314. encryption_mode='repokey',
  315. )
  316. def test_create_repository_with_local_path_calls_borg_via_local_path():
  317. insert_repo_info_command_not_found_mock()
  318. insert_repo_create_command_mock(('borg1',) + REPO_CREATE_COMMAND[1:] + ('--repo', 'repo'))
  319. flexmock(module.feature).should_receive('available').and_return(True)
  320. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  321. (
  322. '--repo',
  323. 'repo',
  324. )
  325. )
  326. module.create_repository(
  327. dry_run=False,
  328. repository_path='repo',
  329. config={},
  330. local_borg_version='2.3.4',
  331. global_arguments=flexmock(log_json=False),
  332. encryption_mode='repokey',
  333. local_path='borg1',
  334. )
  335. def test_create_repository_with_exit_codes_calls_borg_using_them():
  336. borg_exit_codes = flexmock()
  337. insert_repo_info_command_not_found_mock()
  338. insert_repo_create_command_mock(
  339. ('borg',) + REPO_CREATE_COMMAND[1:] + ('--repo', 'repo'), borg_exit_codes=borg_exit_codes
  340. )
  341. flexmock(module.feature).should_receive('available').and_return(True)
  342. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  343. (
  344. '--repo',
  345. 'repo',
  346. )
  347. )
  348. module.create_repository(
  349. dry_run=False,
  350. repository_path='repo',
  351. config={'borg_exit_codes': borg_exit_codes},
  352. local_borg_version='2.3.4',
  353. global_arguments=flexmock(log_json=False),
  354. encryption_mode='repokey',
  355. )
  356. def test_create_repository_with_remote_path_calls_borg_with_remote_path_flag():
  357. insert_repo_info_command_not_found_mock()
  358. insert_repo_create_command_mock(
  359. REPO_CREATE_COMMAND + ('--remote-path', 'borg1', '--repo', 'repo')
  360. )
  361. flexmock(module.feature).should_receive('available').and_return(True)
  362. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  363. (
  364. '--repo',
  365. 'repo',
  366. )
  367. )
  368. module.create_repository(
  369. dry_run=False,
  370. repository_path='repo',
  371. config={},
  372. local_borg_version='2.3.4',
  373. global_arguments=flexmock(log_json=False),
  374. encryption_mode='repokey',
  375. remote_path='borg1',
  376. )
  377. def test_create_repository_with_extra_borg_options_calls_borg_with_extra_options():
  378. insert_repo_info_command_not_found_mock()
  379. insert_repo_create_command_mock(
  380. REPO_CREATE_COMMAND + ('--extra', '--options', '--repo', 'repo')
  381. )
  382. flexmock(module.feature).should_receive('available').and_return(True)
  383. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  384. (
  385. '--repo',
  386. 'repo',
  387. )
  388. )
  389. module.create_repository(
  390. dry_run=False,
  391. repository_path='repo',
  392. config={'extra_borg_options': {'repo-create': '--extra --options'}},
  393. local_borg_version='2.3.4',
  394. global_arguments=flexmock(log_json=False),
  395. encryption_mode='repokey',
  396. )
  397. def test_create_repository_calls_borg_with_working_directory():
  398. insert_repo_info_command_not_found_mock()
  399. insert_repo_create_command_mock(
  400. REPO_CREATE_COMMAND + ('--repo', 'repo'), working_directory='/working/dir'
  401. )
  402. flexmock(module.feature).should_receive('available').and_return(True)
  403. flexmock(module.flags).should_receive('make_repository_flags').and_return(
  404. (
  405. '--repo',
  406. 'repo',
  407. )
  408. )
  409. module.create_repository(
  410. dry_run=False,
  411. repository_path='repo',
  412. config={'working_directory': '/working/dir'},
  413. local_borg_version='2.3.4',
  414. global_arguments=flexmock(log_json=False),
  415. encryption_mode='repokey',
  416. )