Support for Borg 2's rcreate and rinfo sub-commands (#557).

This commit is contained in:
Dan Helfman 2022-08-12 14:53:20 -07:00
parent 22149c6401
commit 622caa0c21
18 changed files with 434 additions and 239 deletions

3
NEWS
View file

@ -1,4 +1,5 @@
1.6.7.dev0 2.0.0.dev0
* #557: Support for Borg 2 while still working with Borg 1.
* #565: Fix handling of "repository" and "data" consistency checks to prevent invalid Borg flags. * #565: Fix handling of "repository" and "data" consistency checks to prevent invalid Borg flags.
* #566: Modify "mount" and "extract" actions to require the "--repository" flag when multiple * #566: Modify "mount" and "extract" actions to require the "--repository" flag when multiple
repositories are configured. repositories are configured.

View file

@ -5,7 +5,7 @@ import logging
import os import os
import pathlib import pathlib
from borgmatic.borg import environment, extract, info, state from borgmatic.borg import environment, extract, rinfo, state
from borgmatic.execute import DO_NOT_CAPTURE, execute_command from borgmatic.execute import DO_NOT_CAPTURE, execute_command
DEFAULT_CHECKS = ( DEFAULT_CHECKS = (
@ -241,6 +241,7 @@ def check_archives(
location_config, location_config,
storage_config, storage_config,
consistency_config, consistency_config,
local_borg_version,
local_path='borg', local_path='borg',
remote_path=None, remote_path=None,
progress=None, progress=None,
@ -260,10 +261,11 @@ def check_archives(
''' '''
try: try:
borg_repository_id = json.loads( borg_repository_id = json.loads(
info.display_archives_info( rinfo.display_repository_info(
repository, repository,
storage_config, storage_config,
argparse.Namespace(json=True, archive=None), local_borg_version,
argparse.Namespace(json=True),
local_path, local_path,
remote_path, remote_path,
) )

View file

@ -9,6 +9,10 @@ class Feature(Enum):
NOFLAGS = 3 NOFLAGS = 3
NUMERIC_IDS = 4 NUMERIC_IDS = 4
UPLOAD_RATELIMIT = 5 UPLOAD_RATELIMIT = 5
SEPARATE_REPOSITORY_ARCHIVE = 6
RCREATE = 7
RLIST = 8
RINFO = 9
FEATURE_TO_MINIMUM_BORG_VERSION = { FEATURE_TO_MINIMUM_BORG_VERSION = {
@ -17,6 +21,10 @@ FEATURE_TO_MINIMUM_BORG_VERSION = {
Feature.NOFLAGS: parse_version('1.2.0a8'), # borg create --noflags Feature.NOFLAGS: parse_version('1.2.0a8'), # borg create --noflags
Feature.NUMERIC_IDS: parse_version('1.2.0b3'), # borg create/extract/mount --numeric-ids Feature.NUMERIC_IDS: parse_version('1.2.0b3'), # borg create/extract/mount --numeric-ids
Feature.UPLOAD_RATELIMIT: parse_version('1.2.0b3'), # borg create --upload-ratelimit Feature.UPLOAD_RATELIMIT: parse_version('1.2.0b3'), # borg create --upload-ratelimit
Feature.SEPARATE_REPOSITORY_ARCHIVE: parse_version('2.0.0a2'), # --repo with separate archive
Feature.RCREATE: parse_version('2.0.0a2'), # borg rcreate
Feature.RLIST: parse_version('2.0.0a2'), # borg rlist
Feature.RINFO: parse_version('2.0.0a2'), # borg rinfo
} }

View file

@ -1,6 +1,6 @@
import logging import logging
from borgmatic.borg import environment from borgmatic.borg import environment, feature
from borgmatic.borg.flags import make_flags, make_flags_from_arguments from borgmatic.borg.flags import make_flags, make_flags_from_arguments
from borgmatic.execute import execute_command from borgmatic.execute import execute_command
@ -8,12 +8,17 @@ logger = logging.getLogger(__name__)
def display_archives_info( def display_archives_info(
repository, storage_config, info_arguments, local_path='borg', remote_path=None repository,
storage_config,
local_borg_version,
info_arguments,
local_path='borg',
remote_path=None,
): ):
''' '''
Given a local or remote repository path, a storage config dict, and the arguments to the info Given a local or remote repository path, a storage config dict, the local Borg version, and the
action, display summary information for Borg archives in the repository or return JSON summary arguments to the info action, display summary information for Borg archives in the repository or
information. return JSON summary information.
''' '''
lock_wait = storage_config.get('lock_wait', None) lock_wait = storage_config.get('lock_wait', None)
@ -33,11 +38,18 @@ def display_archives_info(
+ make_flags('lock-wait', lock_wait) + make_flags('lock-wait', lock_wait)
+ make_flags_from_arguments(info_arguments, excludes=('repository', 'archive')) + make_flags_from_arguments(info_arguments, excludes=('repository', 'archive'))
+ ( + (
(
('--repo', repository)
+ (('--glob-archives', info_arguments.archive) if info_arguments.archive else ())
)
if feature.available(feature.Feature.SEPARATE_REPOSITORY_ARCHIVE, local_borg_version)
else (
'::'.join((repository, info_arguments.archive)) '::'.join((repository, info_arguments.archive))
if info_arguments.archive if info_arguments.archive
else repository, else repository,
) )
) )
)
return execute_command( return execute_command(
full_command, full_command,

View file

@ -2,18 +2,19 @@ import argparse
import logging import logging
import subprocess import subprocess
from borgmatic.borg import environment, info from borgmatic.borg import environment, feature, rinfo
from borgmatic.execute import DO_NOT_CAPTURE, execute_command from borgmatic.execute import DO_NOT_CAPTURE, execute_command
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
INFO_REPOSITORY_NOT_FOUND_EXIT_CODE = 2 RINFO_REPOSITORY_NOT_FOUND_EXIT_CODE = 2
def initialize_repository( def create_repository(
repository, repository,
storage_config, storage_config,
local_borg_version,
encryption_mode, encryption_mode,
append_only=None, append_only=None,
storage_quota=None, storage_quota=None,
@ -21,28 +22,34 @@ def initialize_repository(
remote_path=None, remote_path=None,
): ):
''' '''
Given a local or remote repository path, a storage configuration dict, a Borg encryption mode, Given a local or remote repository path, a storage configuration dict, the local Borg version, a
whether the repository should be append-only, and the storage quota to use, initialize the Borg encryption mode, whether the repository should be append-only, and the storage quota to
repository. If the repository already exists, then log and skip initialization. use, create the repository. If the repository already exists, then log and skip creation.
''' '''
try: try:
info.display_archives_info( rinfo.display_repository_info(
repository, repository,
storage_config, storage_config,
argparse.Namespace(json=True, archive=None), local_borg_version,
argparse.Namespace(json=True),
local_path, local_path,
remote_path, remote_path,
) )
logger.info('Repository already exists. Skipping initialization.') logger.info('Repository already exists. Skipping creation.')
return return
except subprocess.CalledProcessError as error: except subprocess.CalledProcessError as error:
if error.returncode != INFO_REPOSITORY_NOT_FOUND_EXIT_CODE: if error.returncode != RINFO_REPOSITORY_NOT_FOUND_EXIT_CODE:
raise raise
extra_borg_options = storage_config.get('extra_borg_options', {}).get('init', '') extra_borg_options = storage_config.get('extra_borg_options', {}).get('rcreate', '')
init_command = ( rcreate_command = (
(local_path, 'init') (local_path,)
+ (
('rcreate',)
if feature.available(feature.Feature.RCREATE, local_borg_version)
else ('init',)
)
+ (('--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 ())
+ (('--storage-quota', storage_quota) if storage_quota else ()) + (('--storage-quota', storage_quota) if storage_quota else ())
@ -50,12 +57,17 @@ def initialize_repository(
+ (('--debug',) if logger.isEnabledFor(logging.DEBUG) else ()) + (('--debug',) if logger.isEnabledFor(logging.DEBUG) else ())
+ (('--remote-path', remote_path) if remote_path else ()) + (('--remote-path', remote_path) if remote_path else ())
+ (tuple(extra_borg_options.split(' ')) if extra_borg_options else ()) + (tuple(extra_borg_options.split(' ')) if extra_borg_options else ())
+ (
('--repo',)
if feature.available(feature.Feature.SEPARATE_REPOSITORY_ARCHIVE, local_borg_version)
else ()
)
+ (repository,) + (repository,)
) )
# Do not capture output here, so as to support interactive prompts. # Do not capture output here, so as to support interactive prompts.
execute_command( execute_command(
init_command, rcreate_command,
output_file=DO_NOT_CAPTURE, output_file=DO_NOT_CAPTURE,
borg_local_path=local_path, borg_local_path=local_path,
extra_environment=environment.make_environment(storage_config), extra_environment=environment.make_environment(storage_config),

View file

@ -4,7 +4,7 @@ from argparse import Action, ArgumentParser
from borgmatic.config import collect from borgmatic.config import collect
SUBPARSER_ALIASES = { SUBPARSER_ALIASES = {
'init': ['--init', '-I'], 'rcreate': ['init', '--init', '-I'],
'prune': ['--prune', '-p'], 'prune': ['--prune', '-p'],
'compact': [], 'compact': [],
'create': ['--create', '-C'], 'create': ['--create', '-C'],
@ -222,33 +222,35 @@ def make_parsers():
metavar='', metavar='',
help='Specify zero or more actions. Defaults to prune, compact, create, and check. Use --help with action for details:', help='Specify zero or more actions. Defaults to prune, compact, create, and check. Use --help with action for details:',
) )
init_parser = subparsers.add_parser( rcreate_parser = subparsers.add_parser(
'init', 'rcreate',
aliases=SUBPARSER_ALIASES['init'], aliases=SUBPARSER_ALIASES['rcreate'],
help='Initialize an empty Borg repository', help='Create a new, empty Borg repository',
description='Initialize an empty Borg repository', description='Create a new, empty Borg repository',
add_help=False, add_help=False,
) )
init_group = init_parser.add_argument_group('init arguments') rcreate_group = rcreate_parser.add_argument_group('rcreate arguments')
init_group.add_argument( rcreate_group.add_argument(
'-e', '-e',
'--encryption', '--encryption',
dest='encryption_mode', dest='encryption_mode',
help='Borg repository encryption mode', help='Borg repository encryption mode',
required=True, required=True,
) )
init_group.add_argument( rcreate_group.add_argument(
'--append-only', '--append-only',
dest='append_only', dest='append_only',
action='store_true', action='store_true',
help='Create an append-only repository', help='Create an append-only repository',
) )
init_group.add_argument( rcreate_group.add_argument(
'--storage-quota', '--storage-quota',
dest='storage_quota', dest='storage_quota',
help='Create a repository with a fixed storage quota', help='Create a repository with a fixed storage quota',
) )
init_group.add_argument('-h', '--help', action='help', help='Show this help message and exit') rcreate_group.add_argument(
'-h', '--help', action='help', help='Show this help message and exit'
)
prune_parser = subparsers.add_parser( prune_parser = subparsers.add_parser(
'prune', 'prune',
@ -688,11 +690,11 @@ def parse_arguments(*unparsed_arguments):
if arguments['global'].excludes_filename: if arguments['global'].excludes_filename:
raise ValueError( raise ValueError(
'The --excludes option has been replaced with exclude_patterns in configuration' 'The --excludes flag has been replaced with exclude_patterns in configuration'
) )
if 'init' in arguments and arguments['global'].dry_run: if 'rcreate' in arguments and arguments['global'].dry_run:
raise ValueError('The init action cannot be used with the --dry-run option') raise ValueError('The rcreate/init action cannot be used with the --dry-run flag')
if ( if (
'list' in arguments 'list' in arguments
@ -700,6 +702,11 @@ def parse_arguments(*unparsed_arguments):
and arguments['list'].json and arguments['list'].json
and arguments['info'].json and arguments['info'].json
): ):
raise ValueError('With the --json option, list and info actions cannot be used together') raise ValueError('With the --json flag, list and info actions cannot be used together')
if 'info' in arguments and arguments['info'].archive and arguments['info'].glob_archives:
raise ValueError(
'With the info action, the --archive and --glob-archives flags cannot be used together'
)
return arguments return arguments

View file

@ -20,10 +20,10 @@ from borgmatic.borg import export_tar as borg_export_tar
from borgmatic.borg import extract as borg_extract from borgmatic.borg import extract as borg_extract
from borgmatic.borg import feature as borg_feature from borgmatic.borg import feature as borg_feature
from borgmatic.borg import info as borg_info from borgmatic.borg import info as borg_info
from borgmatic.borg import init as borg_init
from borgmatic.borg import list as borg_list from borgmatic.borg import list as borg_list
from borgmatic.borg import mount as borg_mount from borgmatic.borg import mount as borg_mount
from borgmatic.borg import prune as borg_prune from borgmatic.borg import prune as borg_prune
from borgmatic.borg import rcreate as borg_rcreate
from borgmatic.borg import umount as borg_umount from borgmatic.borg import umount as borg_umount
from borgmatic.borg import version as borg_version from borgmatic.borg import version as borg_version
from borgmatic.commands.arguments import parse_arguments from borgmatic.commands.arguments import parse_arguments
@ -249,14 +249,15 @@ def run_actions(
'repositories': ','.join(location['repositories']), 'repositories': ','.join(location['repositories']),
} }
if 'init' in arguments: if 'rcreate' in arguments:
logger.info('{}: Initializing repository'.format(repository)) logger.info('{}: Creating repository'.format(repository))
borg_init.initialize_repository( borg_rcreate.create_repository(
repository, repository,
storage, storage,
arguments['init'].encryption_mode, local_borg_version,
arguments['init'].append_only, arguments['rcreate'].encryption_mode,
arguments['init'].storage_quota, arguments['rcreate'].append_only,
arguments['rcreate'].storage_quota,
local_path=local_path, local_path=local_path,
remote_path=remote_path, remote_path=remote_path,
) )
@ -396,6 +397,7 @@ def run_actions(
location, location,
storage, storage,
consistency, consistency,
local_borg_version,
local_path=local_path, local_path=local_path,
remote_path=remote_path, remote_path=remote_path,
progress=arguments['check'].progress, progress=arguments['check'].progress,
@ -624,6 +626,7 @@ def run_actions(
json_output = borg_info.display_archives_info( json_output = borg_info.display_archives_info(
repository, repository,
storage, storage,
local_borg_version,
info_arguments=info_arguments, info_arguments=info_arguments,
local_path=local_path, local_path=local_path,
remote_path=remote_path, remote_path=remote_path,

View file

@ -27,9 +27,6 @@ borgmatic create
borgmatic check borgmatic check
``` ```
(No borgmatic `prune`, `create`, or `check` actions? Try the old-style
`--prune`, `--create`, or `--check`. Or upgrade borgmatic!)
You can run with only one of these actions provided, or you can mix and match You can run with only one of these actions provided, or you can mix and match
any number of them in a single borgmatic run. This supports approaches like any number of them in a single borgmatic run. This supports approaches like
skipping certain actions while running others. For instance, this skips skipping certain actions while running others. For instance, this skips
@ -70,7 +67,9 @@ Here are the available checks from fastest to slowest:
* `extract`: Performs an extraction dry-run of the most recent archive. * `extract`: Performs an extraction dry-run of the most recent archive.
* `data`: Verifies the data integrity of all archives contents, decrypting and decompressing all data (implies `archives` as well). * `data`: Verifies the data integrity of all archives contents, decrypting and decompressing all data (implies `archives` as well).
See [Borg's check documentation](https://borgbackup.readthedocs.io/en/stable/usage/check.html) for more information. See [Borg's check
documentation](https://borgbackup.readthedocs.io/en/stable/usage/check.html)
for more information.
### Check frequency ### Check frequency

View file

@ -37,19 +37,22 @@ borgmatic --stats
## Existing backups ## Existing backups
borgmatic provides convenient actions for Borg's borgmatic provides convenient actions for Borg's
[list](https://borgbackup.readthedocs.io/en/stable/usage/list.html) and [`list`](https://borgbackup.readthedocs.io/en/stable/usage/list.html) and
[info](https://borgbackup.readthedocs.io/en/stable/usage/info.html) [`info`](https://borgbackup.readthedocs.io/en/stable/usage/info.html)
functionality: functionality:
```bash ```bash
borgmatic list borgmatic list
borgmatic info borgmatic info
``` ```
(No borgmatic `list` or `info` actions? Try the old-style `--list` or <span class="minilink minilink-addedin">New in borgmatic version 2.0.0</span>
`--info`. Or upgrade borgmatic!) There's also an `rinfo` action for displaying repository information with Borg
2.x:
```bash
borgmatic rinfo
```
### Searching for a file ### Searching for a file

View file

@ -186,32 +186,36 @@ files via configuration management, or you want to double check that your hand
edits are valid. edits are valid.
## Initialization ## Repository creation
Before you can create backups with borgmatic, you first need to initialize a Before you can create backups with borgmatic, you first need to create a Borg
Borg repository so you have a destination for your backup archives. (But skip repository so you have a destination for your backup archives. (But skip this
this step if you already have a Borg repository.) To create a repository, run step if you already have a Borg repository.) To create a repository, run a
a command like the following: command like the following with Borg 1.x:
```bash ```bash
sudo borgmatic init --encryption repokey sudo borgmatic init --encryption repokey
``` ```
(No borgmatic `init` action? Try the old-style `--init` flag, or upgrade <span class="minilink minilink-addedin">New in borgmatic version 2.0.0</span>
borgmatic!) Or, with Borg 2.x:
```bash
sudo borgmatic rcreate --encryption repokey-aes-ocb
```
This uses the borgmatic configuration file you created above to determine This uses the borgmatic configuration file you created above to determine
which local or remote repository to create, and encrypts it with the which local or remote repository to create, and encrypts it with the
encryption passphrase specified there if one is provided. Read about [Borg encryption passphrase specified there if one is provided. Read about [Borg
encryption encryption
modes](https://borgbackup.readthedocs.io/en/stable/usage/init.html#encryption-modes) modes](https://borgbackup.readthedocs.io/en/stable/usage/init.html#encryption-mode-tldr)
for the menu of available encryption modes. for the menu of available encryption modes.
Also, optionally check out the [Borg Quick Also, optionally check out the [Borg Quick
Start](https://borgbackup.readthedocs.org/en/stable/quickstart.html) for more Start](https://borgbackup.readthedocs.org/en/stable/quickstart.html) for more
background about repository initialization. background about repository creation.
Note that borgmatic skips repository initialization if the repository already Note that borgmatic skips repository creation if the repository already
exists. This supports use cases like ensuring a repository exists prior to exists. This supports use cases like ensuring a repository exists prior to
performing a backup. performing a backup.
@ -221,8 +225,8 @@ key-based SSH access to the desired user account on the remote host.
## Backups ## Backups
Now that you've configured borgmatic and initialized a repository, it's a Now that you've configured borgmatic and created a repository, it's a good
good idea to test that borgmatic is working. So to run borgmatic and start a idea to test that borgmatic is working. So to run borgmatic and start a
backup, you can invoke it like this: backup, you can invoke it like this:
```bash ```bash
@ -230,7 +234,7 @@ sudo borgmatic create --verbosity 1 --files --stats
``` ```
(No borgmatic `--files` flag? It's only present in newer versions of (No borgmatic `--files` flag? It's only present in newer versions of
borgmatic. So try leaving it out, or upgrade borgmatic!) borgmatic. So try leaving it out or upgrade borgmatic!)
The `--verbosity` flag makes borgmatic show the steps it's performing. The The `--verbosity` flag makes borgmatic show the steps it's performing. The
`--files` flag lists each file that's new or changed since the last backup. `--files` flag lists each file that's new or changed since the last backup.

View file

@ -14,8 +14,8 @@ apk add --no-cache python3 py3-pip borgbackup postgresql-client mariadb-client m
py3-ruamel.yaml py3-ruamel.yaml.clib bash py3-ruamel.yaml py3-ruamel.yaml.clib bash
# If certain dependencies of black are available in this version of Alpine, install them. # If certain dependencies of black are available in this version of Alpine, install them.
apk add --no-cache py3-typed-ast py3-regex || true apk add --no-cache py3-typed-ast py3-regex || true
python3 -m pip install --no-cache --upgrade pip==22.0.3 setuptools==60.8.1 python3 -m pip install --no-cache --upgrade pip==22.2.2 setuptools==64.0.1
pip3 install tox==3.24.5 pip3 install --ignore-installed tox==3.25.1
export COVERAGE_FILE=/tmp/.coverage export COVERAGE_FILE=/tmp/.coverage
tox --workdir /tmp/.tox --sitepackages tox --workdir /tmp/.tox --sitepackages
tox --workdir /tmp/.tox --sitepackages -e end-to-end tox --workdir /tmp/.tox --sitepackages -e end-to-end

View file

@ -1,6 +1,6 @@
from setuptools import find_packages, setup from setuptools import find_packages, setup
VERSION = '1.6.7.dev0' VERSION = '2.0.0.dev0'
setup( setup(

View file

@ -496,6 +496,13 @@ def test_parse_arguments_disallows_json_with_both_list_and_info():
module.parse_arguments('list', 'info', '--json') module.parse_arguments('list', 'info', '--json')
def test_parse_arguments_disallows_info_with_both_archive_and_glob_archives():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])
with pytest.raises(ValueError):
module.parse_arguments('info', '--archive', 'foo', '--glob-archives', '*bar')
def test_parse_arguments_check_only_extract_does_not_raise_extract_subparser_error(): def test_parse_arguments_check_only_extract_does_not_raise_extract_subparser_error():
flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default']) flexmock(module.collect).should_receive('get_default_config_paths').and_return(['default'])

View file

@ -296,7 +296,7 @@ def test_check_archives_with_progress_calls_borg_with_progress_parameter():
consistency_config = {'check_last': None} consistency_config = {'check_last': None}
flexmock(module).should_receive('parse_checks') flexmock(module).should_receive('parse_checks')
flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks) flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks)
flexmock(module.info).should_receive('display_archives_info').and_return( flexmock(module.rinfo).should_receive('display_repository_info').and_return(
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
flexmock(module).should_receive('make_check_flags').and_return(()) flexmock(module).should_receive('make_check_flags').and_return(())
@ -315,6 +315,7 @@ def test_check_archives_with_progress_calls_borg_with_progress_parameter():
location_config={}, location_config={},
storage_config={}, storage_config={},
consistency_config=consistency_config, consistency_config=consistency_config,
local_borg_version='1.2.3',
progress=True, progress=True,
) )
@ -324,7 +325,7 @@ def test_check_archives_with_repair_calls_borg_with_repair_parameter():
consistency_config = {'check_last': None} consistency_config = {'check_last': None}
flexmock(module).should_receive('parse_checks') flexmock(module).should_receive('parse_checks')
flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks) flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks)
flexmock(module.info).should_receive('display_archives_info').and_return( flexmock(module.rinfo).should_receive('display_repository_info').and_return(
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
flexmock(module).should_receive('make_check_flags').and_return(()) flexmock(module).should_receive('make_check_flags').and_return(())
@ -343,6 +344,7 @@ def test_check_archives_with_repair_calls_borg_with_repair_parameter():
location_config={}, location_config={},
storage_config={}, storage_config={},
consistency_config=consistency_config, consistency_config=consistency_config,
local_borg_version='1.2.3',
repair=True, repair=True,
) )
@ -361,7 +363,7 @@ def test_check_archives_calls_borg_with_parameters(checks):
consistency_config = {'check_last': check_last} consistency_config = {'check_last': check_last}
flexmock(module).should_receive('parse_checks') flexmock(module).should_receive('parse_checks')
flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks) flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks)
flexmock(module.info).should_receive('display_archives_info').and_return( flexmock(module.rinfo).should_receive('display_repository_info').and_return(
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
flexmock(module).should_receive('make_check_flags').with_args( flexmock(module).should_receive('make_check_flags').with_args(
@ -376,6 +378,7 @@ def test_check_archives_calls_borg_with_parameters(checks):
location_config={}, location_config={},
storage_config={}, storage_config={},
consistency_config=consistency_config, consistency_config=consistency_config,
local_borg_version='1.2.3',
) )
@ -385,7 +388,7 @@ def test_check_archives_with_json_error_raises():
consistency_config = {'check_last': check_last} consistency_config = {'check_last': check_last}
flexmock(module).should_receive('parse_checks') flexmock(module).should_receive('parse_checks')
flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks) flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks)
flexmock(module.info).should_receive('display_archives_info').and_return( flexmock(module.rinfo).should_receive('display_repository_info').and_return(
'{"unexpected": {"id": "repo"}}' '{"unexpected": {"id": "repo"}}'
) )
@ -395,6 +398,7 @@ def test_check_archives_with_json_error_raises():
location_config={}, location_config={},
storage_config={}, storage_config={},
consistency_config=consistency_config, consistency_config=consistency_config,
local_borg_version='1.2.3',
) )
@ -404,7 +408,7 @@ def test_check_archives_with_missing_json_keys_raises():
consistency_config = {'check_last': check_last} consistency_config = {'check_last': check_last}
flexmock(module).should_receive('parse_checks') flexmock(module).should_receive('parse_checks')
flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks) flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks)
flexmock(module.info).should_receive('display_archives_info').and_return('{invalid JSON') flexmock(module.rinfo).should_receive('display_repository_info').and_return('{invalid JSON')
with pytest.raises(ValueError): with pytest.raises(ValueError):
module.check_archives( module.check_archives(
@ -412,6 +416,7 @@ def test_check_archives_with_missing_json_keys_raises():
location_config={}, location_config={},
storage_config={}, storage_config={},
consistency_config=consistency_config, consistency_config=consistency_config,
local_borg_version='1.2.3',
) )
@ -421,7 +426,7 @@ def test_check_archives_with_extract_check_calls_extract_only():
consistency_config = {'check_last': check_last} consistency_config = {'check_last': check_last}
flexmock(module).should_receive('parse_checks') flexmock(module).should_receive('parse_checks')
flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks) flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks)
flexmock(module.info).should_receive('display_archives_info').and_return( flexmock(module.rinfo).should_receive('display_repository_info').and_return(
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
flexmock(module).should_receive('make_check_flags').never() flexmock(module).should_receive('make_check_flags').never()
@ -434,6 +439,7 @@ def test_check_archives_with_extract_check_calls_extract_only():
location_config={}, location_config={},
storage_config={}, storage_config={},
consistency_config=consistency_config, consistency_config=consistency_config,
local_borg_version='1.2.3',
) )
@ -442,7 +448,7 @@ def test_check_archives_with_log_info_calls_borg_with_info_parameter():
consistency_config = {'check_last': None} consistency_config = {'check_last': None}
flexmock(module).should_receive('parse_checks') flexmock(module).should_receive('parse_checks')
flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks) flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks)
flexmock(module.info).should_receive('display_archives_info').and_return( flexmock(module.rinfo).should_receive('display_repository_info').and_return(
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
flexmock(module).should_receive('make_check_flags').and_return(()) flexmock(module).should_receive('make_check_flags').and_return(())
@ -456,6 +462,7 @@ def test_check_archives_with_log_info_calls_borg_with_info_parameter():
location_config={}, location_config={},
storage_config={}, storage_config={},
consistency_config=consistency_config, consistency_config=consistency_config,
local_borg_version='1.2.3',
) )
@ -464,7 +471,7 @@ def test_check_archives_with_log_debug_calls_borg_with_debug_parameter():
consistency_config = {'check_last': None} consistency_config = {'check_last': None}
flexmock(module).should_receive('parse_checks') flexmock(module).should_receive('parse_checks')
flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks) flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks)
flexmock(module.info).should_receive('display_archives_info').and_return( flexmock(module.rinfo).should_receive('display_repository_info').and_return(
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
flexmock(module).should_receive('make_check_flags').and_return(()) flexmock(module).should_receive('make_check_flags').and_return(())
@ -478,6 +485,7 @@ def test_check_archives_with_log_debug_calls_borg_with_debug_parameter():
location_config={}, location_config={},
storage_config={}, storage_config={},
consistency_config=consistency_config, consistency_config=consistency_config,
local_borg_version='1.2.3',
) )
@ -485,7 +493,7 @@ def test_check_archives_without_any_checks_bails():
consistency_config = {'check_last': None} consistency_config = {'check_last': None}
flexmock(module).should_receive('parse_checks') flexmock(module).should_receive('parse_checks')
flexmock(module).should_receive('filter_checks_on_frequency').and_return(()) flexmock(module).should_receive('filter_checks_on_frequency').and_return(())
flexmock(module.info).should_receive('display_archives_info').and_return( flexmock(module.rinfo).should_receive('display_repository_info').and_return(
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
insert_execute_command_never() insert_execute_command_never()
@ -495,6 +503,7 @@ def test_check_archives_without_any_checks_bails():
location_config={}, location_config={},
storage_config={}, storage_config={},
consistency_config=consistency_config, consistency_config=consistency_config,
local_borg_version='1.2.3',
) )
@ -504,7 +513,7 @@ def test_check_archives_with_local_path_calls_borg_via_local_path():
consistency_config = {'check_last': check_last} consistency_config = {'check_last': check_last}
flexmock(module).should_receive('parse_checks') flexmock(module).should_receive('parse_checks')
flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks) flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks)
flexmock(module.info).should_receive('display_archives_info').and_return( flexmock(module.rinfo).should_receive('display_repository_info').and_return(
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
flexmock(module).should_receive('make_check_flags').with_args( flexmock(module).should_receive('make_check_flags').with_args(
@ -519,6 +528,7 @@ def test_check_archives_with_local_path_calls_borg_via_local_path():
location_config={}, location_config={},
storage_config={}, storage_config={},
consistency_config=consistency_config, consistency_config=consistency_config,
local_borg_version='1.2.3',
local_path='borg1', local_path='borg1',
) )
@ -529,7 +539,7 @@ def test_check_archives_with_remote_path_calls_borg_with_remote_path_parameters(
consistency_config = {'check_last': check_last} consistency_config = {'check_last': check_last}
flexmock(module).should_receive('parse_checks') flexmock(module).should_receive('parse_checks')
flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks) flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks)
flexmock(module.info).should_receive('display_archives_info').and_return( flexmock(module.rinfo).should_receive('display_repository_info').and_return(
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
flexmock(module).should_receive('make_check_flags').with_args( flexmock(module).should_receive('make_check_flags').with_args(
@ -544,6 +554,7 @@ def test_check_archives_with_remote_path_calls_borg_with_remote_path_parameters(
location_config={}, location_config={},
storage_config={}, storage_config={},
consistency_config=consistency_config, consistency_config=consistency_config,
local_borg_version='1.2.3',
remote_path='borg1', remote_path='borg1',
) )
@ -554,7 +565,7 @@ def test_check_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
consistency_config = {'check_last': check_last} consistency_config = {'check_last': check_last}
flexmock(module).should_receive('parse_checks') flexmock(module).should_receive('parse_checks')
flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks) flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks)
flexmock(module.info).should_receive('display_archives_info').and_return( flexmock(module.rinfo).should_receive('display_repository_info').and_return(
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
flexmock(module).should_receive('make_check_flags').with_args( flexmock(module).should_receive('make_check_flags').with_args(
@ -569,6 +580,7 @@ def test_check_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
location_config={}, location_config={},
storage_config={'lock_wait': 5}, storage_config={'lock_wait': 5},
consistency_config=consistency_config, consistency_config=consistency_config,
local_borg_version='1.2.3',
) )
@ -579,7 +591,7 @@ def test_check_archives_with_retention_prefix():
consistency_config = {'check_last': check_last, 'prefix': prefix} consistency_config = {'check_last': check_last, 'prefix': prefix}
flexmock(module).should_receive('parse_checks') flexmock(module).should_receive('parse_checks')
flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks) flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks)
flexmock(module.info).should_receive('display_archives_info').and_return( flexmock(module.rinfo).should_receive('display_repository_info').and_return(
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
flexmock(module).should_receive('make_check_flags').with_args( flexmock(module).should_receive('make_check_flags').with_args(
@ -594,6 +606,7 @@ def test_check_archives_with_retention_prefix():
location_config={}, location_config={},
storage_config={}, storage_config={},
consistency_config=consistency_config, consistency_config=consistency_config,
local_borg_version='1.2.3',
) )
@ -602,7 +615,7 @@ def test_check_archives_with_extra_borg_options_calls_borg_with_extra_options():
consistency_config = {'check_last': None} consistency_config = {'check_last': None}
flexmock(module).should_receive('parse_checks') flexmock(module).should_receive('parse_checks')
flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks) flexmock(module).should_receive('filter_checks_on_frequency').and_return(checks)
flexmock(module.info).should_receive('display_archives_info').and_return( flexmock(module.rinfo).should_receive('display_repository_info').and_return(
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
flexmock(module).should_receive('make_check_flags').and_return(()) flexmock(module).should_receive('make_check_flags').and_return(())
@ -615,4 +628,5 @@ def test_check_archives_with_extra_borg_options_calls_borg_with_extra_options():
location_config={}, location_config={},
storage_config={'extra_borg_options': {'check': '--extra --options'}}, storage_config={'extra_borg_options': {'check': '--extra --options'}},
consistency_config=consistency_config, consistency_config=consistency_config,
local_borg_version='1.2.3',
) )

View file

@ -9,6 +9,25 @@ from ..test_verbosity import insert_logging_mock
def test_display_archives_info_calls_borg_with_parameters(): def test_display_archives_info_calls_borg_with_parameters():
flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--repo', 'repo'),
output_log_level=logging.WARNING,
borg_local_path='borg',
extra_environment=None,
)
module.display_archives_info(
repository='repo',
storage_config={},
local_borg_version='2.3.4',
info_arguments=flexmock(archive=None, json=False),
)
def test_display_archives_info_without_borg_features_calls_borg_without_repo_flag():
flexmock(module.feature).should_receive('available').and_return(False)
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', 'repo'), ('borg', 'info', 'repo'),
@ -18,28 +37,36 @@ def test_display_archives_info_calls_borg_with_parameters():
) )
module.display_archives_info( module.display_archives_info(
repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=False) repository='repo',
storage_config={},
local_borg_version='2.3.4',
info_arguments=flexmock(archive=None, json=False),
) )
def test_display_archives_info_with_log_info_calls_borg_with_info_parameter(): def test_display_archives_info_with_log_info_calls_borg_with_info_parameter():
flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--info', 'repo'), ('borg', 'info', '--info', '--repo', 'repo'),
output_log_level=logging.WARNING, output_log_level=logging.WARNING,
borg_local_path='borg', borg_local_path='borg',
extra_environment=None, extra_environment=None,
) )
insert_logging_mock(logging.INFO) insert_logging_mock(logging.INFO)
module.display_archives_info( module.display_archives_info(
repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=False) repository='repo',
storage_config={},
local_borg_version='2.3.4',
info_arguments=flexmock(archive=None, json=False),
) )
def test_display_archives_info_with_log_info_and_json_suppresses_most_borg_output(): def test_display_archives_info_with_log_info_and_json_suppresses_most_borg_output():
flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--json', 'repo'), ('borg', 'info', '--json', '--repo', 'repo'),
output_log_level=None, output_log_level=None,
borg_local_path='borg', borg_local_path='borg',
extra_environment=None, extra_environment=None,
@ -47,16 +74,20 @@ def test_display_archives_info_with_log_info_and_json_suppresses_most_borg_outpu
insert_logging_mock(logging.INFO) insert_logging_mock(logging.INFO)
json_output = module.display_archives_info( json_output = module.display_archives_info(
repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=True) repository='repo',
storage_config={},
local_borg_version='2.3.4',
info_arguments=flexmock(archive=None, json=True),
) )
assert json_output == '[]' assert json_output == '[]'
def test_display_archives_info_with_log_debug_calls_borg_with_debug_parameter(): def test_display_archives_info_with_log_debug_calls_borg_with_debug_parameter():
flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--debug', '--show-rc', 'repo'), ('borg', 'info', '--debug', '--show-rc', '--repo', 'repo'),
output_log_level=logging.WARNING, output_log_level=logging.WARNING,
borg_local_path='borg', borg_local_path='borg',
extra_environment=None, extra_environment=None,
@ -64,14 +95,18 @@ def test_display_archives_info_with_log_debug_calls_borg_with_debug_parameter():
insert_logging_mock(logging.DEBUG) insert_logging_mock(logging.DEBUG)
module.display_archives_info( module.display_archives_info(
repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=False) repository='repo',
storage_config={},
local_borg_version='2.3.4',
info_arguments=flexmock(archive=None, json=False),
) )
def test_display_archives_info_with_log_debug_and_json_suppresses_most_borg_output(): def test_display_archives_info_with_log_debug_and_json_suppresses_most_borg_output():
flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--json', 'repo'), ('borg', 'info', '--json', '--repo', 'repo'),
output_log_level=None, output_log_level=None,
borg_local_path='borg', borg_local_path='borg',
extra_environment=None, extra_environment=None,
@ -79,29 +114,55 @@ def test_display_archives_info_with_log_debug_and_json_suppresses_most_borg_outp
insert_logging_mock(logging.DEBUG) insert_logging_mock(logging.DEBUG)
json_output = module.display_archives_info( json_output = module.display_archives_info(
repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=True) repository='repo',
storage_config={},
local_borg_version='2.3.4',
info_arguments=flexmock(archive=None, json=True),
) )
assert json_output == '[]' assert json_output == '[]'
def test_display_archives_info_with_json_calls_borg_with_json_parameter(): def test_display_archives_info_with_json_calls_borg_with_json_parameter():
flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--json', 'repo'), ('borg', 'info', '--json', '--repo', 'repo'),
output_log_level=None, output_log_level=None,
borg_local_path='borg', borg_local_path='borg',
extra_environment=None, extra_environment=None,
).and_return('[]') ).and_return('[]')
json_output = module.display_archives_info( json_output = module.display_archives_info(
repository='repo', storage_config={}, info_arguments=flexmock(archive=None, json=True) repository='repo',
storage_config={},
local_borg_version='2.3.4',
info_arguments=flexmock(archive=None, json=True),
) )
assert json_output == '[]' assert json_output == '[]'
def test_display_archives_info_with_archive_calls_borg_with_archive_parameter(): def test_display_archives_info_with_archive_calls_borg_with_glob_archives_parameter():
flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--repo', 'repo', '--glob-archives', 'archive'),
output_log_level=logging.WARNING,
borg_local_path='borg',
extra_environment=None,
)
module.display_archives_info(
repository='repo',
storage_config={},
local_borg_version='2.3.4',
info_arguments=flexmock(archive='archive', json=False),
)
def test_display_archives_info_with_archive_and_without_borg_features_calls_borg_with_repo_archive_parameter():
flexmock(module.feature).should_receive('available').and_return(False)
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', 'repo::archive'), ('borg', 'info', 'repo::archive'),
@ -111,14 +172,18 @@ def test_display_archives_info_with_archive_calls_borg_with_archive_parameter():
) )
module.display_archives_info( module.display_archives_info(
repository='repo', storage_config={}, info_arguments=flexmock(archive='archive', json=False) repository='repo',
storage_config={},
local_borg_version='2.3.4',
info_arguments=flexmock(archive='archive', json=False),
) )
def test_display_archives_info_with_local_path_calls_borg_via_local_path(): def test_display_archives_info_with_local_path_calls_borg_via_local_path():
flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg1', 'info', 'repo'), ('borg1', 'info', '--repo', 'repo'),
output_log_level=logging.WARNING, output_log_level=logging.WARNING,
borg_local_path='borg1', borg_local_path='borg1',
extra_environment=None, extra_environment=None,
@ -127,15 +192,17 @@ def test_display_archives_info_with_local_path_calls_borg_via_local_path():
module.display_archives_info( module.display_archives_info(
repository='repo', repository='repo',
storage_config={}, storage_config={},
local_borg_version='2.3.4',
info_arguments=flexmock(archive=None, json=False), info_arguments=flexmock(archive=None, json=False),
local_path='borg1', local_path='borg1',
) )
def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_parameters(): def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_parameters():
flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--remote-path', 'borg1', 'repo'), ('borg', 'info', '--remote-path', 'borg1', '--repo', 'repo'),
output_log_level=logging.WARNING, output_log_level=logging.WARNING,
borg_local_path='borg', borg_local_path='borg',
extra_environment=None, extra_environment=None,
@ -144,6 +211,7 @@ def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_para
module.display_archives_info( module.display_archives_info(
repository='repo', repository='repo',
storage_config={}, storage_config={},
local_borg_version='2.3.4',
info_arguments=flexmock(archive=None, json=False), info_arguments=flexmock(archive=None, json=False),
remote_path='borg1', remote_path='borg1',
) )
@ -151,9 +219,10 @@ def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_para
def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_parameters(): def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_parameters():
storage_config = {'lock_wait': 5} storage_config = {'lock_wait': 5}
flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--lock-wait', '5', 'repo'), ('borg', 'info', '--lock-wait', '5', '--repo', 'repo'),
output_log_level=logging.WARNING, output_log_level=logging.WARNING,
borg_local_path='borg', borg_local_path='borg',
extra_environment=None, extra_environment=None,
@ -162,15 +231,17 @@ def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_paramete
module.display_archives_info( module.display_archives_info(
repository='repo', repository='repo',
storage_config=storage_config, storage_config=storage_config,
local_borg_version='2.3.4',
info_arguments=flexmock(archive=None, json=False), info_arguments=flexmock(archive=None, json=False),
) )
@pytest.mark.parametrize('argument_name', ('prefix', 'glob_archives', 'sort_by', 'first', 'last')) @pytest.mark.parametrize('argument_name', ('prefix', 'glob_archives', 'sort_by', 'first', 'last'))
def test_display_archives_info_passes_through_arguments_to_borg(argument_name): def test_display_archives_info_passes_through_arguments_to_borg(argument_name):
flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args( flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--' + argument_name.replace('_', '-'), 'value', 'repo'), ('borg', 'info', '--' + argument_name.replace('_', '-'), 'value', '--repo', 'repo'),
output_log_level=logging.WARNING, output_log_level=logging.WARNING,
borg_local_path='borg', borg_local_path='borg',
extra_environment=None, extra_environment=None,
@ -179,5 +250,6 @@ def test_display_archives_info_passes_through_arguments_to_borg(argument_name):
module.display_archives_info( module.display_archives_info(
repository='repo', repository='repo',
storage_config={}, storage_config={},
local_borg_version='2.3.4',
info_arguments=flexmock(archive=None, json=False, **{argument_name: 'value'}), info_arguments=flexmock(archive=None, json=False, **{argument_name: 'value'}),
) )

View file

@ -1,132 +0,0 @@
import logging
import subprocess
import pytest
from flexmock import flexmock
from borgmatic.borg import init as module
from ..test_verbosity import insert_logging_mock
INFO_SOME_UNKNOWN_EXIT_CODE = -999
INIT_COMMAND = ('borg', 'init', '--encryption', 'repokey')
def insert_info_command_found_mock():
flexmock(module.info).should_receive('display_archives_info')
def insert_info_command_not_found_mock():
flexmock(module.info).should_receive('display_archives_info').and_raise(
subprocess.CalledProcessError(module.INFO_REPOSITORY_NOT_FOUND_EXIT_CODE, [])
)
def insert_init_command_mock(init_command, **kwargs):
flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args(
init_command,
output_file=module.DO_NOT_CAPTURE,
borg_local_path=init_command[0],
extra_environment=None,
).once()
def test_initialize_repository_calls_borg_with_parameters():
insert_info_command_not_found_mock()
insert_init_command_mock(INIT_COMMAND + ('repo',))
module.initialize_repository(repository='repo', storage_config={}, encryption_mode='repokey')
def test_initialize_repository_raises_for_borg_init_error():
insert_info_command_not_found_mock()
flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').and_raise(
module.subprocess.CalledProcessError(2, 'borg init')
)
with pytest.raises(subprocess.CalledProcessError):
module.initialize_repository(
repository='repo', storage_config={}, encryption_mode='repokey'
)
def test_initialize_repository_skips_initialization_when_repository_already_exists():
insert_info_command_found_mock()
module.initialize_repository(repository='repo', storage_config={}, encryption_mode='repokey')
def test_initialize_repository_raises_for_unknown_info_command_error():
flexmock(module.info).should_receive('display_archives_info').and_raise(
subprocess.CalledProcessError(INFO_SOME_UNKNOWN_EXIT_CODE, [])
)
with pytest.raises(subprocess.CalledProcessError):
module.initialize_repository(
repository='repo', storage_config={}, encryption_mode='repokey'
)
def test_initialize_repository_with_append_only_calls_borg_with_append_only_parameter():
insert_info_command_not_found_mock()
insert_init_command_mock(INIT_COMMAND + ('--append-only', 'repo'))
module.initialize_repository(
repository='repo', storage_config={}, encryption_mode='repokey', append_only=True
)
def test_initialize_repository_with_storage_quota_calls_borg_with_storage_quota_parameter():
insert_info_command_not_found_mock()
insert_init_command_mock(INIT_COMMAND + ('--storage-quota', '5G', 'repo'))
module.initialize_repository(
repository='repo', storage_config={}, encryption_mode='repokey', storage_quota='5G'
)
def test_initialize_repository_with_log_info_calls_borg_with_info_parameter():
insert_info_command_not_found_mock()
insert_init_command_mock(INIT_COMMAND + ('--info', 'repo'))
insert_logging_mock(logging.INFO)
module.initialize_repository(repository='repo', storage_config={}, encryption_mode='repokey')
def test_initialize_repository_with_log_debug_calls_borg_with_debug_parameter():
insert_info_command_not_found_mock()
insert_init_command_mock(INIT_COMMAND + ('--debug', 'repo'))
insert_logging_mock(logging.DEBUG)
module.initialize_repository(repository='repo', storage_config={}, encryption_mode='repokey')
def test_initialize_repository_with_local_path_calls_borg_via_local_path():
insert_info_command_not_found_mock()
insert_init_command_mock(('borg1',) + INIT_COMMAND[1:] + ('repo',))
module.initialize_repository(
repository='repo', storage_config={}, encryption_mode='repokey', local_path='borg1'
)
def test_initialize_repository_with_remote_path_calls_borg_with_remote_path_parameter():
insert_info_command_not_found_mock()
insert_init_command_mock(INIT_COMMAND + ('--remote-path', 'borg1', 'repo'))
module.initialize_repository(
repository='repo', storage_config={}, encryption_mode='repokey', remote_path='borg1'
)
def test_initialize_repository_with_extra_borg_options_calls_borg_with_extra_options():
insert_info_command_not_found_mock()
insert_init_command_mock(INIT_COMMAND + ('--extra', '--options', 'repo'))
module.initialize_repository(
repository='repo',
storage_config={'extra_borg_options': {'init': '--extra --options'}},
encryption_mode='repokey',
)

