Add "generate-borgmatic-config --overwrite" flag to replace an existing destination file (#539).
This commit is contained in:
parent
5b615d51a4
commit
2bc91ac3d2
5 changed files with 50 additions and 13 deletions
1
NEWS
1
NEWS
|
@ -6,6 +6,7 @@
|
||||||
* #536: Fix generate-borgmatic-config to support more complex schema changes like the new
|
* #536: Fix generate-borgmatic-config to support more complex schema changes like the new
|
||||||
Healthchecks configuration options when the "--source" flag is used.
|
Healthchecks configuration options when the "--source" flag is used.
|
||||||
* #538: Add support for "borgmatic borg debug" command.
|
* #538: Add support for "borgmatic borg debug" command.
|
||||||
|
* #539: Add "generate-borgmatic-config --overwrite" flag to replace an existing destination file.
|
||||||
* Add Bash completion script so you can tab-complete the borgmatic command-line. See the
|
* Add Bash completion script so you can tab-complete the borgmatic command-line. See the
|
||||||
documentation for more information:
|
documentation for more information:
|
||||||
https://torsion.org/borgmatic/docs/how-to/set-up-backups/#shell-completion
|
https://torsion.org/borgmatic/docs/how-to/set-up-backups/#shell-completion
|
||||||
|
|
|
@ -23,10 +23,16 @@ def parse_arguments(*arguments):
|
||||||
'--destination',
|
'--destination',
|
||||||
dest='destination_filename',
|
dest='destination_filename',
|
||||||
default=DEFAULT_DESTINATION_CONFIG_FILENAME,
|
default=DEFAULT_DESTINATION_CONFIG_FILENAME,
|
||||||
help='Destination YAML configuration file. Default: {}'.format(
|
help='Destination YAML configuration file, default: {}'.format(
|
||||||
DEFAULT_DESTINATION_CONFIG_FILENAME
|
DEFAULT_DESTINATION_CONFIG_FILENAME
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--overwrite',
|
||||||
|
default=False,
|
||||||
|
action='store_true',
|
||||||
|
help='Whether to overwrite any existing destination file, defaults to false',
|
||||||
|
)
|
||||||
|
|
||||||
return parser.parse_args(arguments)
|
return parser.parse_args(arguments)
|
||||||
|
|
||||||
|
@ -36,7 +42,10 @@ def main(): # pragma: no cover
|
||||||
args = parse_arguments(*sys.argv[1:])
|
args = parse_arguments(*sys.argv[1:])
|
||||||
|
|
||||||
generate.generate_sample_configuration(
|
generate.generate_sample_configuration(
|
||||||
args.source_filename, args.destination_filename, validate.schema_filename()
|
args.source_filename,
|
||||||
|
args.destination_filename,
|
||||||
|
validate.schema_filename(),
|
||||||
|
overwrite=args.overwrite,
|
||||||
)
|
)
|
||||||
|
|
||||||
print('Generated a sample configuration file at {}.'.format(args.destination_filename))
|
print('Generated a sample configuration file at {}.'.format(args.destination_filename))
|
||||||
|
|
|
@ -109,13 +109,18 @@ def render_configuration(config):
|
||||||
return rendered.getvalue()
|
return rendered.getvalue()
|
||||||
|
|
||||||
|
|
||||||
def write_configuration(config_filename, rendered_config, mode=0o600):
|
def write_configuration(config_filename, rendered_config, mode=0o600, overwrite=False):
|
||||||
'''
|
'''
|
||||||
Given a target config filename and rendered config YAML, write it out to file. Create any
|
Given a target config filename and rendered config YAML, write it out to file. Create any
|
||||||
containing directories as needed.
|
containing directories as needed. But if the file already exists and overwrite is False,
|
||||||
|
abort before writing anything.
|
||||||
'''
|
'''
|
||||||
if os.path.exists(config_filename):
|
if not overwrite and os.path.exists(config_filename):
|
||||||
raise FileExistsError('{} already exists. Aborting.'.format(config_filename))
|
raise FileExistsError(
|
||||||
|
'{} already exists. Aborting. Use --overwrite to replace the file.'.format(
|
||||||
|
config_filename
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.makedirs(os.path.dirname(config_filename), mode=0o700)
|
os.makedirs(os.path.dirname(config_filename), mode=0o700)
|
||||||
|
@ -263,12 +268,15 @@ def merge_source_configuration_into_destination(destination_config, source_confi
|
||||||
return destination_config
|
return destination_config
|
||||||
|
|
||||||
|
|
||||||
def generate_sample_configuration(source_filename, destination_filename, schema_filename):
|
def generate_sample_configuration(
|
||||||
|
source_filename, destination_filename, schema_filename, overwrite=False
|
||||||
|
):
|
||||||
'''
|
'''
|
||||||
Given an optional source configuration filename, and a required destination configuration
|
Given an optional source configuration filename, and a required destination configuration
|
||||||
filename, and the path to a schema filename in a YAML rendition of the JSON Schema format,
|
filename, the path to a schema filename in a YAML rendition of the JSON Schema format, and
|
||||||
write out a sample configuration file based on that schema. If a source filename is provided,
|
whether to overwrite a destination file, write out a sample configuration file based on that
|
||||||
merge the parsed contents of that configuration into the generated configuration.
|
schema. If a source filename is provided, merge the parsed contents of that configuration into
|
||||||
|
the generated configuration.
|
||||||
'''
|
'''
|
||||||
schema = yaml.round_trip_load(open(schema_filename))
|
schema = yaml.round_trip_load(open(schema_filename))
|
||||||
source_config = None
|
source_config = None
|
||||||
|
@ -284,4 +292,5 @@ def generate_sample_configuration(source_filename, destination_filename, schema_
|
||||||
write_configuration(
|
write_configuration(
|
||||||
destination_filename,
|
destination_filename,
|
||||||
_comment_out_optional_configuration(render_configuration(destination_config)),
|
_comment_out_optional_configuration(render_configuration(destination_config)),
|
||||||
|
overwrite=overwrite,
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,13 +1,25 @@
|
||||||
from borgmatic.commands import generate_config as module
|
from borgmatic.commands import generate_config as module
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_no_arguments_uses_defaults():
|
def test_parse_arguments_with_no_arguments_uses_default_destination():
|
||||||
parser = module.parse_arguments()
|
parser = module.parse_arguments()
|
||||||
|
|
||||||
assert parser.destination_filename == module.DEFAULT_DESTINATION_CONFIG_FILENAME
|
assert parser.destination_filename == module.DEFAULT_DESTINATION_CONFIG_FILENAME
|
||||||
|
|
||||||
|
|
||||||
def test_parse_arguments_with_filename_argument_overrides_defaults():
|
def test_parse_arguments_with_destination_argument_overrides_default():
|
||||||
parser = module.parse_arguments('--destination', 'config.yaml')
|
parser = module.parse_arguments('--destination', 'config.yaml')
|
||||||
|
|
||||||
assert parser.destination_filename == 'config.yaml'
|
assert parser.destination_filename == 'config.yaml'
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_parses_source():
|
||||||
|
parser = module.parse_arguments('--source', 'source.yaml', '--destination', 'config.yaml')
|
||||||
|
|
||||||
|
assert parser.source_filename == 'source.yaml'
|
||||||
|
|
||||||
|
|
||||||
|
def test_parse_arguments_parses_overwrite():
|
||||||
|
parser = module.parse_arguments('--destination', 'config.yaml', '--overwrite')
|
||||||
|
|
||||||
|
assert parser.overwrite
|
||||||
|
|
|
@ -87,7 +87,7 @@ location:
|
||||||
assert module._comment_out_optional_configuration(config.strip()) == expected_config.strip()
|
assert module._comment_out_optional_configuration(config.strip()) == expected_config.strip()
|
||||||
|
|
||||||
|
|
||||||
def testrender_configuration_converts_configuration_to_yaml_string():
|
def test_render_configuration_converts_configuration_to_yaml_string():
|
||||||
yaml_string = module.render_configuration({'foo': 'bar'})
|
yaml_string = module.render_configuration({'foo': 'bar'})
|
||||||
|
|
||||||
assert yaml_string == 'foo: bar\n'
|
assert yaml_string == 'foo: bar\n'
|
||||||
|
@ -110,6 +110,12 @@ def test_write_configuration_with_already_existing_file_raises():
|
||||||
module.write_configuration('config.yaml', 'config: yaml')
|
module.write_configuration('config.yaml', 'config: yaml')
|
||||||
|
|
||||||
|
|
||||||
|
def test_write_configuration_with_already_existing_file_and_overwrite_does_not_raise():
|
||||||
|
flexmock(os.path).should_receive('exists').and_return(True)
|
||||||
|
|
||||||
|
module.write_configuration('config.yaml', 'config: yaml', overwrite=True)
|
||||||
|
|
||||||
|
|
||||||
def test_write_configuration_with_already_existing_directory_does_not_raise():
|
def test_write_configuration_with_already_existing_directory_does_not_raise():
|
||||||
flexmock(os.path).should_receive('exists').and_return(False)
|
flexmock(os.path).should_receive('exists').and_return(False)
|
||||||
flexmock(os).should_receive('makedirs').and_raise(FileExistsError)
|
flexmock(os).should_receive('makedirs').and_raise(FileExistsError)
|
||||||
|
|
Loading…
Reference in a new issue