mirror of
https://github.com/borgbackup/borg.git
synced 2024-12-25 17:27:31 +00:00
Add borg config command (fixes #3304)
This command works similarly to "git config" - it parses repo and cache configs to get, set, and delete values. It only works on local repos so a malicious client can't e.g. override their storage quota or reset the append_only flag. Add tests for borg config Add documentation for borg config Change manual config edits -> borg config There were a couple places in the documentation where it was advised to edit the repository or cache config file, a process that is stream- lined by borg config.
This commit is contained in:
parent
0a5d17018e
commit
2ac0bf4980
8 changed files with 124 additions and 11 deletions
|
@ -499,8 +499,7 @@ space for chunks.archive.d (see :issue:`235` for details):
|
|||
::
|
||||
|
||||
# this assumes you are working with the same user as the backup.
|
||||
# you can get the REPOID from the "config" file inside the repository.
|
||||
cd ~/.cache/borg/<REPOID>
|
||||
cd ~/.cache/borg/$(borg config /path/to/repo id)
|
||||
rm -rf chunks.archive.d ; touch chunks.archive.d
|
||||
|
||||
This deletes all the cached archive chunk indexes and replaces the directory
|
||||
|
@ -817,7 +816,7 @@ There are some caveats:
|
|||
- If the repository is in "keyfile" encryption mode, the keyfile must
|
||||
exist locally or it must be manually moved after performing the upgrade:
|
||||
|
||||
1. Locate the repository ID, contained in the ``config`` file in the repository.
|
||||
1. Get the repository ID with ``borg config /path/to/repo id``.
|
||||
2. Locate the attic key file at ``~/.attic/keys/``. The correct key for the
|
||||
repository starts with the line ``ATTIC_KEY <repository id>``.
|
||||
3. Copy the attic key file to ``~/.config/borg/keys/``
|
||||
|
|
|
@ -22,10 +22,11 @@ a good amount of free space on the filesystem that has your backup repository
|
|||
repositories. See also :ref:`cache-memory-usage`.
|
||||
|
||||
Borg doesn't use space reserved for root on repository disks (even when run as root),
|
||||
on file systems which do not support this mechanism (e.g. XFS) we recommend to
|
||||
reserve some space in Borg itself just to be safe by adjusting the
|
||||
``additional_free_space`` setting in the ``[repository]`` section of a repositories
|
||||
``config`` file. A good starting point is ``2G``.
|
||||
on file systems which do not support this mechanism (e.g. XFS) we recommend to reserve
|
||||
some space in Borg itself just to be safe by adjusting the ``additional_free_space``
|
||||
setting (a good starting point is ``2G``)::
|
||||
|
||||
borg config /path/to/repo additional_free_space 2G
|
||||
|
||||
If |project_name| runs out of disk space, it tries to free as much space as it
|
||||
can while aborting the current operation safely, which allows to free more space
|
||||
|
|
|
@ -51,6 +51,7 @@ Usage
|
|||
usage/serve
|
||||
usage/lock
|
||||
usage/benchmark
|
||||
usage/config
|
||||
|
||||
usage/help
|
||||
usage/debug
|
||||
|
|
22
docs/usage/config.rst
Normal file
22
docs/usage/config.rst
Normal file
|
@ -0,0 +1,22 @@
|
|||
.. include:: config.rst.inc
|
||||
|
||||
.. note::
|
||||
|
||||
The repository & cache config files are some of the only directly manipulable
|
||||
parts of a repository that aren't versioned or backed up, so be careful when
|
||||
making changes\!
|
||||
|
||||
Examples
|
||||
~~~~~~~~
|
||||
::
|
||||
|
||||
# find cache directory
|
||||
$ cd ~/.cache/borg/$(borg config /path/to/repo id)
|
||||
|
||||
# reserve some space
|
||||
$ borg config /path/to/repo additional_free_space 2G
|
||||
|
||||
# make a repo append-only
|
||||
$ borg config /path/to/repo append_only 1
|
||||
|
||||
|
|
@ -149,8 +149,9 @@ reject to delete the repository completely). This is useful for scenarios where
|
|||
backup client machine backups remotely to a backup server using ``borg serve``, since
|
||||
a hacked client machine cannot delete backups on the server permanently.
|
||||
|
||||
To activate append-only mode, edit the repository ``config`` file and add a line
|
||||
``append_only=1`` to the ``[repository]`` section (or edit the line if it exists).
|
||||
To activate append-only mode, set ``append_only`` to 1 in the repository config::
|
||||
|
||||
borg config /path/to/repo append_only 1
|
||||
|
||||
In append-only mode Borg will create a transaction log in the ``transactions`` file,
|
||||
where each line is a transaction and a UTC timestamp.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import argparse
|
||||
import collections
|
||||
import configparser
|
||||
import faulthandler
|
||||
import functools
|
||||
import hashlib
|
||||
|
@ -1709,6 +1710,41 @@ def do_with_lock(self, args, repository):
|
|||
# see issue #1867.
|
||||
repository.commit()
|
||||
|
||||
@with_repository(exclusive=True, cache=True, compatibility=(Manifest.Operation.WRITE,))
|
||||
def do_config(self, args, repository, manifest, key, cache):
|
||||
"""get, set, and delete values in a repository or cache config file"""
|
||||
try:
|
||||
section, name = args.name.split('.')
|
||||
except ValueError:
|
||||
section = args.cache and "cache" or "repository"
|
||||
name = args.name
|
||||
|
||||
if args.cache:
|
||||
cache.cache_config.load()
|
||||
config = cache.cache_config._config
|
||||
save = cache.cache_config.save
|
||||
else:
|
||||
config = repository.config
|
||||
save = lambda: repository.save_config(repository.path, repository.config)
|
||||
|
||||
if args.delete:
|
||||
config.remove_option(section, name)
|
||||
if len(config.options(section)) == 0:
|
||||
config.remove_section(section)
|
||||
save()
|
||||
elif args.value:
|
||||
if section not in config.sections():
|
||||
config.add_section(section)
|
||||
config.set(section, name, args.value)
|
||||
save()
|
||||
else:
|
||||
try:
|
||||
print(config.get(section, name))
|
||||
except (configparser.NoOptionError, configparser.NoSectionError) as e:
|
||||
print(e, file=sys.stderr)
|
||||
return EXIT_WARNING
|
||||
return EXIT_SUCCESS
|
||||
|
||||
def do_debug_info(self, args):
|
||||
"""display system information for debugging / bug reports"""
|
||||
print(sysinfo())
|
||||
|
@ -3681,6 +3717,37 @@ def define_archive_filters_group(subparser, *, sort_by=True, first_last=True):
|
|||
subparser.add_argument('args', metavar='ARGS', nargs=argparse.REMAINDER,
|
||||
help='command arguments')
|
||||
|
||||
config_epilog = process_epilog("""
|
||||
This command gets and sets options in a local repository or cache config file.
|
||||
For security reasons, this command only works on local repositories.
|
||||
|
||||
To delete a config value entirely, use ``--delete``. To get an existing key, pass
|
||||
only the key name. To set a key, pass both the key name and the new value. Keys
|
||||
can be specified in the format "section.name" or simply "name"; the section will
|
||||
default to "repository" and "cache" for the repo and cache configs, respectively.
|
||||
|
||||
By default, borg config manipulates the repository config file. Using ``--cache``
|
||||
edits the repository cache's config file instead.
|
||||
""")
|
||||
subparser = subparsers.add_parser('config', parents=[common_parser], add_help=False,
|
||||
description=self.do_config.__doc__,
|
||||
epilog=config_epilog,
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
help='get and set repository config options')
|
||||
subparser.set_defaults(func=self.do_config)
|
||||
subparser.add_argument('-c', '--cache', dest='cache', action='store_true',
|
||||
help='get and set values from the repo cache')
|
||||
subparser.add_argument('-d', '--delete', dest='delete', action='store_true',
|
||||
help='delete the key from the config file')
|
||||
|
||||
subparser.add_argument('location', metavar='REPOSITORY',
|
||||
type=location_validator(archive=False, proto='file'),
|
||||
help='repository to configure')
|
||||
subparser.add_argument('name', metavar='NAME',
|
||||
help='name of config key')
|
||||
subparser.add_argument('value', metavar='VALUE', nargs='?',
|
||||
help='new value for key')
|
||||
|
||||
subparser = subparsers.add_parser('help', parents=[common_parser], add_help=False,
|
||||
description='Extra help')
|
||||
subparser.add_argument('--epilog-only', dest='epilog_only', action='store_true')
|
||||
|
|
|
@ -1139,7 +1139,7 @@ def canonical_path(self):
|
|||
path)
|
||||
|
||||
|
||||
def location_validator(archive=None):
|
||||
def location_validator(archive=None, proto=None):
|
||||
def validator(text):
|
||||
try:
|
||||
loc = Location(text)
|
||||
|
@ -1148,7 +1148,12 @@ def validator(text):
|
|||
if archive is True and not loc.archive:
|
||||
raise argparse.ArgumentTypeError('"%s": No archive specified' % text)
|
||||
elif archive is False and loc.archive:
|
||||
raise argparse.ArgumentTypeError('"%s" No archive can be specified' % text)
|
||||
raise argparse.ArgumentTypeError('"%s": No archive can be specified' % text)
|
||||
if proto is not None and loc.proto != proto:
|
||||
if proto == 'file':
|
||||
raise argparse.ArgumentTypeError('"%s": Repository must be local' % text)
|
||||
else:
|
||||
raise argparse.ArgumentTypeError('"%s": Repository must be remote' % text)
|
||||
return loc
|
||||
return validator
|
||||
|
||||
|
|
|
@ -2778,6 +2778,19 @@ def test_benchmark_crud(self):
|
|||
with environment_variable(_BORG_BENCHMARK_CRUD_TEST='YES'):
|
||||
self.cmd('benchmark', 'crud', self.repository_location, self.input_path)
|
||||
|
||||
def test_config(self):
|
||||
self.create_test_files()
|
||||
os.unlink('input/flagfile')
|
||||
self.cmd('init', '--encryption=repokey', self.repository_location)
|
||||
for flags in [[], ['--cache']]:
|
||||
for key in {'testkey', 'testsection.testkey'}:
|
||||
self.cmd('config', self.repository_location, *flags, key, exit_code=1)
|
||||
self.cmd('config', self.repository_location, *flags, key, 'testcontents')
|
||||
output = self.cmd('config', self.repository_location, *flags, key)
|
||||
assert output == 'testcontents\n'
|
||||
self.cmd('config', self.repository_location, *flags, '--delete', key)
|
||||
self.cmd('config', self.repository_location, *flags, key, exit_code=1)
|
||||
|
||||
requires_gnutar = pytest.mark.skipif(not have_gnutar(), reason='GNU tar must be installed for this test.')
|
||||
requires_gzip = pytest.mark.skipif(not shutil.which('gzip'), reason='gzip must be installed for this test.')
|
||||
|
||||
|
@ -3260,6 +3273,10 @@ def test_remote_repo_restrict_to_repository(self):
|
|||
def test_debug_put_get_delete_obj(self):
|
||||
pass
|
||||
|
||||
@unittest.skip('only works locally')
|
||||
def test_config(self):
|
||||
pass
|
||||
|
||||
def test_strip_components_doesnt_leak(self):
|
||||
self.cmd('init', '--encryption=repokey', self.repository_location)
|
||||
self.create_regular_file('dir/file', contents=b"test file contents 1")
|
||||
|
|
Loading…
Reference in a new issue