View file

@ -0,0 +1,183 @@
import logging
import subprocess
import pytest
from flexmock import flexmock
from borgmatic.borg import rcreate as module
from ..test_verbosity import insert_logging_mock
RINFO_SOME_UNKNOWN_EXIT_CODE = -999
RCREATE_COMMAND = ('borg', 'rcreate', '--encryption', 'repokey')
def insert_rinfo_command_found_mock():
flexmock(module.rinfo).should_receive('display_repository_info')
def insert_rinfo_command_not_found_mock():
flexmock(module.rinfo).should_receive('display_repository_info').and_raise(
subprocess.CalledProcessError(module.RINFO_REPOSITORY_NOT_FOUND_EXIT_CODE, [])
)
def insert_rcreate_command_mock(rcreate_command, **kwargs):
flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args(
rcreate_command,
output_file=module.DO_NOT_CAPTURE,
borg_local_path=rcreate_command[0],
extra_environment=None,
).once()
def test_create_repository_calls_borg_with_parameters():
insert_rinfo_command_not_found_mock()
insert_rcreate_command_mock(RCREATE_COMMAND + ('--repo', 'repo'))
flexmock(module.feature).should_receive('available').and_return(True)
module.create_repository(
repository='repo', storage_config={}, local_borg_version='2.3.4', encryption_mode='repokey'
)
def test_create_repository_without_borg_features_calls_borg_with_init_sub_command():
insert_rinfo_command_not_found_mock()
insert_rcreate_command_mock(('borg', 'init', '--encryption', 'repokey', 'repo'))
flexmock(module.feature).should_receive('available').and_return(False)
module.create_repository(
repository='repo', storage_config={}, local_borg_version='2.3.4', encryption_mode='repokey'
)
def test_create_repository_raises_for_borg_rcreate_error():
insert_rinfo_command_not_found_mock()
flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').and_raise(
module.subprocess.CalledProcessError(2, 'borg rcreate')
)
with pytest.raises(subprocess.CalledProcessError):
module.create_repository(
repository='repo',
storage_config={},
local_borg_version='2.3.4',
encryption_mode='repokey',
)
def test_create_repository_skips_creation_when_repository_already_exists():
insert_rinfo_command_found_mock()
flexmock(module.feature).should_receive('available').and_return(True)
module.create_repository(
repository='repo', storage_config={}, local_borg_version='2.3.4', encryption_mode='repokey'
)
def test_create_repository_raises_for_unknown_rinfo_command_error():
flexmock(module.rinfo).should_receive('display_repository_info').and_raise(
subprocess.CalledProcessError(RINFO_SOME_UNKNOWN_EXIT_CODE, [])
)
with pytest.raises(subprocess.CalledProcessError):
module.create_repository(
repository='repo',
storage_config={},
local_borg_version='2.3.4',
encryption_mode='repokey',
)
def test_create_repository_with_append_only_calls_borg_with_append_only_parameter():
insert_rinfo_command_not_found_mock()
insert_rcreate_command_mock(RCREATE_COMMAND + ('--append-only', '--repo', 'repo'))
flexmock(module.feature).should_receive('available').and_return(True)
module.create_repository(
repository='repo',
storage_config={},
local_borg_version='2.3.4',
encryption_mode='repokey',
append_only=True,
)
def test_create_repository_with_storage_quota_calls_borg_with_storage_quota_parameter():
insert_rinfo_command_not_found_mock()
insert_rcreate_command_mock(RCREATE_COMMAND + ('--storage-quota', '5G', '--repo', 'repo'))
flexmock(module.feature).should_receive('available').and_return(True)
module.create_repository(
repository='repo',
storage_config={},
local_borg_version='2.3.4',
encryption_mode='repokey',
storage_quota='5G',
)
def test_create_repository_with_log_info_calls_borg_with_info_parameter():
insert_rinfo_command_not_found_mock()
insert_rcreate_command_mock(RCREATE_COMMAND + ('--info', '--repo', 'repo'))
insert_logging_mock(logging.INFO)
flexmock(module.feature).should_receive('available').and_return(True)
module.create_repository(
repository='repo', storage_config={}, local_borg_version='2.3.4', encryption_mode='repokey'
)
def test_create_repository_with_log_debug_calls_borg_with_debug_parameter():
insert_rinfo_command_not_found_mock()
insert_rcreate_command_mock(RCREATE_COMMAND + ('--debug', '--repo', 'repo'))
insert_logging_mock(logging.DEBUG)
flexmock(module.feature).should_receive('available').and_return(True)
module.create_repository(
repository='repo', storage_config={}, local_borg_version='2.3.4', encryption_mode='repokey'
)
def test_create_repository_with_local_path_calls_borg_via_local_path():
insert_rinfo_command_not_found_mock()
insert_rcreate_command_mock(('borg1',) + RCREATE_COMMAND[1:] + ('--repo', 'repo'))
flexmock(module.feature).should_receive('available').and_return(True)
module.create_repository(
repository='repo',
storage_config={},
local_borg_version='2.3.4',
encryption_mode='repokey',
local_path='borg1',
)
def test_create_repository_with_remote_path_calls_borg_with_remote_path_parameter():
insert_rinfo_command_not_found_mock()
insert_rcreate_command_mock(RCREATE_COMMAND + ('--remote-path', 'borg1', '--repo', 'repo'))
flexmock(module.feature).should_receive('available').and_return(True)
module.create_repository(
repository='repo',
storage_config={},
local_borg_version='2.3.4',
encryption_mode='repokey',
remote_path='borg1',
)
def test_create_repository_with_extra_borg_options_calls_borg_with_extra_options():
insert_rinfo_command_not_found_mock()
insert_rcreate_command_mock(RCREATE_COMMAND + ('--extra', '--options', '--repo', 'repo'))
flexmock(module.feature).should_receive('available').and_return(True)
module.create_repository(
repository='repo',
storage_config={'extra_borg_options': {'rcreate': '--extra --options'}},
local_borg_version='2.3.4',
encryption_mode='repokey',
)

View file

@ -340,11 +340,11 @@ def test_run_configuration_retries_timeout_multiple_repos():
assert results == error_logs assert results == error_logs
def test_run_actions_does_not_raise_for_init_action(): def test_run_actions_does_not_raise_for_rcreate_action():
flexmock(module.borg_init).should_receive('initialize_repository') flexmock(module.borg_rcreate).should_receive('create_repository')
arguments = { arguments = {
'global': flexmock(monitoring_verbosity=1, dry_run=False), 'global': flexmock(monitoring_verbosity=1, dry_run=False),
'init': flexmock( 'rcreate': flexmock(
encryption_mode=flexmock(), append_only=flexmock(), storage_quota=flexmock() encryption_mode=flexmock(), append_only=flexmock(), storage_quota=flexmock()
), ),
} }