test_prune.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. import logging
  2. from collections import OrderedDict
  3. from flexmock import flexmock
  4. from borgmatic.borg import prune as module
  5. from ..test_verbosity import insert_logging_mock
  6. def insert_execute_command_mock(prune_command, output_log_level):
  7. flexmock(module.environment).should_receive('make_environment')
  8. flexmock(module).should_receive('execute_command').with_args(
  9. prune_command,
  10. output_log_level=output_log_level,
  11. borg_local_path=prune_command[0],
  12. extra_environment=None,
  13. ).once()
  14. BASE_PRUNE_FLAGS = ('--keep-daily', '1', '--keep-weekly', '2', '--keep-monthly', '3')
  15. def test_make_prune_flags_returns_flags_from_config():
  16. retention_config = OrderedDict((('keep_daily', 1), ('keep_weekly', 2), ('keep_monthly', 3)))
  17. flexmock(module.feature).should_receive('available').and_return(True)
  18. flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
  19. result = module.make_prune_flags({}, retention_config, local_borg_version='1.2.3')
  20. assert result == BASE_PRUNE_FLAGS
  21. def test_make_prune_flags_accepts_prefix_with_placeholders():
  22. retention_config = OrderedDict(
  23. (('keep_daily', 1), ('prefix', 'Documents_{hostname}-{now}')) # noqa: FS003
  24. )
  25. flexmock(module.feature).should_receive('available').and_return(True)
  26. flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
  27. result = module.make_prune_flags({}, retention_config, local_borg_version='1.2.3')
  28. expected = (
  29. '--keep-daily',
  30. '1',
  31. '--match-archives',
  32. 'sh:Documents_{hostname}-{now}*', # noqa: FS003
  33. )
  34. assert result == expected
  35. def test_make_prune_flags_with_prefix_without_borg_features_uses_glob_archives():
  36. retention_config = OrderedDict(
  37. (('keep_daily', 1), ('prefix', 'Documents_{hostname}-{now}')) # noqa: FS003
  38. )
  39. flexmock(module.feature).should_receive('available').and_return(False)
  40. flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
  41. result = module.make_prune_flags({}, retention_config, local_borg_version='1.2.3')
  42. expected = (
  43. '--keep-daily',
  44. '1',
  45. '--glob-archives',
  46. 'Documents_{hostname}-{now}*', # noqa: FS003
  47. )
  48. assert result == expected
  49. def test_make_prune_flags_prefers_prefix_to_archive_name_format():
  50. storage_config = {'archive_name_format': 'bar-{now}'} # noqa: FS003
  51. retention_config = OrderedDict((('keep_daily', 1), ('prefix', 'bar-')))
  52. flexmock(module.feature).should_receive('available').and_return(True)
  53. flexmock(module.flags).should_receive('make_match_archives_flags').never()
  54. result = module.make_prune_flags(storage_config, retention_config, local_borg_version='1.2.3')
  55. expected = (
  56. '--keep-daily',
  57. '1',
  58. '--match-archives',
  59. 'sh:bar-*', # noqa: FS003
  60. )
  61. assert result == expected
  62. def test_make_prune_flags_without_prefix_uses_archive_name_format_instead():
  63. storage_config = {'archive_name_format': 'bar-{now}'} # noqa: FS003
  64. retention_config = OrderedDict((('keep_daily', 1), ('prefix', None)))
  65. flexmock(module.feature).should_receive('available').and_return(True)
  66. flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
  67. None, 'bar-{now}', '1.2.3' # noqa: FS003
  68. ).and_return(('--match-archives', 'sh:bar-*'))
  69. result = module.make_prune_flags(storage_config, retention_config, local_borg_version='1.2.3')
  70. expected = (
  71. '--keep-daily',
  72. '1',
  73. '--match-archives',
  74. 'sh:bar-*', # noqa: FS003
  75. )
  76. assert result == expected
  77. PRUNE_COMMAND = ('borg', 'prune', '--keep-daily', '1', '--keep-weekly', '2', '--keep-monthly', '3')
  78. def test_prune_archives_calls_borg_with_parameters():
  79. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  80. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  81. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  82. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  83. insert_execute_command_mock(PRUNE_COMMAND + ('repo',), logging.INFO)
  84. prune_arguments = flexmock(stats=False, list_archives=False)
  85. module.prune_archives(
  86. dry_run=False,
  87. repository_path='repo',
  88. storage_config={},
  89. retention_config=flexmock(),
  90. local_borg_version='1.2.3',
  91. prune_arguments=prune_arguments,
  92. )
  93. def test_prune_archives_with_log_info_calls_borg_with_info_parameter():
  94. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  95. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  96. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  97. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  98. insert_execute_command_mock(PRUNE_COMMAND + ('--info', 'repo'), logging.INFO)
  99. insert_logging_mock(logging.INFO)
  100. prune_arguments = flexmock(stats=False, list_archives=False)
  101. module.prune_archives(
  102. repository_path='repo',
  103. storage_config={},
  104. dry_run=False,
  105. retention_config=flexmock(),
  106. local_borg_version='1.2.3',
  107. prune_arguments=prune_arguments,
  108. )
  109. def test_prune_archives_with_log_debug_calls_borg_with_debug_parameter():
  110. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  111. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  112. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  113. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  114. insert_execute_command_mock(PRUNE_COMMAND + ('--debug', '--show-rc', 'repo'), logging.INFO)
  115. insert_logging_mock(logging.DEBUG)
  116. prune_arguments = flexmock(stats=False, list_archives=False)
  117. module.prune_archives(
  118. repository_path='repo',
  119. storage_config={},
  120. dry_run=False,
  121. retention_config=flexmock(),
  122. local_borg_version='1.2.3',
  123. prune_arguments=prune_arguments,
  124. )
  125. def test_prune_archives_with_dry_run_calls_borg_with_dry_run_parameter():
  126. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  127. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  128. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  129. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  130. insert_execute_command_mock(PRUNE_COMMAND + ('--dry-run', 'repo'), logging.INFO)
  131. prune_arguments = flexmock(stats=False, list_archives=False)
  132. module.prune_archives(
  133. repository_path='repo',
  134. storage_config={},
  135. dry_run=True,
  136. retention_config=flexmock(),
  137. local_borg_version='1.2.3',
  138. prune_arguments=prune_arguments,
  139. )
  140. def test_prune_archives_with_local_path_calls_borg_via_local_path():
  141. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  142. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  143. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  144. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  145. insert_execute_command_mock(('borg1',) + PRUNE_COMMAND[1:] + ('repo',), logging.INFO)
  146. prune_arguments = flexmock(stats=False, list_archives=False)
  147. module.prune_archives(
  148. dry_run=False,
  149. repository_path='repo',
  150. storage_config={},
  151. retention_config=flexmock(),
  152. local_borg_version='1.2.3',
  153. local_path='borg1',
  154. prune_arguments=prune_arguments,
  155. )
  156. def test_prune_archives_with_remote_path_calls_borg_with_remote_path_parameters():
  157. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  158. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  159. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  160. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  161. insert_execute_command_mock(PRUNE_COMMAND + ('--remote-path', 'borg1', 'repo'), logging.INFO)
  162. prune_arguments = flexmock(stats=False, list_archives=False)
  163. module.prune_archives(
  164. dry_run=False,
  165. repository_path='repo',
  166. storage_config={},
  167. retention_config=flexmock(),
  168. local_borg_version='1.2.3',
  169. remote_path='borg1',
  170. prune_arguments=prune_arguments,
  171. )
  172. def test_prune_archives_with_stats_calls_borg_with_stats_parameter_and_answer_output_log_level():
  173. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  174. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  175. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  176. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  177. insert_execute_command_mock(PRUNE_COMMAND + ('--stats', 'repo'), module.borgmatic.logger.ANSWER)
  178. prune_arguments = flexmock(stats=True, list_archives=False)
  179. module.prune_archives(
  180. dry_run=False,
  181. repository_path='repo',
  182. storage_config={},
  183. retention_config=flexmock(),
  184. local_borg_version='1.2.3',
  185. prune_arguments=prune_arguments,
  186. )
  187. def test_prune_archives_with_files_calls_borg_with_list_parameter_and_answer_output_log_level():
  188. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  189. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  190. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  191. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  192. insert_execute_command_mock(PRUNE_COMMAND + ('--list', 'repo'), module.borgmatic.logger.ANSWER)
  193. prune_arguments = flexmock(stats=False, list_archives=True)
  194. module.prune_archives(
  195. dry_run=False,
  196. repository_path='repo',
  197. storage_config={},
  198. retention_config=flexmock(),
  199. local_borg_version='1.2.3',
  200. prune_arguments=prune_arguments,
  201. )
  202. def test_prune_archives_with_umask_calls_borg_with_umask_parameters():
  203. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  204. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  205. storage_config = {'umask': '077'}
  206. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  207. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  208. insert_execute_command_mock(PRUNE_COMMAND + ('--umask', '077', 'repo'), logging.INFO)
  209. prune_arguments = flexmock(stats=False, list_archives=False)
  210. module.prune_archives(
  211. dry_run=False,
  212. repository_path='repo',
  213. storage_config=storage_config,
  214. retention_config=flexmock(),
  215. local_borg_version='1.2.3',
  216. prune_arguments=prune_arguments,
  217. )
  218. def test_prune_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
  219. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  220. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  221. storage_config = {'lock_wait': 5}
  222. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  223. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  224. insert_execute_command_mock(PRUNE_COMMAND + ('--lock-wait', '5', 'repo'), logging.INFO)
  225. prune_arguments = flexmock(stats=False, list_archives=False)
  226. module.prune_archives(
  227. dry_run=False,
  228. repository_path='repo',
  229. storage_config=storage_config,
  230. retention_config=flexmock(),
  231. local_borg_version='1.2.3',
  232. prune_arguments=prune_arguments,
  233. )
  234. def test_prune_archives_with_extra_borg_options_calls_borg_with_extra_options():
  235. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  236. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  237. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  238. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  239. insert_execute_command_mock(PRUNE_COMMAND + ('--extra', '--options', 'repo'), logging.INFO)
  240. prune_arguments = flexmock(stats=False, list_archives=False)
  241. module.prune_archives(
  242. dry_run=False,
  243. repository_path='repo',
  244. storage_config={'extra_borg_options': {'prune': '--extra --options'}},
  245. retention_config=flexmock(),
  246. local_borg_version='1.2.3',
  247. prune_arguments=prune_arguments,
  248. )
  249. def test_prune_archives_with_date_based_matching_calls_borg_with_date_based_flags():
  250. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  251. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  252. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  253. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  254. insert_execute_command_mock(
  255. PRUNE_COMMAND
  256. + ('--newer', '1d', '--newest', '1y', '--older', '1m', '--oldest', '1w', 'repo'),
  257. logging.INFO,
  258. )
  259. prune_arguments = flexmock(
  260. stats=False, list_archives=False, newer='1d', newest='1y', older='1m', oldest='1w'
  261. )
  262. module.prune_archives(
  263. dry_run=False,
  264. repository_path='repo',
  265. storage_config={},
  266. retention_config=flexmock(),
  267. local_borg_version='1.2.3',
  268. prune_arguments=prune_arguments,
  269. )