With --init command-line flag, if a repository already exists, proceed without erroring (#117).

This commit is contained in:
Dan Helfman 2018-12-24 22:28:02 -08:00
parent 14aeddc11f
commit 30b52e5523
6 changed files with 60 additions and 27 deletions

2
NEWS
View file

@ -1,5 +1,7 @@
1.2.13.dev0 1.2.13.dev0
* #100: Support for --stats command-line flag independent of --verbosity. * #100: Support for --stats command-line flag independent of --verbosity.
* #117: With borgmatic --init command-line flag, if a repository already exists, proceed without
erroring.
1.2.12 1.2.12
* #110: Support for Borg repository initialization via borgmatic --init command-line flag. * #110: Support for Borg repository initialization via borgmatic --init command-line flag.

View file

@ -146,6 +146,10 @@ Also, optionally check out the [Borg Quick
Start](https://borgbackup.readthedocs.org/en/latest/quickstart.html) for more Start](https://borgbackup.readthedocs.org/en/latest/quickstart.html) for more
background about repository initialization. background about repository initialization.
Note that borgmatic skips repository initialization if the repository already
exists. This supports use cases like ensuring a repository exists prior to
performing a backup.
If the repository is on a remote host, make sure that your local user has If the repository is on a remote host, make sure that your local user has
key-based SSH access to the desired user account on the remote host. key-based SSH access to the desired user account on the remote host.

View file

@ -15,9 +15,17 @@ def initialize_repository(
): ):
''' '''
Given a local or remote repository path, a Borg encryption mode, whether the repository should Given a local or remote repository path, a Borg encryption mode, whether the repository should
be append-only, and the storage quota to use, initialize the repository. be append-only, and the storage quota to use, initialize the repository. If the repository
already exists, then log and skip initialization.
''' '''
full_command = ( info_command = (local_path, 'info', repository)
logger.debug(' '.join(info_command))
if subprocess.call(info_command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0:
logger.info('Repository already exists. Skipping initialization.')
return
init_command = (
(local_path, 'init', repository) (local_path, 'init', repository)
+ (('--encryption', encryption_mode) if encryption_mode else ()) + (('--encryption', encryption_mode) if encryption_mode else ())
+ (('--append-only',) if append_only else ()) + (('--append-only',) if append_only else ())
@ -27,5 +35,5 @@ def initialize_repository(
+ (('--remote-path', remote_path) if remote_path else ()) + (('--remote-path', remote_path) if remote_path else ())
) )
logger.debug(' '.join(full_command)) logger.debug(' '.join(init_command))
subprocess.check_call(full_command) subprocess.check_call(init_command)

View file

@ -149,10 +149,8 @@ def parse_arguments(*arguments):
'The --encryption, --append-only, and --storage-quota options can only be used with the --init option' 'The --encryption, --append-only, and --storage-quota options can only be used with the --init option'
) )
if args.init and (args.prune or args.create or args.dry_run): if args.init and args.dry_run:
raise ValueError( raise ValueError('The --init option cannot be used with the --dry-run option')
'The --init option cannot be used with the --prune, --create, or --dry-run options'
)
if args.init and not args.encryption_mode: if args.init and not args.encryption_mode:
raise ValueError('The --encryption option is required with the --init option') raise ValueError('The --encryption option is required with the --init option')

View file

@ -113,25 +113,25 @@ def test_parse_arguments_disallows_storage_quota_without_init():
module.parse_arguments('--config', 'myconfig', '--storage-quota', '5G') module.parse_arguments('--config', 'myconfig', '--storage-quota', '5G')
def test_parse_arguments_disallows_init_and_prune(): def test_parse_arguments_allows_init_and_prune():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default']) flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
with pytest.raises(ValueError): module.parse_arguments('--config', 'myconfig', '--init', '--encryption', 'repokey', '--prune')
module.parse_arguments('--config', 'myconfig', '--init', '--prune')
def test_parse_arguments_disallows_init_and_create(): def test_parse_arguments_allows_init_and_create():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default']) flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
with pytest.raises(ValueError): module.parse_arguments('--config', 'myconfig', '--init', '--encryption', 'repokey', '--create')
module.parse_arguments('--config', 'myconfig', '--init', '--create')
def test_parse_arguments_disallows_init_and_dry_run(): def test_parse_arguments_disallows_init_and_dry_run():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default']) flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
with pytest.raises(ValueError): with pytest.raises(ValueError):
module.parse_arguments('--config', 'myconfig', '--init', '--dry-run') module.parse_arguments(
'--config', 'myconfig', '--init', '--encryption', 'repokey', '--dry-run'
)
def test_parse_arguments_allows_progress_and_create(): def test_parse_arguments_allows_progress_and_create():

View file

@ -6,53 +6,74 @@ from borgmatic.borg import init as module
from ..test_verbosity import insert_logging_mock from ..test_verbosity import insert_logging_mock
def insert_subprocess_mock(check_call_command, **kwargs): INFO_REPOSITORY_EXISTS_RESPONSE_CODE = 0
subprocess = flexmock(module.subprocess) INFO_REPOSITORY_NOT_FOUND_RESPONSE_CODE = 2
subprocess.should_receive('check_call').with_args(check_call_command, **kwargs).once()
INIT_COMMAND = ('borg', 'init', 'repo', '--encryption', 'repokey') INIT_COMMAND = ('borg', 'init', 'repo', '--encryption', 'repokey')
def insert_info_command_mock(info_response):
subprocess = flexmock(module.subprocess)
subprocess.should_receive('call').and_return(info_response)
def insert_init_command_mock(init_command, **kwargs):
subprocess = flexmock(module.subprocess)
subprocess.should_receive('check_call').with_args(init_command, **kwargs).once()
def test_initialize_repository_calls_borg_with_parameters(): def test_initialize_repository_calls_borg_with_parameters():
insert_subprocess_mock(INIT_COMMAND) insert_info_command_mock(INFO_REPOSITORY_NOT_FOUND_RESPONSE_CODE)
insert_init_command_mock(INIT_COMMAND)
module.initialize_repository(repository='repo', encryption_mode='repokey')
def test_initialize_repository_skips_initialization_when_repository_already_exists():
insert_info_command_mock(INFO_REPOSITORY_EXISTS_RESPONSE_CODE)
flexmock(module.subprocess).should_receive('check_call').never()
module.initialize_repository(repository='repo', encryption_mode='repokey') module.initialize_repository(repository='repo', encryption_mode='repokey')
def test_initialize_repository_with_append_only_calls_borg_with_append_only_parameter(): def test_initialize_repository_with_append_only_calls_borg_with_append_only_parameter():
insert_subprocess_mock(INIT_COMMAND + ('--append-only',)) insert_info_command_mock(INFO_REPOSITORY_NOT_FOUND_RESPONSE_CODE)
insert_init_command_mock(INIT_COMMAND + ('--append-only',))
module.initialize_repository(repository='repo', encryption_mode='repokey', append_only=True) module.initialize_repository(repository='repo', encryption_mode='repokey', append_only=True)
def test_initialize_repository_with_storage_quota_calls_borg_with_storage_quota_parameter(): def test_initialize_repository_with_storage_quota_calls_borg_with_storage_quota_parameter():
insert_subprocess_mock(INIT_COMMAND + ('--storage-quota', '5G')) insert_info_command_mock(INFO_REPOSITORY_NOT_FOUND_RESPONSE_CODE)
insert_init_command_mock(INIT_COMMAND + ('--storage-quota', '5G'))
module.initialize_repository(repository='repo', encryption_mode='repokey', storage_quota='5G') module.initialize_repository(repository='repo', encryption_mode='repokey', storage_quota='5G')
def test_initialize_repository_with_log_info_calls_borg_with_info_parameter(): def test_initialize_repository_with_log_info_calls_borg_with_info_parameter():
insert_subprocess_mock(INIT_COMMAND + ('--info',)) insert_info_command_mock(INFO_REPOSITORY_NOT_FOUND_RESPONSE_CODE)
insert_init_command_mock(INIT_COMMAND + ('--info',))
insert_logging_mock(logging.INFO) insert_logging_mock(logging.INFO)
module.initialize_repository(repository='repo', encryption_mode='repokey') module.initialize_repository(repository='repo', encryption_mode='repokey')
def test_initialize_repository_with_log_debug_calls_borg_with_debug_parameter(): def test_initialize_repository_with_log_debug_calls_borg_with_debug_parameter():
insert_subprocess_mock(INIT_COMMAND + ('--debug',)) insert_info_command_mock(INFO_REPOSITORY_NOT_FOUND_RESPONSE_CODE)
insert_init_command_mock(INIT_COMMAND + ('--debug',))
insert_logging_mock(logging.DEBUG) insert_logging_mock(logging.DEBUG)
module.initialize_repository(repository='repo', encryption_mode='repokey') module.initialize_repository(repository='repo', encryption_mode='repokey')
def test_initialize_repository_with_local_path_calls_borg_via_local_path(): def test_initialize_repository_with_local_path_calls_borg_via_local_path():
insert_subprocess_mock(('borg1',) + INIT_COMMAND[1:]) insert_info_command_mock(INFO_REPOSITORY_NOT_FOUND_RESPONSE_CODE)
insert_init_command_mock(('borg1',) + INIT_COMMAND[1:])
module.initialize_repository(repository='repo', encryption_mode='repokey', local_path='borg1') module.initialize_repository(repository='repo', encryption_mode='repokey', local_path='borg1')
def test_initialize_repository_with_remote_path_calls_borg_with_remote_path_parameter(): def test_initialize_repository_with_remote_path_calls_borg_with_remote_path_parameter():
insert_subprocess_mock(INIT_COMMAND + ('--remote-path', 'borg1')) insert_info_command_mock(INFO_REPOSITORY_NOT_FOUND_RESPONSE_CODE)
insert_init_command_mock(INIT_COMMAND + ('--remote-path', 'borg1'))
module.initialize_repository(repository='repo', encryption_mode='repokey', remote_path='borg1') module.initialize_repository(repository='repo', encryption_mode='repokey', remote_path='borg1')