test_btrfs.py 23 KB

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