test_zfs.py 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  1. import os
  2. import pytest
  3. from flexmock import flexmock
  4. from borgmatic.borg.pattern import Pattern, Pattern_style, Pattern_type
  5. from borgmatic.hooks.data_source import zfs as module
  6. def test_get_datasets_to_backup_filters_datasets_by_patterns():
  7. flexmock(module.borgmatic.execute).should_receive(
  8. 'execute_command_and_capture_output'
  9. ).and_return(
  10. 'dataset\t/dataset\t-\nother\t/other\t-',
  11. )
  12. flexmock(module.borgmatic.hooks.data_source.snapshot).should_receive(
  13. 'get_contained_patterns'
  14. ).with_args('/dataset', object).and_return((Pattern('/dataset'),))
  15. flexmock(module.borgmatic.hooks.data_source.snapshot).should_receive(
  16. 'get_contained_patterns'
  17. ).with_args('/other', object).and_return(())
  18. assert module.get_datasets_to_backup(
  19. 'zfs',
  20. patterns=(
  21. Pattern('/foo'),
  22. Pattern('/dataset'),
  23. Pattern('/bar'),
  24. ),
  25. ) == (
  26. module.Dataset(
  27. name='dataset',
  28. mount_point='/dataset',
  29. contained_patterns=(Pattern('/dataset'),),
  30. ),
  31. )
  32. def test_get_datasets_to_backup_filters_datasets_by_user_property():
  33. flexmock(module.borgmatic.execute).should_receive(
  34. 'execute_command_and_capture_output'
  35. ).and_return(
  36. 'dataset\t/dataset\tauto\nother\t/other\t-',
  37. )
  38. flexmock(module.borgmatic.hooks.data_source.snapshot).should_receive(
  39. 'get_contained_patterns'
  40. ).with_args('/dataset', object).and_return(())
  41. flexmock(module.borgmatic.hooks.data_source.snapshot).should_receive(
  42. 'get_contained_patterns'
  43. ).with_args('/other', object).and_return(())
  44. assert module.get_datasets_to_backup('zfs', patterns=(Pattern('/foo'), Pattern('/bar'))) == (
  45. module.Dataset(
  46. name='dataset',
  47. mount_point='/dataset',
  48. auto_backup=True,
  49. contained_patterns=(Pattern('/dataset'),),
  50. ),
  51. )
  52. def test_get_datasets_to_backup_with_invalid_list_output_raises():
  53. flexmock(module.borgmatic.execute).should_receive(
  54. 'execute_command_and_capture_output'
  55. ).and_return(
  56. 'dataset',
  57. )
  58. flexmock(module.borgmatic.hooks.data_source.snapshot).should_receive(
  59. 'get_contained_patterns'
  60. ).never()
  61. with pytest.raises(ValueError, match='zfs'):
  62. module.get_datasets_to_backup('zfs', patterns=(Pattern('/foo'), Pattern('/bar')))
  63. def test_get_all_dataset_mount_points_does_not_filter_datasets():
  64. flexmock(module.borgmatic.execute).should_receive(
  65. 'execute_command_and_capture_output'
  66. ).and_return(
  67. '/dataset\n/other',
  68. )
  69. flexmock(module.borgmatic.hooks.data_source.snapshot).should_receive(
  70. 'get_contained_patterns'
  71. ).and_return((Pattern('/dataset'),))
  72. assert module.get_all_dataset_mount_points('zfs') == (
  73. ('/dataset'),
  74. ('/other'),
  75. )
  76. @pytest.mark.parametrize(
  77. 'pattern,expected_pattern',
  78. (
  79. (
  80. Pattern('/foo/bar/baz'),
  81. Pattern('/run/borgmatic/zfs_snapshots/./foo/bar/baz'),
  82. ),
  83. (Pattern('/foo/bar'), Pattern('/run/borgmatic/zfs_snapshots/./foo/bar')),
  84. (
  85. Pattern('^/foo/bar', Pattern_type.INCLUDE, Pattern_style.REGULAR_EXPRESSION),
  86. Pattern(
  87. '^/run/borgmatic/zfs_snapshots/./foo/bar',
  88. Pattern_type.INCLUDE,
  89. Pattern_style.REGULAR_EXPRESSION,
  90. ),
  91. ),
  92. (
  93. Pattern('/foo/bar', Pattern_type.INCLUDE, Pattern_style.REGULAR_EXPRESSION),
  94. Pattern(
  95. '/run/borgmatic/zfs_snapshots/./foo/bar',
  96. Pattern_type.INCLUDE,
  97. Pattern_style.REGULAR_EXPRESSION,
  98. ),
  99. ),
  100. (Pattern('/foo'), Pattern('/run/borgmatic/zfs_snapshots/./foo')),
  101. (Pattern('/'), Pattern('/run/borgmatic/zfs_snapshots/./')),
  102. ),
  103. )
  104. def test_make_borg_snapshot_pattern_includes_slashdot_hack_and_stripped_pattern_path(
  105. pattern, expected_pattern
  106. ):
  107. assert module.make_borg_snapshot_pattern(pattern, '/run/borgmatic') == expected_pattern
  108. def test_dump_data_sources_snapshots_and_mounts_and_updates_patterns():
  109. flexmock(module).should_receive('get_datasets_to_backup').and_return(
  110. (
  111. flexmock(
  112. name='dataset',
  113. mount_point='/mnt/dataset',
  114. contained_patterns=(Pattern('/mnt/dataset/subdir'),),
  115. )
  116. )
  117. )
  118. flexmock(module.os).should_receive('getpid').and_return(1234)
  119. full_snapshot_name = 'dataset@borgmatic-1234'
  120. flexmock(module).should_receive('snapshot_dataset').with_args(
  121. 'zfs',
  122. full_snapshot_name,
  123. ).once()
  124. snapshot_mount_path = '/run/borgmatic/zfs_snapshots/./mnt/dataset'
  125. flexmock(module).should_receive('mount_snapshot').with_args(
  126. 'mount',
  127. full_snapshot_name,
  128. module.os.path.normpath(snapshot_mount_path),
  129. ).once()
  130. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  131. Pattern('/mnt/dataset/subdir'), '/run/borgmatic'
  132. ).and_return(Pattern('/run/borgmatic/zfs_snapshots/./mnt/dataset/subdir'))
  133. patterns = [Pattern('/mnt/dataset/subdir')]
  134. assert (
  135. module.dump_data_sources(
  136. hook_config={},
  137. config={'source_directories': '/mnt/dataset', 'zfs': {}},
  138. log_prefix='test',
  139. config_paths=('test.yaml',),
  140. borgmatic_runtime_directory='/run/borgmatic',
  141. patterns=patterns,
  142. dry_run=False,
  143. )
  144. == []
  145. )
  146. assert patterns == [Pattern(os.path.join(snapshot_mount_path, 'subdir'))]
  147. def test_dump_data_sources_with_no_datasets_skips_snapshots():
  148. flexmock(module).should_receive('get_datasets_to_backup').and_return(())
  149. flexmock(module.os).should_receive('getpid').and_return(1234)
  150. flexmock(module).should_receive('snapshot_dataset').never()
  151. flexmock(module).should_receive('mount_snapshot').never()
  152. patterns = [Pattern('/mnt/dataset')]
  153. assert (
  154. module.dump_data_sources(
  155. hook_config={},
  156. config={'patterns': flexmock(), 'zfs': {}},
  157. log_prefix='test',
  158. config_paths=('test.yaml',),
  159. borgmatic_runtime_directory='/run/borgmatic',
  160. patterns=patterns,
  161. dry_run=False,
  162. )
  163. == []
  164. )
  165. assert patterns == [Pattern('/mnt/dataset')]
  166. def test_dump_data_sources_uses_custom_commands():
  167. flexmock(module).should_receive('get_datasets_to_backup').and_return(
  168. (
  169. flexmock(
  170. name='dataset',
  171. mount_point='/mnt/dataset',
  172. contained_patterns=(Pattern('/mnt/dataset/subdir'),),
  173. )
  174. )
  175. )
  176. flexmock(module.os).should_receive('getpid').and_return(1234)
  177. full_snapshot_name = 'dataset@borgmatic-1234'
  178. flexmock(module).should_receive('snapshot_dataset').with_args(
  179. '/usr/local/bin/zfs',
  180. full_snapshot_name,
  181. ).once()
  182. snapshot_mount_path = '/run/borgmatic/zfs_snapshots/./mnt/dataset'
  183. flexmock(module).should_receive('mount_snapshot').with_args(
  184. '/usr/local/bin/mount',
  185. full_snapshot_name,
  186. module.os.path.normpath(snapshot_mount_path),
  187. ).once()
  188. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  189. Pattern('/mnt/dataset/subdir'), '/run/borgmatic'
  190. ).and_return(Pattern('/run/borgmatic/zfs_snapshots/./mnt/dataset/subdir'))
  191. patterns = [Pattern('/mnt/dataset/subdir')]
  192. hook_config = {
  193. 'zfs_command': '/usr/local/bin/zfs',
  194. 'mount_command': '/usr/local/bin/mount',
  195. }
  196. assert (
  197. module.dump_data_sources(
  198. hook_config=hook_config,
  199. config={
  200. 'patterns': flexmock(),
  201. 'zfs': hook_config,
  202. },
  203. log_prefix='test',
  204. config_paths=('test.yaml',),
  205. borgmatic_runtime_directory='/run/borgmatic',
  206. patterns=patterns,
  207. dry_run=False,
  208. )
  209. == []
  210. )
  211. assert patterns == [Pattern(os.path.join(snapshot_mount_path, 'subdir'))]
  212. def test_dump_data_sources_with_dry_run_skips_commands_and_does_not_touch_patterns():
  213. flexmock(module).should_receive('get_datasets_to_backup').and_return(
  214. (flexmock(name='dataset', mount_point='/mnt/dataset'),)
  215. )
  216. flexmock(module.os).should_receive('getpid').and_return(1234)
  217. flexmock(module).should_receive('snapshot_dataset').never()
  218. flexmock(module).should_receive('mount_snapshot').never()
  219. patterns = [Pattern('/mnt/dataset')]
  220. assert (
  221. module.dump_data_sources(
  222. hook_config={},
  223. config={'patterns': ('R /mnt/dataset',), 'zfs': {}},
  224. log_prefix='test',
  225. config_paths=('test.yaml',),
  226. borgmatic_runtime_directory='/run/borgmatic',
  227. patterns=patterns,
  228. dry_run=True,
  229. )
  230. == []
  231. )
  232. assert patterns == [Pattern('/mnt/dataset')]
  233. def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained_patterns():
  234. flexmock(module).should_receive('get_datasets_to_backup').and_return(
  235. (
  236. flexmock(
  237. name='dataset',
  238. mount_point='/mnt/dataset',
  239. contained_patterns=(Pattern('/mnt/dataset/subdir'),),
  240. )
  241. )
  242. )
  243. flexmock(module.os).should_receive('getpid').and_return(1234)
  244. full_snapshot_name = 'dataset@borgmatic-1234'
  245. flexmock(module).should_receive('snapshot_dataset').with_args(
  246. 'zfs',
  247. full_snapshot_name,
  248. ).once()
  249. snapshot_mount_path = '/run/borgmatic/zfs_snapshots/./mnt/dataset'
  250. flexmock(module).should_receive('mount_snapshot').with_args(
  251. 'mount',
  252. full_snapshot_name,
  253. module.os.path.normpath(snapshot_mount_path),
  254. ).once()
  255. flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
  256. Pattern('/mnt/dataset/subdir'), '/run/borgmatic'
  257. ).and_return(Pattern('/run/borgmatic/zfs_snapshots/./mnt/dataset/subdir'))
  258. patterns = [Pattern('/hmm')]
  259. assert (
  260. module.dump_data_sources(
  261. hook_config={},
  262. config={'patterns': ('R /mnt/dataset',), 'zfs': {}},
  263. log_prefix='test',
  264. config_paths=('test.yaml',),
  265. borgmatic_runtime_directory='/run/borgmatic',
  266. patterns=patterns,
  267. dry_run=False,
  268. )
  269. == []
  270. )
  271. assert patterns == [Pattern('/hmm'), Pattern(os.path.join(snapshot_mount_path, 'subdir'))]
  272. def test_get_all_snapshots_parses_list_output():
  273. flexmock(module.borgmatic.execute).should_receive(
  274. 'execute_command_and_capture_output'
  275. ).and_return(
  276. 'dataset1@borgmatic-1234\ndataset2@borgmatic-4567',
  277. )
  278. assert module.get_all_snapshots('zfs') == ('dataset1@borgmatic-1234', 'dataset2@borgmatic-4567')
  279. def test_remove_data_source_dumps_unmounts_and_destroys_snapshots():
  280. flexmock(module).should_receive('get_all_dataset_mount_points').and_return(('/mnt/dataset',))
  281. flexmock(module.borgmatic.config.paths).should_receive(
  282. 'replace_temporary_subdirectory_with_glob'
  283. ).and_return('/run/borgmatic')
  284. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  285. flexmock(module.os.path).should_receive('isdir').and_return(True)
  286. flexmock(module.shutil).should_receive('rmtree')
  287. flexmock(module).should_receive('unmount_snapshot').with_args(
  288. 'umount', '/run/borgmatic/zfs_snapshots/mnt/dataset'
  289. ).once()
  290. flexmock(module).should_receive('get_all_snapshots').and_return(
  291. ('dataset@borgmatic-1234', 'dataset@other', 'other@other', 'invalid')
  292. )
  293. flexmock(module).should_receive('destroy_snapshot').with_args(
  294. 'zfs', 'dataset@borgmatic-1234'
  295. ).once()
  296. module.remove_data_source_dumps(
  297. hook_config={},
  298. config={'source_directories': '/mnt/dataset', 'zfs': {}},
  299. log_prefix='test',
  300. borgmatic_runtime_directory='/run/borgmatic',
  301. dry_run=False,
  302. )
  303. def test_remove_data_source_dumps_use_custom_commands():
  304. flexmock(module).should_receive('get_all_dataset_mount_points').and_return(('/mnt/dataset',))
  305. flexmock(module.borgmatic.config.paths).should_receive(
  306. 'replace_temporary_subdirectory_with_glob'
  307. ).and_return('/run/borgmatic')
  308. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  309. flexmock(module.os.path).should_receive('isdir').and_return(True)
  310. flexmock(module.shutil).should_receive('rmtree')
  311. flexmock(module).should_receive('unmount_snapshot').with_args(
  312. '/usr/local/bin/umount', '/run/borgmatic/zfs_snapshots/mnt/dataset'
  313. ).once()
  314. flexmock(module).should_receive('get_all_snapshots').and_return(
  315. ('dataset@borgmatic-1234', 'dataset@other', 'other@other', 'invalid')
  316. )
  317. flexmock(module).should_receive('destroy_snapshot').with_args(
  318. '/usr/local/bin/zfs', 'dataset@borgmatic-1234'
  319. ).once()
  320. hook_config = {'zfs_command': '/usr/local/bin/zfs', 'umount_command': '/usr/local/bin/umount'}
  321. module.remove_data_source_dumps(
  322. hook_config=hook_config,
  323. config={'source_directories': '/mnt/dataset', 'zfs': hook_config},
  324. log_prefix='test',
  325. borgmatic_runtime_directory='/run/borgmatic',
  326. dry_run=False,
  327. )
  328. def test_remove_data_source_dumps_bails_for_missing_hook_configuration():
  329. flexmock(module).should_receive('get_all_dataset_mount_points').never()
  330. flexmock(module.borgmatic.config.paths).should_receive(
  331. 'replace_temporary_subdirectory_with_glob'
  332. ).never()
  333. module.remove_data_source_dumps(
  334. hook_config=None,
  335. config={'source_directories': '/mnt/dataset'},
  336. log_prefix='test',
  337. borgmatic_runtime_directory='/run/borgmatic',
  338. dry_run=False,
  339. )
  340. def test_remove_data_source_dumps_bails_for_missing_zfs_command():
  341. flexmock(module).should_receive('get_all_dataset_mount_points').and_raise(FileNotFoundError)
  342. flexmock(module.borgmatic.config.paths).should_receive(
  343. 'replace_temporary_subdirectory_with_glob'
  344. ).never()
  345. hook_config = {'zfs_command': 'wtf'}
  346. module.remove_data_source_dumps(
  347. hook_config=hook_config,
  348. config={'source_directories': '/mnt/dataset', 'zfs': hook_config},
  349. log_prefix='test',
  350. borgmatic_runtime_directory='/run/borgmatic',
  351. dry_run=False,
  352. )
  353. def test_remove_data_source_dumps_bails_for_zfs_command_error():
  354. flexmock(module).should_receive('get_all_dataset_mount_points').and_raise(
  355. module.subprocess.CalledProcessError(1, 'wtf')
  356. )
  357. flexmock(module.borgmatic.config.paths).should_receive(
  358. 'replace_temporary_subdirectory_with_glob'
  359. ).never()
  360. hook_config = {'zfs_command': 'wtf'}
  361. module.remove_data_source_dumps(
  362. hook_config=hook_config,
  363. config={'source_directories': '/mnt/dataset', 'zfs': hook_config},
  364. log_prefix='test',
  365. borgmatic_runtime_directory='/run/borgmatic',
  366. dry_run=False,
  367. )
  368. def test_remove_data_source_dumps_bails_for_missing_umount_command():
  369. flexmock(module).should_receive('get_all_dataset_mount_points').and_return(('/mnt/dataset',))
  370. flexmock(module.borgmatic.config.paths).should_receive(
  371. 'replace_temporary_subdirectory_with_glob'
  372. ).and_return('/run/borgmatic')
  373. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  374. flexmock(module.os.path).should_receive('isdir').and_return(True)
  375. flexmock(module.shutil).should_receive('rmtree')
  376. flexmock(module).should_receive('unmount_snapshot').with_args(
  377. '/usr/local/bin/umount', '/run/borgmatic/zfs_snapshots/mnt/dataset'
  378. ).and_raise(FileNotFoundError)
  379. flexmock(module).should_receive('get_all_snapshots').never()
  380. flexmock(module).should_receive('destroy_snapshot').never()
  381. hook_config = {'zfs_command': '/usr/local/bin/zfs', 'umount_command': '/usr/local/bin/umount'}
  382. module.remove_data_source_dumps(
  383. hook_config=hook_config,
  384. config={'source_directories': '/mnt/dataset', 'zfs': hook_config},
  385. log_prefix='test',
  386. borgmatic_runtime_directory='/run/borgmatic',
  387. dry_run=False,
  388. )
  389. def test_remove_data_source_dumps_bails_for_umount_command_error():
  390. flexmock(module).should_receive('get_all_dataset_mount_points').and_return(('/mnt/dataset',))
  391. flexmock(module.borgmatic.config.paths).should_receive(
  392. 'replace_temporary_subdirectory_with_glob'
  393. ).and_return('/run/borgmatic')
  394. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  395. flexmock(module.os.path).should_receive('isdir').and_return(True)
  396. flexmock(module.shutil).should_receive('rmtree')
  397. flexmock(module).should_receive('unmount_snapshot').with_args(
  398. '/usr/local/bin/umount', '/run/borgmatic/zfs_snapshots/mnt/dataset'
  399. ).and_raise(module.subprocess.CalledProcessError(1, 'wtf'))
  400. flexmock(module).should_receive('get_all_snapshots').never()
  401. flexmock(module).should_receive('destroy_snapshot').never()
  402. hook_config = {'zfs_command': '/usr/local/bin/zfs', 'umount_command': '/usr/local/bin/umount'}
  403. module.remove_data_source_dumps(
  404. hook_config=hook_config,
  405. config={'source_directories': '/mnt/dataset', 'zfs': hook_config},
  406. log_prefix='test',
  407. borgmatic_runtime_directory='/run/borgmatic',
  408. dry_run=False,
  409. )
  410. def test_remove_data_source_dumps_skips_unmount_snapshot_directories_that_are_not_actually_directories():
  411. flexmock(module).should_receive('get_all_dataset_mount_points').and_return(('/mnt/dataset',))
  412. flexmock(module.borgmatic.config.paths).should_receive(
  413. 'replace_temporary_subdirectory_with_glob'
  414. ).and_return('/run/borgmatic')
  415. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  416. flexmock(module.os.path).should_receive('isdir').and_return(False)
  417. flexmock(module.shutil).should_receive('rmtree').never()
  418. flexmock(module).should_receive('unmount_snapshot').never()
  419. flexmock(module).should_receive('get_all_snapshots').and_return(
  420. ('dataset@borgmatic-1234', 'dataset@other', 'other@other', 'invalid')
  421. )
  422. flexmock(module).should_receive('destroy_snapshot').with_args(
  423. 'zfs', 'dataset@borgmatic-1234'
  424. ).once()
  425. module.remove_data_source_dumps(
  426. hook_config={},
  427. config={'source_directories': '/mnt/dataset', 'zfs': {}},
  428. log_prefix='test',
  429. borgmatic_runtime_directory='/run/borgmatic',
  430. dry_run=False,
  431. )
  432. def test_remove_data_source_dumps_skips_unmount_snapshot_mount_paths_that_are_not_actually_directories():
  433. flexmock(module).should_receive('get_all_dataset_mount_points').and_return(('/mnt/dataset',))
  434. flexmock(module.borgmatic.config.paths).should_receive(
  435. 'replace_temporary_subdirectory_with_glob'
  436. ).and_return('/run/borgmatic')
  437. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  438. flexmock(module.os.path).should_receive('isdir').with_args(
  439. '/run/borgmatic/zfs_snapshots'
  440. ).and_return(True)
  441. flexmock(module.os.path).should_receive('isdir').with_args(
  442. '/run/borgmatic/zfs_snapshots/mnt/dataset'
  443. ).and_return(False)
  444. flexmock(module.shutil).should_receive('rmtree')
  445. flexmock(module).should_receive('unmount_snapshot').never()
  446. flexmock(module).should_receive('get_all_snapshots').and_return(
  447. ('dataset@borgmatic-1234', 'dataset@other', 'other@other', 'invalid')
  448. )
  449. flexmock(module).should_receive('destroy_snapshot').with_args(
  450. 'zfs', 'dataset@borgmatic-1234'
  451. ).once()
  452. module.remove_data_source_dumps(
  453. hook_config={},
  454. config={'source_directories': '/mnt/dataset', 'zfs': {}},
  455. log_prefix='test',
  456. borgmatic_runtime_directory='/run/borgmatic',
  457. dry_run=False,
  458. )
  459. def test_remove_data_source_dumps_skips_unmount_snapshot_mount_paths_after_rmtree_succeeds():
  460. flexmock(module).should_receive('get_all_dataset_mount_points').and_return(('/mnt/dataset',))
  461. flexmock(module.borgmatic.config.paths).should_receive(
  462. 'replace_temporary_subdirectory_with_glob'
  463. ).and_return('/run/borgmatic')
  464. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  465. flexmock(module.os.path).should_receive('isdir').with_args(
  466. '/run/borgmatic/zfs_snapshots'
  467. ).and_return(True)
  468. flexmock(module.os.path).should_receive('isdir').with_args(
  469. '/run/borgmatic/zfs_snapshots/mnt/dataset'
  470. ).and_return(True).and_return(False)
  471. flexmock(module.shutil).should_receive('rmtree')
  472. flexmock(module).should_receive('unmount_snapshot').never()
  473. flexmock(module).should_receive('get_all_snapshots').and_return(
  474. ('dataset@borgmatic-1234', 'dataset@other', 'other@other', 'invalid')
  475. )
  476. flexmock(module).should_receive('destroy_snapshot').with_args(
  477. 'zfs', 'dataset@borgmatic-1234'
  478. ).once()
  479. module.remove_data_source_dumps(
  480. hook_config={},
  481. config={'source_directories': '/mnt/dataset', 'zfs': {}},
  482. log_prefix='test',
  483. borgmatic_runtime_directory='/run/borgmatic',
  484. dry_run=False,
  485. )
  486. def test_remove_data_source_dumps_with_dry_run_skips_unmount_and_destroy():
  487. flexmock(module).should_receive('get_all_dataset_mount_points').and_return(('/mnt/dataset',))
  488. flexmock(module.borgmatic.config.paths).should_receive(
  489. 'replace_temporary_subdirectory_with_glob'
  490. ).and_return('/run/borgmatic')
  491. flexmock(module.glob).should_receive('glob').replace_with(lambda path: [path])
  492. flexmock(module.os.path).should_receive('isdir').and_return(True)
  493. flexmock(module.shutil).should_receive('rmtree').never()
  494. flexmock(module).should_receive('unmount_snapshot').never()
  495. flexmock(module).should_receive('get_all_snapshots').and_return(
  496. ('dataset@borgmatic-1234', 'dataset@other', 'other@other', 'invalid')
  497. )
  498. flexmock(module).should_receive('destroy_snapshot').never()
  499. module.remove_data_source_dumps(
  500. hook_config={},
  501. config={'source_directories': '/mnt/dataset', 'zfs': {}},
  502. log_prefix='test',
  503. borgmatic_runtime_directory='/run/borgmatic',
  504. dry_run=True,
  505. )