test_btrfs.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. import pytest
  2. from flexmock import flexmock
  3. from borgmatic.hooks.data_source import btrfs as module
  4. def test_get_filesystem_mount_points_parses_findmnt_output():
  5. flexmock(module.borgmatic.execute).should_receive(
  6. 'execute_command_and_capture_output'
  7. ).and_return(
  8. '/mnt0 /dev/loop0 btrfs rw,relatime,ssd,space_cache=v2,subvolid=5,subvol=/\n'
  9. '/mnt1 /dev/loop1 btrfs rw,relatime,ssd,space_cache=v2,subvolid=5,subvol=/\n'
  10. )
  11. assert module.get_filesystem_mount_points('findmnt') == ('/mnt0', '/mnt1')
  12. def test_get_subvolumes_for_filesystem_parses_subvolume_list_output():
  13. flexmock(module.borgmatic.execute).should_receive(
  14. 'execute_command_and_capture_output'
  15. ).and_return(
  16. 'ID 270 gen 107 top level 5 path subvol1\n' 'ID 272 gen 74 top level 5 path subvol2\n'
  17. )
  18. assert module.get_subvolumes_for_filesystem('btrfs', '/mnt') == ('/mnt/subvol1', '/mnt/subvol2')
  19. def test_get_subvolumes_collects_subvolumes_matching_source_directories_from_all_filesystems():
  20. flexmock(module).should_receive('get_filesystem_mount_points').and_return(('/mnt1', '/mnt2'))
  21. flexmock(module).should_receive('get_subvolumes_for_filesystem').with_args(
  22. 'btrfs', '/mnt1'
  23. ).and_return(('/one', '/two'))
  24. flexmock(module).should_receive('get_subvolumes_for_filesystem').with_args(
  25. 'btrfs', '/mnt2'
  26. ).and_return(('/three', '/four'))
  27. assert module.get_subvolumes(
  28. 'btrfs', 'findmnt', source_directories=['/one', '/four', '/five', '/six', '/mnt2', '/mnt3']
  29. ) == ('/one', '/mnt2', '/four')
  30. def test_get_subvolumes_without_source_directories_collects_all_subvolumes_from_all_filesystems():
  31. flexmock(module).should_receive('get_filesystem_mount_points').and_return(('/mnt1', '/mnt2'))
  32. flexmock(module).should_receive('get_subvolumes_for_filesystem').with_args(
  33. 'btrfs', '/mnt1'
  34. ).and_return(('/one', '/two'))
  35. flexmock(module).should_receive('get_subvolumes_for_filesystem').with_args(
  36. 'btrfs', '/mnt2'
  37. ).and_return(('/three', '/four'))
  38. assert module.get_subvolumes('btrfs', 'findmnt') == (
  39. '/mnt1',
  40. '/one',
  41. '/two',
  42. '/mnt2',
  43. '/three',
  44. '/four',
  45. )
  46. def test_dump_data_sources_snapshots_each_subvolume_and_updates_source_directories():
  47. source_directories = ['/foo', '/mnt/subvol1']
  48. config = {'btrfs': {}}
  49. flexmock(module).should_receive('get_subvolumes').and_return(('/mnt/subvol1', '/mnt/subvol2'))
  50. flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
  51. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1'
  52. )
  53. flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol2').and_return(
  54. '/mnt/subvol2/.borgmatic-1234/mnt/subvol2'
  55. )
  56. flexmock(module).should_receive('snapshot_subvolume').with_args(
  57. 'btrfs', '/mnt/subvol1', '/mnt/subvol1/.borgmatic-1234/mnt/subvol1'
  58. ).once()
  59. flexmock(module).should_receive('snapshot_subvolume').with_args(
  60. 'btrfs', '/mnt/subvol2', '/mnt/subvol2/.borgmatic-1234/mnt/subvol2'
  61. ).once()
  62. flexmock(module).should_receive('make_snapshot_exclude_path').with_args(
  63. '/mnt/subvol1'
  64. ).and_return('/mnt/subvol1/.borgmatic-1234/mnt/subvol1/.borgmatic-1234')
  65. flexmock(module).should_receive('make_snapshot_exclude_path').with_args(
  66. '/mnt/subvol2'
  67. ).and_return('/mnt/subvol2/.borgmatic-1234/mnt/subvol2/.borgmatic-1234')
  68. assert (
  69. module.dump_data_sources(
  70. hook_config=config['btrfs'],
  71. config=config,
  72. log_prefix='test',
  73. config_paths=('test.yaml',),
  74. borgmatic_runtime_directory='/run/borgmatic',
  75. source_directories=source_directories,
  76. dry_run=False,
  77. )
  78. == []
  79. )
  80. assert source_directories == [
  81. '/foo',
  82. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1',
  83. '/mnt/subvol2/.borgmatic-1234/mnt/subvol2',
  84. ]
  85. assert config == {
  86. 'btrfs': {},
  87. 'exclude_patterns': [
  88. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1/.borgmatic-1234',
  89. '/mnt/subvol2/.borgmatic-1234/mnt/subvol2/.borgmatic-1234',
  90. ],
  91. }
  92. def test_dump_data_sources_uses_custom_btrfs_command_in_commands():
  93. source_directories = ['/foo', '/mnt/subvol1']
  94. config = {'btrfs': {'btrfs_command': '/usr/local/bin/btrfs'}}
  95. flexmock(module).should_receive('get_subvolumes').and_return(('/mnt/subvol1',))
  96. flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
  97. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1'
  98. )
  99. flexmock(module).should_receive('snapshot_subvolume').with_args(
  100. '/usr/local/bin/btrfs', '/mnt/subvol1', '/mnt/subvol1/.borgmatic-1234/mnt/subvol1'
  101. ).once()
  102. flexmock(module).should_receive('make_snapshot_exclude_path').with_args(
  103. '/mnt/subvol1'
  104. ).and_return('/mnt/subvol1/.borgmatic-1234/mnt/subvol1/.borgmatic-1234')
  105. assert (
  106. module.dump_data_sources(
  107. hook_config=config['btrfs'],
  108. config=config,
  109. log_prefix='test',
  110. config_paths=('test.yaml',),
  111. borgmatic_runtime_directory='/run/borgmatic',
  112. source_directories=source_directories,
  113. dry_run=False,
  114. )
  115. == []
  116. )
  117. assert source_directories == [
  118. '/foo',
  119. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1',
  120. ]
  121. assert config == {
  122. 'btrfs': {
  123. 'btrfs_command': '/usr/local/bin/btrfs',
  124. },
  125. 'exclude_patterns': [
  126. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1/.borgmatic-1234',
  127. ],
  128. }
  129. def test_dump_data_sources_uses_custom_findmnt_command_in_commands():
  130. source_directories = ['/foo', '/mnt/subvol1']
  131. config = {'btrfs': {'findmnt_command': '/usr/local/bin/findmnt'}}
  132. flexmock(module).should_receive('get_subvolumes').with_args(
  133. 'btrfs', '/usr/local/bin/findmnt', source_directories
  134. ).and_return(('/mnt/subvol1',)).once()
  135. flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
  136. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1'
  137. )
  138. flexmock(module).should_receive('snapshot_subvolume').with_args(
  139. 'btrfs', '/mnt/subvol1', '/mnt/subvol1/.borgmatic-1234/mnt/subvol1'
  140. ).once()
  141. flexmock(module).should_receive('make_snapshot_exclude_path').with_args(
  142. '/mnt/subvol1'
  143. ).and_return('/mnt/subvol1/.borgmatic-1234/mnt/subvol1/.borgmatic-1234')
  144. assert (
  145. module.dump_data_sources(
  146. hook_config=config['btrfs'],
  147. config=config,
  148. log_prefix='test',
  149. config_paths=('test.yaml',),
  150. borgmatic_runtime_directory='/run/borgmatic',
  151. source_directories=source_directories,
  152. dry_run=False,
  153. )
  154. == []
  155. )
  156. assert source_directories == [
  157. '/foo',
  158. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1',
  159. ]
  160. assert config == {
  161. 'btrfs': {
  162. 'findmnt_command': '/usr/local/bin/findmnt',
  163. },
  164. 'exclude_patterns': [
  165. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1/.borgmatic-1234',
  166. ],
  167. }
  168. def test_dump_data_sources_with_dry_run_skips_snapshot_and_source_directories_update():
  169. source_directories = ['/foo', '/mnt/subvol1']
  170. config = {'btrfs': {}}
  171. flexmock(module).should_receive('get_subvolumes').and_return(('/mnt/subvol1',))
  172. flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
  173. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1'
  174. )
  175. flexmock(module).should_receive('snapshot_subvolume').never()
  176. flexmock(module).should_receive('make_snapshot_exclude_path').never()
  177. assert (
  178. module.dump_data_sources(
  179. hook_config=config['btrfs'],
  180. config=config,
  181. log_prefix='test',
  182. config_paths=('test.yaml',),
  183. borgmatic_runtime_directory='/run/borgmatic',
  184. source_directories=source_directories,
  185. dry_run=True,
  186. )
  187. == []
  188. )
  189. assert source_directories == ['/foo', '/mnt/subvol1']
  190. assert config == {'btrfs': {}}
  191. def test_dump_data_sources_without_matching_subvolumes_skips_snapshot_and_source_directories_update():
  192. source_directories = ['/foo', '/mnt/subvol1']
  193. config = {'btrfs': {}}
  194. flexmock(module).should_receive('get_subvolumes').and_return(())
  195. flexmock(module).should_receive('make_snapshot_path').never()
  196. flexmock(module).should_receive('snapshot_subvolume').never()
  197. flexmock(module).should_receive('make_snapshot_exclude_path').never()
  198. assert (
  199. module.dump_data_sources(
  200. hook_config=config['btrfs'],
  201. config=config,
  202. log_prefix='test',
  203. config_paths=('test.yaml',),
  204. borgmatic_runtime_directory='/run/borgmatic',
  205. source_directories=source_directories,
  206. dry_run=False,
  207. )
  208. == []
  209. )
  210. assert source_directories == ['/foo', '/mnt/subvol1']
  211. assert config == {'btrfs': {}}
  212. def test_remove_data_source_dumps_deletes_snapshots():
  213. config = {'btrfs': {}}
  214. flexmock(module).should_receive('get_subvolumes').and_return(('/mnt/subvol1', '/mnt/subvol2'))
  215. flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
  216. '/mnt/subvol1/.borgmatic-1234/./mnt/subvol1'
  217. )
  218. flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol2').and_return(
  219. '/mnt/subvol2/.borgmatic-1234/./mnt/subvol2'
  220. )
  221. flexmock(module.borgmatic.config.paths).should_receive(
  222. 'replace_temporary_subdirectory_with_glob'
  223. ).with_args(
  224. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1',
  225. temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
  226. ).and_return(
  227. '/mnt/subvol1/.borgmatic-*/mnt/subvol1'
  228. )
  229. flexmock(module.borgmatic.config.paths).should_receive(
  230. 'replace_temporary_subdirectory_with_glob'
  231. ).with_args(
  232. '/mnt/subvol2/.borgmatic-1234/mnt/subvol2',
  233. temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
  234. ).and_return(
  235. '/mnt/subvol2/.borgmatic-*/mnt/subvol2'
  236. )
  237. flexmock(module.glob).should_receive('glob').with_args(
  238. '/mnt/subvol1/.borgmatic-*/mnt/subvol1'
  239. ).and_return(
  240. ('/mnt/subvol1/.borgmatic-1234/mnt/subvol1', '/mnt/subvol1/.borgmatic-5678/mnt/subvol1')
  241. )
  242. flexmock(module.glob).should_receive('glob').with_args(
  243. '/mnt/subvol2/.borgmatic-*/mnt/subvol2'
  244. ).and_return(
  245. ('/mnt/subvol2/.borgmatic-1234/mnt/subvol2', '/mnt/subvol2/.borgmatic-5678/mnt/subvol2')
  246. )
  247. flexmock(module.os.path).should_receive('isdir').with_args(
  248. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1'
  249. ).and_return(True)
  250. flexmock(module.os.path).should_receive('isdir').with_args(
  251. '/mnt/subvol1/.borgmatic-5678/mnt/subvol1'
  252. ).and_return(True)
  253. flexmock(module.os.path).should_receive('isdir').with_args(
  254. '/mnt/subvol2/.borgmatic-1234/mnt/subvol2'
  255. ).and_return(True)
  256. flexmock(module.os.path).should_receive('isdir').with_args(
  257. '/mnt/subvol2/.borgmatic-5678/mnt/subvol2'
  258. ).and_return(False)
  259. flexmock(module).should_receive('delete_snapshot').with_args(
  260. 'btrfs', '/mnt/subvol1/.borgmatic-1234/mnt/subvol1'
  261. ).once()
  262. flexmock(module).should_receive('delete_snapshot').with_args(
  263. 'btrfs', '/mnt/subvol1/.borgmatic-5678/mnt/subvol1'
  264. ).once()
  265. flexmock(module).should_receive('delete_snapshot').with_args(
  266. 'btrfs', '/mnt/subvol2/.borgmatic-1234/mnt/subvol2'
  267. ).once()
  268. flexmock(module).should_receive('delete_snapshot').with_args(
  269. 'btrfs', '/mnt/subvol2/.borgmatic-5678/mnt/subvol2'
  270. ).never()
  271. flexmock(module.shutil).should_receive('rmtree').with_args(
  272. '/mnt/subvol1/.borgmatic-1234'
  273. ).once()
  274. flexmock(module.shutil).should_receive('rmtree').with_args(
  275. '/mnt/subvol1/.borgmatic-5678'
  276. ).once()
  277. flexmock(module.shutil).should_receive('rmtree').with_args(
  278. '/mnt/subvol2/.borgmatic-1234'
  279. ).once()
  280. flexmock(module.shutil).should_receive('rmtree').with_args(
  281. '/mnt/subvol2/.borgmatic-5678'
  282. ).never()
  283. module.remove_data_source_dumps(
  284. hook_config=config['btrfs'],
  285. config=config,
  286. log_prefix='test',
  287. borgmatic_runtime_directory='/run/borgmatic',
  288. dry_run=False,
  289. )
  290. def test_remove_data_source_dumps_with_get_subvolumes_file_not_found_error_bails():
  291. config = {'btrfs': {}}
  292. flexmock(module).should_receive('get_subvolumes').and_raise(FileNotFoundError)
  293. flexmock(module).should_receive('make_snapshot_path').never()
  294. flexmock(module.borgmatic.config.paths).should_receive(
  295. 'replace_temporary_subdirectory_with_glob'
  296. ).never()
  297. flexmock(module).should_receive('delete_snapshot').never()
  298. flexmock(module.shutil).should_receive('rmtree').never()
  299. module.remove_data_source_dumps(
  300. hook_config=config['btrfs'],
  301. config=config,
  302. log_prefix='test',
  303. borgmatic_runtime_directory='/run/borgmatic',
  304. dry_run=False,
  305. )
  306. def test_remove_data_source_dumps_with_get_subvolumes_called_process_error_bails():
  307. config = {'btrfs': {}}
  308. flexmock(module).should_receive('get_subvolumes').and_raise(
  309. module.subprocess.CalledProcessError(1, 'command', 'error')
  310. )
  311. flexmock(module).should_receive('make_snapshot_path').never()
  312. flexmock(module.borgmatic.config.paths).should_receive(
  313. 'replace_temporary_subdirectory_with_glob'
  314. ).never()
  315. flexmock(module).should_receive('delete_snapshot').never()
  316. flexmock(module.shutil).should_receive('rmtree').never()
  317. module.remove_data_source_dumps(
  318. hook_config=config['btrfs'],
  319. config=config,
  320. log_prefix='test',
  321. borgmatic_runtime_directory='/run/borgmatic',
  322. dry_run=False,
  323. )
  324. def test_remove_data_source_dumps_with_dry_run_skips_deletes():
  325. config = {'btrfs': {}}
  326. flexmock(module).should_receive('get_subvolumes').and_return(('/mnt/subvol1', '/mnt/subvol2'))
  327. flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
  328. '/mnt/subvol1/.borgmatic-1234/./mnt/subvol1'
  329. )
  330. flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol2').and_return(
  331. '/mnt/subvol2/.borgmatic-1234/./mnt/subvol2'
  332. )
  333. flexmock(module.borgmatic.config.paths).should_receive(
  334. 'replace_temporary_subdirectory_with_glob'
  335. ).with_args(
  336. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1',
  337. temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
  338. ).and_return(
  339. '/mnt/subvol1/.borgmatic-*/mnt/subvol1'
  340. )
  341. flexmock(module.borgmatic.config.paths).should_receive(
  342. 'replace_temporary_subdirectory_with_glob'
  343. ).with_args(
  344. '/mnt/subvol2/.borgmatic-1234/mnt/subvol2',
  345. temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
  346. ).and_return(
  347. '/mnt/subvol2/.borgmatic-*/mnt/subvol2'
  348. )
  349. flexmock(module.glob).should_receive('glob').with_args(
  350. '/mnt/subvol1/.borgmatic-*/mnt/subvol1'
  351. ).and_return(
  352. ('/mnt/subvol1/.borgmatic-1234/mnt/subvol1', '/mnt/subvol1/.borgmatic-5678/mnt/subvol1')
  353. )
  354. flexmock(module.glob).should_receive('glob').with_args(
  355. '/mnt/subvol2/.borgmatic-*/mnt/subvol2'
  356. ).and_return(
  357. ('/mnt/subvol2/.borgmatic-1234/mnt/subvol2', '/mnt/subvol2/.borgmatic-5678/mnt/subvol2')
  358. )
  359. flexmock(module.os.path).should_receive('isdir').with_args(
  360. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1'
  361. ).and_return(True)
  362. flexmock(module.os.path).should_receive('isdir').with_args(
  363. '/mnt/subvol1/.borgmatic-5678/mnt/subvol1'
  364. ).and_return(True)
  365. flexmock(module.os.path).should_receive('isdir').with_args(
  366. '/mnt/subvol2/.borgmatic-1234/mnt/subvol2'
  367. ).and_return(True)
  368. flexmock(module.os.path).should_receive('isdir').with_args(
  369. '/mnt/subvol2/.borgmatic-5678/mnt/subvol2'
  370. ).and_return(False)
  371. flexmock(module).should_receive('delete_snapshot').never()
  372. flexmock(module.shutil).should_receive('rmtree').never()
  373. module.remove_data_source_dumps(
  374. hook_config=config['btrfs'],
  375. config=config,
  376. log_prefix='test',
  377. borgmatic_runtime_directory='/run/borgmatic',
  378. dry_run=True,
  379. )
  380. def test_remove_data_source_dumps_without_subvolumes_skips_deletes():
  381. config = {'btrfs': {}}
  382. flexmock(module).should_receive('get_subvolumes').and_return(())
  383. flexmock(module).should_receive('make_snapshot_path').never()
  384. flexmock(module.borgmatic.config.paths).should_receive(
  385. 'replace_temporary_subdirectory_with_glob'
  386. ).never()
  387. flexmock(module).should_receive('delete_snapshot').never()
  388. flexmock(module.shutil).should_receive('rmtree').never()
  389. module.remove_data_source_dumps(
  390. hook_config=config['btrfs'],
  391. config=config,
  392. log_prefix='test',
  393. borgmatic_runtime_directory='/run/borgmatic',
  394. dry_run=False,
  395. )
  396. def test_remove_data_source_without_snapshots_skips_deletes():
  397. config = {'btrfs': {}}
  398. flexmock(module).should_receive('get_subvolumes').and_return(('/mnt/subvol1', '/mnt/subvol2'))
  399. flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
  400. '/mnt/subvol1/.borgmatic-1234/./mnt/subvol1'
  401. )
  402. flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol2').and_return(
  403. '/mnt/subvol2/.borgmatic-1234/./mnt/subvol2'
  404. )
  405. flexmock(module.borgmatic.config.paths).should_receive(
  406. 'replace_temporary_subdirectory_with_glob'
  407. ).with_args(
  408. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1',
  409. temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
  410. ).and_return(
  411. '/mnt/subvol1/.borgmatic-*/mnt/subvol1'
  412. )
  413. flexmock(module.borgmatic.config.paths).should_receive(
  414. 'replace_temporary_subdirectory_with_glob'
  415. ).with_args(
  416. '/mnt/subvol2/.borgmatic-1234/mnt/subvol2',
  417. temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
  418. ).and_return(
  419. '/mnt/subvol2/.borgmatic-*/mnt/subvol2'
  420. )
  421. flexmock(module.glob).should_receive('glob').and_return(())
  422. flexmock(module.os.path).should_receive('isdir').never()
  423. flexmock(module).should_receive('delete_snapshot').never()
  424. flexmock(module.shutil).should_receive('rmtree').never()
  425. module.remove_data_source_dumps(
  426. hook_config=config['btrfs'],
  427. config=config,
  428. log_prefix='test',
  429. borgmatic_runtime_directory='/run/borgmatic',
  430. dry_run=False,
  431. )
  432. def test_remove_data_source_dumps_with_delete_snapshot_file_not_found_error_bails():
  433. config = {'btrfs': {}}
  434. flexmock(module).should_receive('get_subvolumes').and_return(('/mnt/subvol1', '/mnt/subvol2'))
  435. flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
  436. '/mnt/subvol1/.borgmatic-1234/./mnt/subvol1'
  437. )
  438. flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol2').and_return(
  439. '/mnt/subvol2/.borgmatic-1234/./mnt/subvol2'
  440. )
  441. flexmock(module.borgmatic.config.paths).should_receive(
  442. 'replace_temporary_subdirectory_with_glob'
  443. ).with_args(
  444. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1',
  445. temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
  446. ).and_return(
  447. '/mnt/subvol1/.borgmatic-*/mnt/subvol1'
  448. )
  449. flexmock(module.borgmatic.config.paths).should_receive(
  450. 'replace_temporary_subdirectory_with_glob'
  451. ).with_args(
  452. '/mnt/subvol2/.borgmatic-1234/mnt/subvol2',
  453. temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
  454. ).and_return(
  455. '/mnt/subvol2/.borgmatic-*/mnt/subvol2'
  456. )
  457. flexmock(module.glob).should_receive('glob').with_args(
  458. '/mnt/subvol1/.borgmatic-*/mnt/subvol1'
  459. ).and_return(
  460. ('/mnt/subvol1/.borgmatic-1234/mnt/subvol1', '/mnt/subvol1/.borgmatic-5678/mnt/subvol1')
  461. )
  462. flexmock(module.glob).should_receive('glob').with_args(
  463. '/mnt/subvol2/.borgmatic-*/mnt/subvol2'
  464. ).and_return(
  465. ('/mnt/subvol2/.borgmatic-1234/mnt/subvol2', '/mnt/subvol2/.borgmatic-5678/mnt/subvol2')
  466. )
  467. flexmock(module.os.path).should_receive('isdir').with_args(
  468. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1'
  469. ).and_return(True)
  470. flexmock(module.os.path).should_receive('isdir').with_args(
  471. '/mnt/subvol1/.borgmatic-5678/mnt/subvol1'
  472. ).and_return(True)
  473. flexmock(module.os.path).should_receive('isdir').with_args(
  474. '/mnt/subvol2/.borgmatic-1234/mnt/subvol2'
  475. ).and_return(True)
  476. flexmock(module.os.path).should_receive('isdir').with_args(
  477. '/mnt/subvol2/.borgmatic-5678/mnt/subvol2'
  478. ).and_return(False)
  479. flexmock(module).should_receive('delete_snapshot').and_raise(FileNotFoundError)
  480. flexmock(module.shutil).should_receive('rmtree').never()
  481. module.remove_data_source_dumps(
  482. hook_config=config['btrfs'],
  483. config=config,
  484. log_prefix='test',
  485. borgmatic_runtime_directory='/run/borgmatic',
  486. dry_run=False,
  487. )
  488. def test_remove_data_source_dumps_with_delete_snapshot_called_process_error_bails():
  489. config = {'btrfs': {}}
  490. flexmock(module).should_receive('get_subvolumes').and_return(('/mnt/subvol1', '/mnt/subvol2'))
  491. flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
  492. '/mnt/subvol1/.borgmatic-1234/./mnt/subvol1'
  493. )
  494. flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol2').and_return(
  495. '/mnt/subvol2/.borgmatic-1234/./mnt/subvol2'
  496. )
  497. flexmock(module.borgmatic.config.paths).should_receive(
  498. 'replace_temporary_subdirectory_with_glob'
  499. ).with_args(
  500. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1',
  501. temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
  502. ).and_return(
  503. '/mnt/subvol1/.borgmatic-*/mnt/subvol1'
  504. )
  505. flexmock(module.borgmatic.config.paths).should_receive(
  506. 'replace_temporary_subdirectory_with_glob'
  507. ).with_args(
  508. '/mnt/subvol2/.borgmatic-1234/mnt/subvol2',
  509. temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
  510. ).and_return(
  511. '/mnt/subvol2/.borgmatic-*/mnt/subvol2'
  512. )
  513. flexmock(module.glob).should_receive('glob').with_args(
  514. '/mnt/subvol1/.borgmatic-*/mnt/subvol1'
  515. ).and_return(
  516. ('/mnt/subvol1/.borgmatic-1234/mnt/subvol1', '/mnt/subvol1/.borgmatic-5678/mnt/subvol1')
  517. )
  518. flexmock(module.glob).should_receive('glob').with_args(
  519. '/mnt/subvol2/.borgmatic-*/mnt/subvol2'
  520. ).and_return(
  521. ('/mnt/subvol2/.borgmatic-1234/mnt/subvol2', '/mnt/subvol2/.borgmatic-5678/mnt/subvol2')
  522. )
  523. flexmock(module.os.path).should_receive('isdir').with_args(
  524. '/mnt/subvol1/.borgmatic-1234/mnt/subvol1'
  525. ).and_return(True)
  526. flexmock(module.os.path).should_receive('isdir').with_args(
  527. '/mnt/subvol1/.borgmatic-5678/mnt/subvol1'
  528. ).and_return(True)
  529. flexmock(module.os.path).should_receive('isdir').with_args(
  530. '/mnt/subvol2/.borgmatic-1234/mnt/subvol2'
  531. ).and_return(True)
  532. flexmock(module.os.path).should_receive('isdir').with_args(
  533. '/mnt/subvol2/.borgmatic-5678/mnt/subvol2'
  534. ).and_return(False)
  535. flexmock(module).should_receive('delete_snapshot').and_raise(
  536. module.subprocess.CalledProcessError(1, 'command', 'error')
  537. )
  538. flexmock(module.shutil).should_receive('rmtree').never()
  539. module.remove_data_source_dumps(
  540. hook_config=config['btrfs'],
  541. config=config,
  542. log_prefix='test',
  543. borgmatic_runtime_directory='/run/borgmatic',
  544. dry_run=False,
  545. )