test_prune.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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_flags():
  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. module.prune_archives(
  85. dry_run=False,
  86. repository_path='repo',
  87. storage_config={},
  88. retention_config=flexmock(),
  89. local_borg_version='1.2.3',
  90. global_arguments=flexmock(log_json=False),
  91. )
  92. def test_prune_archives_with_log_info_calls_borg_with_info_flag():
  93. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  94. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  95. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  96. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  97. insert_execute_command_mock(PRUNE_COMMAND + ('--info', 'repo'), logging.INFO)
  98. insert_logging_mock(logging.INFO)
  99. module.prune_archives(
  100. repository_path='repo',
  101. storage_config={},
  102. dry_run=False,
  103. retention_config=flexmock(),
  104. local_borg_version='1.2.3',
  105. global_arguments=flexmock(log_json=False),
  106. )
  107. def test_prune_archives_with_log_debug_calls_borg_with_debug_flag():
  108. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  109. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  110. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  111. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  112. insert_execute_command_mock(PRUNE_COMMAND + ('--debug', '--show-rc', 'repo'), logging.INFO)
  113. insert_logging_mock(logging.DEBUG)
  114. module.prune_archives(
  115. repository_path='repo',
  116. storage_config={},
  117. dry_run=False,
  118. retention_config=flexmock(),
  119. local_borg_version='1.2.3',
  120. global_arguments=flexmock(log_json=False),
  121. )
  122. def test_prune_archives_with_dry_run_calls_borg_with_dry_run_flag():
  123. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  124. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  125. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  126. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  127. insert_execute_command_mock(PRUNE_COMMAND + ('--dry-run', 'repo'), logging.INFO)
  128. module.prune_archives(
  129. repository_path='repo',
  130. storage_config={},
  131. dry_run=True,
  132. retention_config=flexmock(),
  133. local_borg_version='1.2.3',
  134. global_arguments=flexmock(log_json=False),
  135. )
  136. def test_prune_archives_with_local_path_calls_borg_via_local_path():
  137. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  138. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  139. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  140. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  141. insert_execute_command_mock(('borg1',) + PRUNE_COMMAND[1:] + ('repo',), logging.INFO)
  142. module.prune_archives(
  143. dry_run=False,
  144. repository_path='repo',
  145. storage_config={},
  146. retention_config=flexmock(),
  147. local_borg_version='1.2.3',
  148. global_arguments=flexmock(log_json=False),
  149. local_path='borg1',
  150. )
  151. def test_prune_archives_with_remote_path_calls_borg_with_remote_path_flags():
  152. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  153. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  154. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  155. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  156. insert_execute_command_mock(PRUNE_COMMAND + ('--remote-path', 'borg1', 'repo'), logging.INFO)
  157. module.prune_archives(
  158. dry_run=False,
  159. repository_path='repo',
  160. storage_config={},
  161. retention_config=flexmock(),
  162. local_borg_version='1.2.3',
  163. global_arguments=flexmock(log_json=False),
  164. remote_path='borg1',
  165. )
  166. def test_prune_archives_with_stats_calls_borg_with_stats_flag_and_answer_output_log_level():
  167. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  168. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  169. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  170. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  171. insert_execute_command_mock(PRUNE_COMMAND + ('--stats', 'repo'), module.borgmatic.logger.ANSWER)
  172. module.prune_archives(
  173. dry_run=False,
  174. repository_path='repo',
  175. storage_config={},
  176. retention_config=flexmock(),
  177. local_borg_version='1.2.3',
  178. global_arguments=flexmock(log_json=False),
  179. stats=True,
  180. )
  181. def test_prune_archives_with_files_calls_borg_with_list_flag_and_answer_output_log_level():
  182. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  183. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  184. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  185. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  186. insert_execute_command_mock(PRUNE_COMMAND + ('--list', 'repo'), module.borgmatic.logger.ANSWER)
  187. module.prune_archives(
  188. dry_run=False,
  189. repository_path='repo',
  190. storage_config={},
  191. retention_config=flexmock(),
  192. local_borg_version='1.2.3',
  193. global_arguments=flexmock(log_json=False),
  194. list_archives=True,
  195. )
  196. def test_prune_archives_with_umask_calls_borg_with_umask_flags():
  197. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  198. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  199. storage_config = {'umask': '077'}
  200. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  201. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  202. insert_execute_command_mock(PRUNE_COMMAND + ('--umask', '077', 'repo'), logging.INFO)
  203. module.prune_archives(
  204. dry_run=False,
  205. repository_path='repo',
  206. storage_config=storage_config,
  207. retention_config=flexmock(),
  208. local_borg_version='1.2.3',
  209. global_arguments=flexmock(log_json=False),
  210. )
  211. def test_prune_archives_with_log_json_calls_borg_with_log_json_flag():
  212. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  213. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  214. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  215. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  216. insert_execute_command_mock(PRUNE_COMMAND + ('--log-json', 'repo'), logging.INFO)
  217. module.prune_archives(
  218. dry_run=False,
  219. repository_path='repo',
  220. storage_config={},
  221. retention_config=flexmock(),
  222. local_borg_version='1.2.3',
  223. global_arguments=flexmock(log_json=True),
  224. )
  225. def test_prune_archives_with_lock_wait_calls_borg_with_lock_wait_flags():
  226. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  227. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  228. storage_config = {'lock_wait': 5}
  229. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  230. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  231. insert_execute_command_mock(PRUNE_COMMAND + ('--lock-wait', '5', 'repo'), logging.INFO)
  232. module.prune_archives(
  233. dry_run=False,
  234. repository_path='repo',
  235. storage_config=storage_config,
  236. retention_config=flexmock(),
  237. local_borg_version='1.2.3',
  238. global_arguments=flexmock(log_json=False),
  239. )
  240. def test_prune_archives_with_extra_borg_options_calls_borg_with_extra_options():
  241. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  242. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  243. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  244. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  245. insert_execute_command_mock(PRUNE_COMMAND + ('--extra', '--options', 'repo'), logging.INFO)
  246. module.prune_archives(
  247. dry_run=False,
  248. repository_path='repo',
  249. storage_config={'extra_borg_options': {'prune': '--extra --options'}},
  250. retention_config=flexmock(),
  251. local_borg_version='1.2.3',
  252. global_arguments=flexmock(log_json=False),
  253. )