2
0
Эх сурвалжийг харах

Remove upgrade-borgmatic-config command for upgrading borgmatic 1.1.0 INI-style configuration (#529).

Dan Helfman 1 жил өмнө
parent
commit
b10aee3070

+ 2 - 0
NEWS

@@ -1,5 +1,7 @@
 1.7.15.dev0
  * #399: Add a documentation troubleshooting note for MySQL/MariaDB authentication errors.
+ * #529: Remove upgrade-borgmatic-config command for upgrading borgmatic 1.1.0 INI-style
+   configuration.
  * #697, #712: Extract borgmatic configuration from backup via "bootstrap" action—even when
    borgmatic has no configuration yet!
  * #669: Add sample systemd user service for running borgmatic as a non-root user.

+ 0 - 102
borgmatic/commands/convert_config.py

@@ -1,102 +0,0 @@
-import os
-import sys
-import textwrap
-from argparse import ArgumentParser
-
-from ruamel import yaml
-
-from borgmatic.config import convert, generate, legacy, validate
-
-DEFAULT_SOURCE_CONFIG_FILENAME = '/etc/borgmatic/config'
-DEFAULT_SOURCE_EXCLUDES_FILENAME = '/etc/borgmatic/excludes'
-DEFAULT_DESTINATION_CONFIG_FILENAME = '/etc/borgmatic/config.yaml'
-
-
-def parse_arguments(*arguments):
-    '''
-    Given command-line arguments with which this script was invoked, parse the arguments and return
-    them as an ArgumentParser instance.
-    '''
-    parser = ArgumentParser(
-        description='''
-            Convert legacy INI-style borgmatic configuration and excludes files to a single YAML
-            configuration file. Note that this replaces any comments from the source files.
-        '''
-    )
-    parser.add_argument(
-        '-s',
-        '--source-config',
-        dest='source_config_filename',
-        default=DEFAULT_SOURCE_CONFIG_FILENAME,
-        help=f'Source INI-style configuration filename. Default: {DEFAULT_SOURCE_CONFIG_FILENAME}',
-    )
-    parser.add_argument(
-        '-e',
-        '--source-excludes',
-        dest='source_excludes_filename',
-        default=DEFAULT_SOURCE_EXCLUDES_FILENAME
-        if os.path.exists(DEFAULT_SOURCE_EXCLUDES_FILENAME)
-        else None,
-        help='Excludes filename',
-    )
-    parser.add_argument(
-        '-d',
-        '--destination-config',
-        dest='destination_config_filename',
-        default=DEFAULT_DESTINATION_CONFIG_FILENAME,
-        help=f'Destination YAML configuration filename. Default: {DEFAULT_DESTINATION_CONFIG_FILENAME}',
-    )
-
-    return parser.parse_args(arguments)
-
-
-TEXT_WRAP_CHARACTERS = 80
-
-
-def display_result(args):  # pragma: no cover
-    result_lines = textwrap.wrap(
-        f'Your borgmatic configuration has been upgraded. Please review the result in {args.destination_config_filename}.',
-        TEXT_WRAP_CHARACTERS,
-    )
-
-    excludes_phrase = (
-        f' and {args.source_excludes_filename}' if args.source_excludes_filename else ''
-    )
-    delete_lines = textwrap.wrap(
-        f'Once you are satisfied, you can safely delete {args.source_config_filename}{excludes_phrase}.',
-        TEXT_WRAP_CHARACTERS,
-    )
-
-    print('\n'.join(result_lines))
-    print()
-    print('\n'.join(delete_lines))
-
-
-def main():  # pragma: no cover
-    try:
-        args = parse_arguments(*sys.argv[1:])
-        schema = yaml.round_trip_load(open(validate.schema_filename()).read())
-        source_config = legacy.parse_configuration(
-            args.source_config_filename, legacy.CONFIG_FORMAT
-        )
-        source_config_file_mode = os.stat(args.source_config_filename).st_mode
-        source_excludes = (
-            open(args.source_excludes_filename).read().splitlines()
-            if args.source_excludes_filename
-            else []
-        )
-
-        destination_config = convert.convert_legacy_parsed_config(
-            source_config, source_excludes, schema
-        )
-
-        generate.write_configuration(
-            args.destination_config_filename,
-            generate.render_configuration(destination_config),
-            mode=source_config_file_mode,
-        )
-
-        display_result(args)
-    except (ValueError, OSError) as error:
-        print(error, file=sys.stderr)
-        sys.exit(1)

+ 0 - 95
borgmatic/config/convert.py

@@ -1,95 +0,0 @@
-import os
-
-from ruamel import yaml
-
-from borgmatic.config import generate
-
-
-def _convert_section(source_section_config, section_schema):
-    '''
-    Given a legacy Parsed_config instance for a single section, convert it to its corresponding
-    yaml.comments.CommentedMap representation in preparation for actual serialization to YAML.
-
-    Where integer types exist in the given section schema, convert their values to integers.
-    '''
-    destination_section_config = yaml.comments.CommentedMap(
-        [
-            (
-                option_name,
-                int(option_value)
-                if section_schema['properties'].get(option_name, {}).get('type') == 'integer'
-                else option_value,
-            )
-            for option_name, option_value in source_section_config.items()
-        ]
-    )
-
-    return destination_section_config
-
-
-def convert_legacy_parsed_config(source_config, source_excludes, schema):
-    '''
-    Given a legacy Parsed_config instance loaded from an INI-style config file and a list of exclude
-    patterns, convert them to a corresponding yaml.comments.CommentedMap representation in
-    preparation for serialization to a single YAML config file.
-
-    Additionally, use the given schema as a source of helpful comments to include within the
-    returned CommentedMap.
-    '''
-    destination_config = yaml.comments.CommentedMap(
-        [
-            (section_name, _convert_section(section_config, schema['properties'][section_name]))
-            for section_name, section_config in source_config._asdict().items()
-        ]
-    )
-
-    # Split space-separated values into actual lists, make "repository" into a list, and merge in
-    # excludes.
-    location = destination_config['location']
-    location['source_directories'] = source_config.location['source_directories'].split(' ')
-    location['repositories'] = [location.pop('repository')]
-    location['exclude_patterns'] = source_excludes
-
-    if source_config.consistency.get('checks'):
-        destination_config['consistency']['checks'] = source_config.consistency['checks'].split(' ')
-
-    # Add comments to each section, and then add comments to the fields in each section.
-    generate.add_comments_to_configuration_object(destination_config, schema)
-
-    for section_name, section_config in destination_config.items():
-        generate.add_comments_to_configuration_object(
-            section_config, schema['properties'][section_name], indent=generate.INDENT
-        )
-
-    return destination_config
-
-
-class Legacy_configuration_not_upgraded(FileNotFoundError):
-    def __init__(self):
-        super(Legacy_configuration_not_upgraded, self).__init__(
-            '''borgmatic changed its configuration file format in version 1.1.0 from INI-style
-to YAML. This better supports validation, and has a more natural way to express
-lists of values. To upgrade your existing configuration, run:
-
-    sudo upgrade-borgmatic-config
-
-That will generate a new YAML configuration file at /etc/borgmatic/config.yaml
-(by default) using the values from both your existing configuration and excludes
-files. The new version of borgmatic will consume the YAML configuration file
-instead of the old one.'''
-        )
-
-
-def guard_configuration_upgraded(source_config_filename, destination_config_filenames):
-    '''
-    If legacy source configuration exists but no destination upgraded configs do, raise
-    Legacy_configuration_not_upgraded.
-
-    The idea is that we want to alert the user about upgrading their config if they haven't already.
-    '''
-    destination_config_exists = any(
-        os.path.exists(filename) for filename in destination_config_filenames
-    )
-
-    if os.path.exists(source_config_filename) and not destination_config_exists:
-        raise Legacy_configuration_not_upgraded()

+ 5 - 21
docs/how-to/upgrade.md

@@ -61,21 +61,22 @@ and, if desired, replace your original configuration file with it.
 borgmatic changed its configuration file format in version 1.1.0 from
 INI-style to YAML. This better supports validation, and has a more natural way
 to express lists of values. To upgrade your existing configuration, first
-upgrade to the new version of borgmatic.
+upgrade to the last version of borgmatic to support converting configuration:
+borgmatic 1.7.14.
 
 As of version 1.1.0, borgmatic no longer supports Python 2. If you were
 already running borgmatic with Python 3, then you can upgrade borgmatic
 in-place:
 
 ```bash
-sudo pip3 install --user --upgrade borgmatic
+sudo pip3 install --user --upgrade borgmatic==1.7.14
 ```
 
 But if you were running borgmatic with Python 2, uninstall and reinstall instead:
 
 ```bash
 sudo pip uninstall borgmatic
-sudo pip3 install --user borgmatic
+sudo pip3 install --user borgmatic==1.7.14
 ```
 
 The pip binary names for different versions of Python can differ, so the above
@@ -93,29 +94,12 @@ That will generate a new YAML configuration file at /etc/borgmatic/config.yaml
 excludes files. The new version of borgmatic will consume the YAML
 configuration file instead of the old one.
 
-
-### Upgrading from atticmatic
-
-You can ignore this section if you're not an atticmatic user (the former name
-of borgmatic).
-
-borgmatic only supports Borg now and no longer supports Attic. So if you're
-an Attic user, consider switching to Borg. See the [Borg upgrade
-command](https://borgbackup.readthedocs.io/en/stable/usage.html#borg-upgrade)
-for more information. Then, follow the instructions above about setting up
-your borgmatic configuration files.
-
-If you were already using Borg with atticmatic, then you can upgrade
-from atticmatic to borgmatic by running the following commands:
+Now you can upgrade to a newer version of borgmatic:
 
 ```bash
-sudo pip3 uninstall atticmatic
 sudo pip3 install --user borgmatic
 ```
 
-That's it! borgmatic will continue using your /etc/borgmatic configuration
-files.
-
 
 ## Upgrading Borg
 

+ 0 - 1
setup.py

@@ -23,7 +23,6 @@ setup(
     entry_points={
         'console_scripts': [
             'borgmatic = borgmatic.commands.borgmatic:main',
-            'upgrade-borgmatic-config = borgmatic.commands.convert_config:main',
             'generate-borgmatic-config = borgmatic.commands.generate_config:main',
             'validate-borgmatic-config = borgmatic.commands.validate_config:main',
         ]

+ 0 - 50
tests/integration/commands/test_convert_config.py

@@ -1,50 +0,0 @@
-import os
-
-import pytest
-from flexmock import flexmock
-
-from borgmatic.commands import convert_config as module
-
-
-def test_parse_arguments_with_no_arguments_uses_defaults():
-    flexmock(os.path).should_receive('exists').and_return(True)
-
-    parser = module.parse_arguments()
-
-    assert parser.source_config_filename == module.DEFAULT_SOURCE_CONFIG_FILENAME
-    assert parser.source_excludes_filename == module.DEFAULT_SOURCE_EXCLUDES_FILENAME
-    assert parser.destination_config_filename == module.DEFAULT_DESTINATION_CONFIG_FILENAME
-
-
-def test_parse_arguments_with_filename_arguments_overrides_defaults():
-    flexmock(os.path).should_receive('exists').and_return(True)
-
-    parser = module.parse_arguments(
-        '--source-config',
-        'config',
-        '--source-excludes',
-        'excludes',
-        '--destination-config',
-        'config.yaml',
-    )
-
-    assert parser.source_config_filename == 'config'
-    assert parser.source_excludes_filename == 'excludes'
-    assert parser.destination_config_filename == 'config.yaml'
-
-
-def test_parse_arguments_with_missing_default_excludes_file_sets_filename_to_none():
-    flexmock(os.path).should_receive('exists').and_return(False)
-
-    parser = module.parse_arguments()
-
-    assert parser.source_config_filename == module.DEFAULT_SOURCE_CONFIG_FILENAME
-    assert parser.source_excludes_filename is None
-    assert parser.destination_config_filename == module.DEFAULT_DESTINATION_CONFIG_FILENAME
-
-
-def test_parse_arguments_with_invalid_arguments_exits():
-    flexmock(os.path).should_receive('exists').and_return(True)
-
-    with pytest.raises(SystemExit):
-        module.parse_arguments('--posix-me-harder')

+ 0 - 126
tests/unit/config/test_convert.py

@@ -1,126 +0,0 @@
-import os
-from collections import OrderedDict, defaultdict, namedtuple
-
-import pytest
-from flexmock import flexmock
-
-from borgmatic.config import convert as module
-
-Parsed_config = namedtuple('Parsed_config', ('location', 'storage', 'retention', 'consistency'))
-
-
-def test_convert_section_generates_integer_value_for_integer_type_in_schema():
-    flexmock(module.yaml.comments).should_receive('CommentedMap').replace_with(OrderedDict)
-    source_section_config = OrderedDict([('check_last', '3')])
-    section_schema = {'type': 'object', 'properties': {'check_last': {'type': 'integer'}}}
-
-    destination_config = module._convert_section(source_section_config, section_schema)
-
-    assert destination_config == OrderedDict([('check_last', 3)])
-
-
-def test_convert_legacy_parsed_config_transforms_source_config_to_mapping():
-    flexmock(module.yaml.comments).should_receive('CommentedMap').replace_with(OrderedDict)
-    flexmock(module.generate).should_receive('add_comments_to_configuration_object')
-    source_config = Parsed_config(
-        location=OrderedDict([('source_directories', '/home'), ('repository', 'hostname.borg')]),
-        storage=OrderedDict([('encryption_passphrase', 'supersecret')]),
-        retention=OrderedDict([('keep_daily', 7)]),
-        consistency=OrderedDict([('checks', 'repository')]),
-    )
-    source_excludes = ['/var']
-    schema = {
-        'type': 'object',
-        'properties': defaultdict(lambda: {'type': 'object', 'properties': {}}),
-    }
-
-    destination_config = module.convert_legacy_parsed_config(source_config, source_excludes, schema)
-
-    assert destination_config == OrderedDict(
-        [
-            (
-                'location',
-                OrderedDict(
-                    [
-                        ('source_directories', ['/home']),
-                        ('repositories', ['hostname.borg']),
-                        ('exclude_patterns', ['/var']),
-                    ]
-                ),
-            ),
-            ('storage', OrderedDict([('encryption_passphrase', 'supersecret')])),
-            ('retention', OrderedDict([('keep_daily', 7)])),
-            ('consistency', OrderedDict([('checks', ['repository'])])),
-        ]
-    )
-
-
-def test_convert_legacy_parsed_config_splits_space_separated_values():
-    flexmock(module.yaml.comments).should_receive('CommentedMap').replace_with(OrderedDict)
-    flexmock(module.generate).should_receive('add_comments_to_configuration_object')
-    source_config = Parsed_config(
-        location=OrderedDict(
-            [('source_directories', '/home /etc'), ('repository', 'hostname.borg')]
-        ),
-        storage=OrderedDict(),
-        retention=OrderedDict(),
-        consistency=OrderedDict([('checks', 'repository archives')]),
-    )
-    source_excludes = ['/var']
-    schema = {
-        'type': 'object',
-        'properties': defaultdict(lambda: {'type': 'object', 'properties': {}}),
-    }
-
-    destination_config = module.convert_legacy_parsed_config(source_config, source_excludes, schema)
-
-    assert destination_config == OrderedDict(
-        [
-            (
-                'location',
-                OrderedDict(
-                    [
-                        ('source_directories', ['/home', '/etc']),
-                        ('repositories', ['hostname.borg']),
-                        ('exclude_patterns', ['/var']),
-                    ]
-                ),
-            ),
-            ('storage', OrderedDict()),
-            ('retention', OrderedDict()),
-            ('consistency', OrderedDict([('checks', ['repository', 'archives'])])),
-        ]
-    )
-
-
-def test_guard_configuration_upgraded_raises_when_only_source_config_present():
-    flexmock(os.path).should_receive('exists').with_args('config').and_return(True)
-    flexmock(os.path).should_receive('exists').with_args('config.yaml').and_return(False)
-    flexmock(os.path).should_receive('exists').with_args('other.yaml').and_return(False)
-
-    with pytest.raises(module.Legacy_configuration_not_upgraded):
-        module.guard_configuration_upgraded('config', ('config.yaml', 'other.yaml'))
-
-
-def test_guard_configuration_upgraded_does_not_raise_when_only_destination_config_present():
-    flexmock(os.path).should_receive('exists').with_args('config').and_return(False)
-    flexmock(os.path).should_receive('exists').with_args('config.yaml').and_return(False)
-    flexmock(os.path).should_receive('exists').with_args('other.yaml').and_return(True)
-
-    module.guard_configuration_upgraded('config', ('config.yaml', 'other.yaml'))
-
-
-def test_guard_configuration_upgraded_does_not_raise_when_both_configs_present():
-    flexmock(os.path).should_receive('exists').with_args('config').and_return(True)
-    flexmock(os.path).should_receive('exists').with_args('config.yaml').and_return(False)
-    flexmock(os.path).should_receive('exists').with_args('other.yaml').and_return(True)
-
-    module.guard_configuration_upgraded('config', ('config.yaml', 'other.yaml'))
-
-
-def test_guard_configuration_upgraded_does_not_raise_when_neither_config_present():
-    flexmock(os.path).should_receive('exists').with_args('config').and_return(False)
-    flexmock(os.path).should_receive('exists').with_args('config.yaml').and_return(False)
-    flexmock(os.path).should_receive('exists').with_args('other.yaml').and_return(False)
-
-    module.guard_configuration_upgraded('config', ('config.yaml', 'other.yaml'))