test_rcreate.py 14 KB

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