mirror of
https://github.com/borgmatic-collective/borgmatic.git
synced 2026-07-27 20:03:01 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef76e87477 | ||
|
|
62526038d6 | ||
|
|
bf2f39623e |
@@ -1,3 +1,7 @@
|
||||
1.1.14
|
||||
* #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",
|
||||
"archives", and "extract") are specified in borgmatic configuration.
|
||||
|
||||
@@ -57,7 +57,7 @@ def _make_pattern_flags(location_config, pattern_filename=None):
|
||||
|
||||
return tuple(
|
||||
itertools.chain.from_iterable(
|
||||
('--pattern-from', pattern_filename)
|
||||
('--patterns-from', pattern_filename)
|
||||
for pattern_filename in pattern_filenames
|
||||
)
|
||||
)
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -84,7 +84,7 @@ def test_make_pattern_flags_includes_pattern_filename_when_given():
|
||||
pattern_filename='/tmp/patterns',
|
||||
)
|
||||
|
||||
assert pattern_flags == ('--pattern-from', '/tmp/patterns')
|
||||
assert pattern_flags == ('--patterns-from', '/tmp/patterns')
|
||||
|
||||
|
||||
def test_make_pattern_flags_includes_patterns_from_filenames_when_in_config():
|
||||
@@ -92,7 +92,7 @@ def test_make_pattern_flags_includes_patterns_from_filenames_when_in_config():
|
||||
location_config={'patterns_from': ['patterns', 'other']},
|
||||
)
|
||||
|
||||
assert pattern_flags == ('--pattern-from', 'patterns', '--pattern-from', 'other')
|
||||
assert pattern_flags == ('--patterns-from', 'patterns', '--patterns-from', 'other')
|
||||
|
||||
|
||||
def test_make_pattern_flags_includes_both_filenames_when_patterns_given_and_patterns_from_in_config():
|
||||
@@ -101,7 +101,7 @@ def test_make_pattern_flags_includes_both_filenames_when_patterns_given_and_patt
|
||||
pattern_filename='/tmp/patterns',
|
||||
)
|
||||
|
||||
assert pattern_flags == ('--pattern-from', 'patterns', '--pattern-from', '/tmp/patterns')
|
||||
assert pattern_flags == ('--patterns-from', 'patterns', '--patterns-from', '/tmp/patterns')
|
||||
|
||||
|
||||
def test_make_pattern_flags_considers_none_patterns_from_filenames_as_empty():
|
||||
@@ -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'],
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user