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

This commit is contained in:
Dan Helfman 2018-01-15 20:55:27 -08:00
parent bf2f39623e
commit 62526038d6
6 changed files with 81 additions and 11 deletions

3
NEWS
View file

@ -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",

View file

@ -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)

View file

@ -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)

View file

@ -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,

View file

@ -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'],

View file

@ -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',