test_prune.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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. 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. )
  91. def test_prune_archives_with_log_info_calls_borg_with_info_parameter():
  92. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  93. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  94. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  95. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  96. insert_execute_command_mock(PRUNE_COMMAND + ('--info', 'repo'), logging.INFO)
  97. insert_logging_mock(logging.INFO)
  98. module.prune_archives(
  99. repository_path='repo',
  100. storage_config={},
  101. dry_run=False,
  102. retention_config=flexmock(),
  103. local_borg_version='1.2.3',
  104. )
  105. def test_prune_archives_with_log_debug_calls_borg_with_debug_parameter():
  106. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  107. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  108. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  109. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  110. insert_execute_command_mock(PRUNE_COMMAND + ('--debug', '--show-rc', 'repo'), logging.INFO)
  111. insert_logging_mock(logging.DEBUG)
  112. module.prune_archives(
  113. repository_path='repo',
  114. storage_config={},
  115. dry_run=False,
  116. retention_config=flexmock(),
  117. local_borg_version='1.2.3',
  118. )
  119. def test_prune_archives_with_dry_run_calls_borg_with_dry_run_parameter():
  120. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  121. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  122. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  123. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  124. insert_execute_command_mock(PRUNE_COMMAND + ('--dry-run', 'repo'), logging.INFO)
  125. module.prune_archives(
  126. repository_path='repo',
  127. storage_config={},
  128. dry_run=True,
  129. retention_config=flexmock(),
  130. local_borg_version='1.2.3',
  131. )
  132. def test_prune_archives_with_local_path_calls_borg_via_local_path():
  133. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  134. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  135. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  136. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  137. insert_execute_command_mock(('borg1',) + PRUNE_COMMAND[1:] + ('repo',), logging.INFO)
  138. module.prune_archives(
  139. dry_run=False,
  140. repository_path='repo',
  141. storage_config={},
  142. retention_config=flexmock(),
  143. local_borg_version='1.2.3',
  144. local_path='borg1',
  145. )
  146. def test_prune_archives_with_remote_path_calls_borg_with_remote_path_parameters():
  147. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  148. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  149. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  150. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  151. insert_execute_command_mock(PRUNE_COMMAND + ('--remote-path', 'borg1', 'repo'), logging.INFO)
  152. module.prune_archives(
  153. dry_run=False,
  154. repository_path='repo',
  155. storage_config={},
  156. retention_config=flexmock(),
  157. local_borg_version='1.2.3',
  158. remote_path='borg1',
  159. )
  160. def test_prune_archives_with_stats_calls_borg_with_stats_parameter_and_answer_output_log_level():
  161. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  162. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  163. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  164. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  165. insert_execute_command_mock(PRUNE_COMMAND + ('--stats', 'repo'), module.borgmatic.logger.ANSWER)
  166. module.prune_archives(
  167. dry_run=False,
  168. repository_path='repo',
  169. storage_config={},
  170. retention_config=flexmock(),
  171. local_borg_version='1.2.3',
  172. stats=True,
  173. )
  174. def test_prune_archives_with_files_calls_borg_with_list_parameter_and_answer_output_log_level():
  175. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  176. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  177. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  178. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  179. insert_execute_command_mock(PRUNE_COMMAND + ('--list', 'repo'), module.borgmatic.logger.ANSWER)
  180. module.prune_archives(
  181. dry_run=False,
  182. repository_path='repo',
  183. storage_config={},
  184. retention_config=flexmock(),
  185. local_borg_version='1.2.3',
  186. list_archives=True,
  187. )
  188. def test_prune_archives_with_umask_calls_borg_with_umask_parameters():
  189. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  190. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  191. storage_config = {'umask': '077'}
  192. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  193. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  194. insert_execute_command_mock(PRUNE_COMMAND + ('--umask', '077', 'repo'), logging.INFO)
  195. module.prune_archives(
  196. dry_run=False,
  197. repository_path='repo',
  198. storage_config=storage_config,
  199. retention_config=flexmock(),
  200. local_borg_version='1.2.3',
  201. )
  202. def test_prune_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
  203. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  204. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  205. storage_config = {'lock_wait': 5}
  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 + ('--lock-wait', '5', 'repo'), logging.INFO)
  209. module.prune_archives(
  210. dry_run=False,
  211. repository_path='repo',
  212. storage_config=storage_config,
  213. retention_config=flexmock(),
  214. local_borg_version='1.2.3',
  215. )
  216. def test_prune_archives_with_extra_borg_options_calls_borg_with_extra_options():
  217. flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
  218. flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
  219. flexmock(module).should_receive('make_prune_flags').and_return(BASE_PRUNE_FLAGS)
  220. flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
  221. insert_execute_command_mock(PRUNE_COMMAND + ('--extra', '--options', 'repo'), logging.INFO)
  222. module.prune_archives(
  223. dry_run=False,
  224. repository_path='repo',
  225. storage_config={'extra_borg_options': {'prune': '--extra --options'}},
  226. retention_config=flexmock(),
  227. local_borg_version='1.2.3',
  228. )