소스 검색

47: Support for Borg --dry-run option via borgmatic command-line.

Dan Helfman 7 년 전
부모
커밋
62526038d6
6개의 변경된 파일81개의 추가작업 그리고 11개의 파일을 삭제
  1. 2 1
      NEWS
  2. 5 4
      borgmatic/borg/create.py
  3. 5 4
      borgmatic/borg/prune.py
  4. 11 2
      borgmatic/commands/borgmatic.py
  5. 38 0
      borgmatic/tests/unit/borg/test_create.py
  6. 20 0
      borgmatic/tests/unit/borg/test_prune.py

+ 2 - 1
NEWS

@@ -1,5 +1,6 @@
 1.1.14.dev0
- * #49: Rename incorrect --pattern-from option to correct --patterns-from.
+ * #49: Fix for typo in --patterns-from option.
+ * #47: Support for Borg --dry-run option via borgmatic command-line.
 
 1.1.13
  * #54: Fix for incorrect consistency check flags passed to Borg when all three checks ("repository",

+ 5 - 4
borgmatic/borg/create.py

@@ -85,11 +85,11 @@ def _make_exclude_flags(location_config, exclude_filename=None):
 
 
 def create_archive(
-    verbosity, repository, location_config, storage_config, local_path='borg', remote_path=None,
+    verbosity, dry_run, repository, location_config, storage_config, local_path='borg', remote_path=None,
 ):
     '''
-    Given a vebosity flag, a local or remote repository path, a location config dict, and a storage
-    config dict, create a Borg archive.
+    Given vebosity/dry-run flags, a local or remote repository path, a location config dict, and a
+    storage config dict, create a Borg archive.
     '''
     sources = tuple(
         itertools.chain.from_iterable(
@@ -122,6 +122,7 @@ def create_archive(
         VERBOSITY_SOME: ('--info', '--stats',),
         VERBOSITY_LOTS: ('--debug', '--list', '--stats'),
     }.get(verbosity, ())
+    dry_run_flags = ('--dry-run',) if dry_run else ()
     default_archive_name_format = '{hostname}-{now:%Y-%m-%dT%H:%M:%S.%f}'
     archive_name_format = storage_config.get('archive_name_format', default_archive_name_format)
 
@@ -133,7 +134,7 @@ def create_archive(
         ),
     ) + sources + pattern_flags + exclude_flags + compression_flags + remote_rate_limit_flags + \
         one_file_system_flags + files_cache_flags + remote_path_flags + umask_flags + \
-        verbosity_flags
+        verbosity_flags + dry_run_flags
 
     logger.debug(' '.join(full_command))
     subprocess.check_call(full_command)

+ 5 - 4
borgmatic/borg/prune.py

@@ -32,16 +32,17 @@ def _make_prune_flags(retention_config):
     )
 
 
-def prune_archives(verbosity, repository, retention_config, local_path='borg', remote_path=None):
+def prune_archives(verbosity, dry_run, repository, retention_config, local_path='borg', remote_path=None):
     '''
-    Given a verbosity flag, a local or remote repository path, a retention config dict, prune Borg
-    archives according the the retention policy specified in that configuration.
+    Given verbosity/dry-run flags, a local or remote repository path, a retention config dict, prune
+    Borg archives according the the retention policy specified in that configuration.
     '''
     remote_path_flags = ('--remote-path', remote_path) if remote_path else ()
     verbosity_flags = {
         VERBOSITY_SOME: ('--info', '--stats',),
         VERBOSITY_LOTS: ('--debug', '--stats', '--list'),
     }.get(verbosity, ())
+    dry_run_flags = ('--dry-run',) if dry_run else ()
 
     full_command = (
         local_path, 'prune',
@@ -50,7 +51,7 @@ def prune_archives(verbosity, repository, retention_config, local_path='borg', r
         element
         for pair in _make_prune_flags(retention_config)
         for element in pair
-    ) + remote_path_flags + verbosity_flags
+    ) + remote_path_flags + verbosity_flags + dry_run_flags
 
     logger.debug(' '.join(full_command))
     subprocess.check_call(full_command)

+ 11 - 2
borgmatic/commands/borgmatic.py

@@ -61,6 +61,12 @@ def parse_arguments(*arguments):
         action='store_true',
         help='Check archives for consistency',
     )
+    parser.add_argument(
+        '-n', '--dry-run',
+        dest='dry_run',
+        action='store_true',
+        help='Go through the motions, but do not actually write any changes to the repository',
+    )
     parser.add_argument(
         '-v', '--verbosity',
         type=int,
@@ -100,19 +106,22 @@ def run_configuration(config_filename, args):  # pragma: no cover
 
         for unexpanded_repository in location['repositories']:
             repository = os.path.expanduser(unexpanded_repository)
+            dry_run_label = ' (dry run; not making any changes)' if args.dry_run else ''
             if args.prune:
-                logger.info('{}: Pruning archives'.format(repository))
+                logger.info('{}: Pruning archives{}'.format(repository, dry_run_label))
                 prune.prune_archives(
                     args.verbosity,
+                    args.dry_run,
                     repository,
                     retention,
                     local_path=local_path,
                     remote_path=remote_path,
                 )
             if args.create:
-                logger.info('{}: Creating archive'.format(repository))
+                logger.info('{}: Creating archive{}'.format(repository, dry_run_label))
                 create.create_archive(
                     args.verbosity,
+                    args.dry_run,
                     repository,
                     location,
                     storage,

+ 38 - 0
borgmatic/tests/unit/borg/test_create.py

@@ -190,6 +190,7 @@ def test_create_archive_calls_borg_with_parameters():
 
     module.create_archive(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         location_config={
             'source_directories': ['foo', 'bar'],
@@ -210,6 +211,7 @@ def test_create_archive_with_patterns_calls_borg_with_patterns():
 
     module.create_archive(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         location_config={
             'source_directories': ['foo', 'bar'],
@@ -230,6 +232,7 @@ def test_create_archive_with_exclude_patterns_calls_borg_with_excludes():
 
     module.create_archive(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         location_config={
             'source_directories': ['foo', 'bar'],
@@ -250,6 +253,7 @@ def test_create_archive_with_verbosity_some_calls_borg_with_info_parameter():
 
     module.create_archive(
         verbosity=VERBOSITY_SOME,
+        dry_run=False,
         repository='repo',
         location_config={
             'source_directories': ['foo', 'bar'],
@@ -269,6 +273,28 @@ def test_create_archive_with_verbosity_lots_calls_borg_with_debug_parameter():
 
     module.create_archive(
         verbosity=VERBOSITY_LOTS,
+        dry_run=False,
+        repository='repo',
+        location_config={
+            'source_directories': ['foo', 'bar'],
+            'repositories': ['repo'],
+            'exclude_patterns': None,
+        },
+        storage_config={},
+    )
+
+
+def test_create_archive_with_dry_run_calls_borg_with_dry_run_parameter():
+    flexmock(module).should_receive('_expand_directory').and_return(['foo']).and_return(['bar'])
+    flexmock(module).should_receive('_write_pattern_file').and_return(None)
+    flexmock(module).should_receive('_make_pattern_flags').and_return(())
+    flexmock(module).should_receive('_make_pattern_flags').and_return(())
+    flexmock(module).should_receive('_make_exclude_flags').and_return(())
+    insert_subprocess_mock(CREATE_COMMAND + ('--dry-run',))
+
+    module.create_archive(
+        verbosity=None,
+        dry_run=True,
         repository='repo',
         location_config={
             'source_directories': ['foo', 'bar'],
@@ -288,6 +314,7 @@ def test_create_archive_with_compression_calls_borg_with_compression_parameters(
 
     module.create_archive(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         location_config={
             'source_directories': ['foo', 'bar'],
@@ -307,6 +334,7 @@ def test_create_archive_with_remote_rate_limit_calls_borg_with_remote_ratelimit_
 
     module.create_archive(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         location_config={
             'source_directories': ['foo', 'bar'],
@@ -326,6 +354,7 @@ def test_create_archive_with_one_file_system_calls_borg_with_one_file_system_par
 
     module.create_archive(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         location_config={
             'source_directories': ['foo', 'bar'],
@@ -346,6 +375,7 @@ def test_create_archive_with_files_cache_calls_borg_with_files_cache_parameters(
 
     module.create_archive(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         location_config={
             'source_directories': ['foo', 'bar'],
@@ -366,6 +396,7 @@ def test_create_archive_with_local_path_calls_borg_via_local_path():
 
     module.create_archive(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         location_config={
             'source_directories': ['foo', 'bar'],
@@ -386,6 +417,7 @@ def test_create_archive_with_remote_path_calls_borg_with_remote_path_parameters(
 
     module.create_archive(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         location_config={
             'source_directories': ['foo', 'bar'],
@@ -406,6 +438,7 @@ def test_create_archive_with_umask_calls_borg_with_umask_parameters():
 
     module.create_archive(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         location_config={
             'source_directories': ['foo', 'bar'],
@@ -426,6 +459,7 @@ def test_create_archive_with_source_directories_glob_expands():
 
     module.create_archive(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         location_config={
             'source_directories': ['foo*'],
@@ -446,6 +480,7 @@ def test_create_archive_with_non_matching_source_directories_glob_passes_through
 
     module.create_archive(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         location_config={
             'source_directories': ['foo*'],
@@ -465,6 +500,7 @@ def test_create_archive_with_glob_calls_borg_with_expanded_directories():
 
     module.create_archive(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         location_config={
             'source_directories': ['foo*'],
@@ -484,6 +520,7 @@ def test_create_archive_with_archive_name_format_calls_borg_with_archive_name():
 
     module.create_archive(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         location_config={
             'source_directories': ['foo', 'bar'],
@@ -505,6 +542,7 @@ def test_create_archive_with_archive_name_format_accepts_borg_placeholders():
 
     module.create_archive(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         location_config={
             'source_directories': ['foo', 'bar'],

+ 20 - 0
borgmatic/tests/unit/borg/test_prune.py

@@ -64,6 +64,7 @@ def test_prune_archives_calls_borg_with_parameters():
 
     module.prune_archives(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         retention_config=retention_config,
     )
@@ -79,6 +80,7 @@ def test_prune_archives_with_verbosity_some_calls_borg_with_info_parameter():
     module.prune_archives(
         repository='repo',
         verbosity=VERBOSITY_SOME,
+        dry_run=False,
         retention_config=retention_config,
     )
 
@@ -93,6 +95,22 @@ def test_prune_archives_with_verbosity_lots_calls_borg_with_debug_parameter():
     module.prune_archives(
         repository='repo',
         verbosity=VERBOSITY_LOTS,
+        dry_run=False,
+        retention_config=retention_config,
+    )
+
+
+def test_prune_archives_with_dry_run_calls_borg_with_dry_run_parameter():
+    retention_config = flexmock()
+    flexmock(module).should_receive('_make_prune_flags').with_args(retention_config).and_return(
+        BASE_PRUNE_FLAGS,
+    )
+    insert_subprocess_mock(PRUNE_COMMAND + ('--dry-run',))
+
+    module.prune_archives(
+        repository='repo',
+        verbosity=None,
+        dry_run=True,
         retention_config=retention_config,
     )
 
@@ -106,6 +124,7 @@ def test_prune_archives_with_local_path_calls_borg_via_local_path():
 
     module.prune_archives(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         retention_config=retention_config,
         local_path='borg1',
@@ -121,6 +140,7 @@ def test_prune_archives_with_remote_path_calls_borg_with_remote_path_parameters(
 
     module.prune_archives(
         verbosity=None,
+        dry_run=False,
         repository='repo',
         retention_config=retention_config,
         remote_path='borg1',