This commit is contained in:
Divyansh Singh 2023-04-01 22:12:41 +05:30
commit 32ab17fa46
27 changed files with 542 additions and 149 deletions

View file

@ -24,6 +24,8 @@ clone:
steps: steps:
- name: build - name: build
image: alpine:3.13 image: alpine:3.13
environment:
TEST_CONTAINER: true
pull: always pull: always
commands: commands:
- scripts/run-full-tests - scripts/run-full-tests

9
NEWS
View file

@ -1,3 +1,12 @@
1.7.11.dev0
* #479: Automatically use the "archive_name_format" option to filter which archives get used for
borgmatic actions that operate on multiple archives. See the documentation for more information:
https://torsion.org/borgmatic/docs/how-to/make-per-application-backups/#archive-naming
* #479: The "prefix" options have been deprecated in favor of the new "archive_name_format"
auto-matching behavior (see above).
* #662: Fix regression in which "check_repositories" option failed to match repositories.
* #663: Fix regression in which the "transfer" action produced a traceback.
1.7.10 1.7.10
* #396: When a database command errors, display and log the error message instead of swallowing it. * #396: When a database command errors, display and log the error message instead of swallowing it.
* #501: Optionally error if a source directory does not exist via "source_directories_must_exist" * #501: Optionally error if a source directory does not exist via "source_directories_must_exist"

View file

@ -17,10 +17,10 @@ def run_transfer(
''' '''
Run the "transfer" action for the given repository. Run the "transfer" action for the given repository.
''' '''
logger.info(f'{repository}: Transferring archives to repository') logger.info(f'{repository["path"]}: Transferring archives to repository')
borgmatic.borg.transfer.transfer_archives( borgmatic.borg.transfer.transfer_archives(
global_arguments.dry_run, global_arguments.dry_run,
repository, repository['path'],
storage, storage,
local_borg_version, local_borg_version,
transfer_arguments, transfer_arguments,

View file

@ -12,7 +12,6 @@ DEFAULT_CHECKS = (
{'name': 'repository', 'frequency': '1 month'}, {'name': 'repository', 'frequency': '1 month'},
{'name': 'archives', 'frequency': '1 month'}, {'name': 'archives', 'frequency': '1 month'},
) )
DEFAULT_PREFIX = '{hostname}-' # noqa: FS003
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -146,9 +145,10 @@ def filter_checks_on_frequency(
return tuple(filtered_checks) return tuple(filtered_checks)
def make_check_flags(local_borg_version, checks, check_last=None, prefix=None): def make_check_flags(local_borg_version, storage_config, checks, check_last=None, prefix=None):
''' '''
Given the local Borg version and a parsed sequence of checks, transform the checks into tuple of Given the local Borg version, a storge configuration dict, a parsed sequence of checks, the
check last value, and a consistency check prefix, transform the checks into tuple of
command-line flags. command-line flags.
For example, given parsed checks of: For example, given parsed checks of:
@ -174,10 +174,19 @@ def make_check_flags(local_borg_version, checks, check_last=None, prefix=None):
if 'archives' in checks: if 'archives' in checks:
last_flags = ('--last', str(check_last)) if check_last else () last_flags = ('--last', str(check_last)) if check_last else ()
if feature.available(feature.Feature.MATCH_ARCHIVES, local_borg_version): match_archives_flags = (
match_archives_flags = ('--match-archives', f'sh:{prefix}*') if prefix else () (
else: ('--match-archives', f'sh:{prefix}*')
match_archives_flags = ('--glob-archives', f'{prefix}*') if prefix else () if feature.available(feature.Feature.MATCH_ARCHIVES, local_borg_version)
else ('--glob-archives', f'{prefix}*')
)
if prefix
else (
flags.make_match_archives_flags(
storage_config.get('archive_name_format'), local_borg_version
)
)
)
else: else:
last_flags = () last_flags = ()
match_archives_flags = () match_archives_flags = ()
@ -291,7 +300,7 @@ def check_archives(
extra_borg_options = storage_config.get('extra_borg_options', {}).get('check', '') extra_borg_options = storage_config.get('extra_borg_options', {}).get('check', '')
if set(checks).intersection({'repository', 'archives', 'data'}): if set(checks).intersection({'repository', 'archives', 'data'}):
lock_wait = storage_config.get('lock_wait', None) lock_wait = storage_config.get('lock_wait')
verbosity_flags = () verbosity_flags = ()
if logger.isEnabledFor(logging.INFO): if logger.isEnabledFor(logging.INFO):
@ -299,12 +308,12 @@ def check_archives(
if logger.isEnabledFor(logging.DEBUG): if logger.isEnabledFor(logging.DEBUG):
verbosity_flags = ('--debug', '--show-rc') verbosity_flags = ('--debug', '--show-rc')
prefix = consistency_config.get('prefix', DEFAULT_PREFIX) prefix = consistency_config.get('prefix')
full_command = ( full_command = (
(local_path, 'check') (local_path, 'check')
+ (('--repair',) if repair else ()) + (('--repair',) if repair else ())
+ make_check_flags(local_borg_version, checks, check_last, prefix) + make_check_flags(local_borg_version, storage_config, checks, check_last, prefix)
+ (('--remote-path', remote_path) if remote_path else ()) + (('--remote-path', remote_path) if remote_path else ())
+ (('--lock-wait', str(lock_wait)) if lock_wait else ()) + (('--lock-wait', str(lock_wait)) if lock_wait else ())
+ verbosity_flags + verbosity_flags

View file

@ -1,4 +1,5 @@
import itertools import itertools
import re
from borgmatic.borg import feature from borgmatic.borg import feature
@ -56,3 +57,20 @@ def make_repository_archive_flags(repository_path, archive, local_borg_version):
if feature.available(feature.Feature.SEPARATE_REPOSITORY_ARCHIVE, local_borg_version) if feature.available(feature.Feature.SEPARATE_REPOSITORY_ARCHIVE, local_borg_version)
else (f'{repository_path}::{archive}',) else (f'{repository_path}::{archive}',)
) )
def make_match_archives_flags(archive_name_format, local_borg_version):
'''
Return the match archives flags that would match archives created with the given archive name
format (if any). This is done by replacing certain archive name format placeholders for
ephemeral data (like "{now}") with globs.
'''
if not archive_name_format:
return ()
match_archives = re.sub(r'\{(now|utcnow|pid)([:%\w\.-]*)\}', '*', archive_name_format)
if feature.available(feature.Feature.MATCH_ARCHIVES, local_borg_version):
return ('--match-archives', f'sh:{match_archives}')
else:
return ('--glob-archives', f'{match_archives}')

View file

@ -44,7 +44,11 @@ def display_archives_info(
else flags.make_flags('glob-archives', f'{info_arguments.prefix}*') else flags.make_flags('glob-archives', f'{info_arguments.prefix}*')
) )
if info_arguments.prefix if info_arguments.prefix
else () else (
flags.make_match_archives_flags(
storage_config.get('archive_name_format'), local_borg_version
)
)
) )
+ flags.make_flags_from_arguments( + flags.make_flags_from_arguments(
info_arguments, excludes=('repository', 'archive', 'prefix') info_arguments, excludes=('repository', 'archive', 'prefix')

View file

@ -7,10 +7,10 @@ from borgmatic.execute import execute_command
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def make_prune_flags(retention_config, local_borg_version): def make_prune_flags(storage_config, retention_config, local_borg_version):
''' '''
Given a retention config dict mapping from option name to value, transform it into an iterable of Given a retention config dict mapping from option name to value, transform it into an sequence of
command-line name-value flag pairs. command-line flags.
For example, given a retention config of: For example, given a retention config of:
@ -24,7 +24,7 @@ def make_prune_flags(retention_config, local_borg_version):
) )
''' '''
config = retention_config.copy() config = retention_config.copy()
prefix = config.pop('prefix', '{hostname}-') # noqa: FS003 prefix = config.pop('prefix', None)
if prefix: if prefix:
if feature.available(feature.Feature.MATCH_ARCHIVES, local_borg_version): if feature.available(feature.Feature.MATCH_ARCHIVES, local_borg_version):
@ -32,10 +32,16 @@ def make_prune_flags(retention_config, local_borg_version):
else: else:
config['glob_archives'] = f'{prefix}*' config['glob_archives'] = f'{prefix}*'
return ( flag_pairs = (
('--' + option_name.replace('_', '-'), str(value)) for option_name, value in config.items() ('--' + option_name.replace('_', '-'), str(value)) for option_name, value in config.items()
) )
return tuple(
element for pair in flag_pairs for element in pair
) + flags.make_match_archives_flags(
storage_config.get('archive_name_format'), local_borg_version
)
def prune_archives( def prune_archives(
dry_run, dry_run,
@ -60,11 +66,7 @@ def prune_archives(
full_command = ( full_command = (
(local_path, 'prune') (local_path, 'prune')
+ tuple( + make_prune_flags(storage_config, retention_config, local_borg_version)
element
for pair in make_prune_flags(retention_config, local_borg_version)
for element in pair
)
+ (('--remote-path', remote_path) if remote_path else ()) + (('--remote-path', remote_path) if remote_path else ())
+ (('--umask', str(umask)) if umask else ()) + (('--umask', str(umask)) if umask else ())
+ (('--lock-wait', str(lock_wait)) if lock_wait else ()) + (('--lock-wait', str(lock_wait)) if lock_wait else ())

View file

@ -94,7 +94,11 @@ def make_rlist_command(
else flags.make_flags('glob-archives', f'{rlist_arguments.prefix}*') else flags.make_flags('glob-archives', f'{rlist_arguments.prefix}*')
) )
if rlist_arguments.prefix if rlist_arguments.prefix
else () else (
flags.make_match_archives_flags(
storage_config.get('archive_name_format'), local_borg_version
)
)
) )
+ flags.make_flags_from_arguments(rlist_arguments, excludes=MAKE_FLAGS_EXCLUDES) + flags.make_flags_from_arguments(rlist_arguments, excludes=MAKE_FLAGS_EXCLUDES)
+ flags.make_repository_flags(repository_path, local_borg_version) + flags.make_repository_flags(repository_path, local_borg_version)

View file

@ -34,9 +34,16 @@ def transfer_archives(
'match-archives', transfer_arguments.match_archives or transfer_arguments.archive 'match-archives', transfer_arguments.match_archives or transfer_arguments.archive
) )
) )
+ flags.make_flags_from_arguments( + (
transfer_arguments, flags.make_flags_from_arguments(
excludes=('repository', 'source_repository', 'archive', 'match_archives'), transfer_arguments,
excludes=('repository', 'source_repository', 'archive', 'match_archives'),
)
or (
flags.make_match_archives_flags(
storage_config.get('archive_name_format'), local_borg_version
)
)
) )
+ flags.make_repository_flags(repository_path, local_borg_version) + flags.make_repository_flags(repository_path, local_borg_version)
+ flags.make_flags('other-repo', transfer_arguments.source_repository) + flags.make_flags('other-repo', transfer_arguments.source_repository)

View file

@ -378,11 +378,9 @@ properties:
description: | description: |
Name of the archive. Borg placeholders can be used. See the Name of the archive. Borg placeholders can be used. See the
output of "borg help placeholders" for details. Defaults to output of "borg help placeholders" for details. Defaults to
"{hostname}-{now:%Y-%m-%dT%H:%M:%S.%f}". If you specify this "{hostname}-{now:%Y-%m-%dT%H:%M:%S.%f}". When running
option, consider also specifying a prefix in the retention actions like rlist, info, or check, borgmatic automatically
and consistency sections to avoid accidental tries to match only archives created with this name format.
pruning/checking of archives with different archive name
formats.
example: "{hostname}-documents-{now}" example: "{hostname}-documents-{now}"
relocated_repo_access_is_ok: relocated_repo_access_is_ok:
type: boolean type: boolean
@ -477,10 +475,12 @@ properties:
prefix: prefix:
type: string type: string
description: | description: |
When pruning, only consider archive names starting with this Deprecated. When pruning, only consider archive names
prefix. Borg placeholders can be used. See the output of starting with this prefix. Borg placeholders can be used.
"borg help placeholders" for details. Defaults to See the output of "borg help placeholders" for details.
"{hostname}-". Use an empty value to disable the default. If a prefix is not specified, borgmatic defaults to
matching archives based on the archive_name_format (see
above).
example: sourcehostname example: sourcehostname
consistency: consistency:
type: object type: object
@ -538,12 +538,12 @@ properties:
items: items:
type: string type: string
description: | description: |
Paths to a subset of the repositories in the location Paths or labels for a subset of the repositories in the
section on which to run consistency checks. Handy in case location section on which to run consistency checks. Handy
some of your repositories are very large, and so running in case some of your repositories are very large, and so
consistency checks on them would take too long. Defaults to running consistency checks on them would take too long.
running consistency checks on all repositories configured in Defaults to running consistency checks on all repositories
the location section. configured in the location section.
example: example:
- user@backupserver:sourcehostname.borg - user@backupserver:sourcehostname.borg
check_last: check_last:
@ -556,11 +556,12 @@ properties:
prefix: prefix:
type: string type: string
description: | description: |
When performing the "archives" check, only consider archive Deprecated. When performing the "archives" check, only
names starting with this prefix. Borg placeholders can be consider archive names starting with this prefix. Borg
used. See the output of "borg help placeholders" for placeholders can be used. See the output of "borg help
details. Defaults to "{hostname}-". Use an empty value to placeholders" for details. If a prefix is not specified,
disable the default. borgmatic defaults to matching archives based on the
archive_name_format (see above).
example: sourcehostname example: sourcehostname
output: output:
type: object type: object

View file

@ -69,7 +69,10 @@ def apply_logical_validation(config_filename, parsed_configuration):
location_repositories = parsed_configuration.get('location', {}).get('repositories') location_repositories = parsed_configuration.get('location', {}).get('repositories')
check_repositories = parsed_configuration.get('consistency', {}).get('check_repositories', []) check_repositories = parsed_configuration.get('consistency', {}).get('check_repositories', [])
for repository in check_repositories: for repository in check_repositories:
if repository not in location_repositories: if not any(
repositories_match(repository, config_repository)
for config_repository in location_repositories
):
raise Validation_error( raise Validation_error(
config_filename, config_filename,
( (

View file

@ -25,7 +25,7 @@ so that you can run borgmatic commands while you're hacking on them to
make sure your changes work. make sure your changes work.
```bash ```bash
cd borgmatic/ cd borgmatic
pip3 install --user --editable . pip3 install --user --editable .
``` ```
@ -51,7 +51,6 @@ pip3 install --user tox
Finally, to actually run tests, run: Finally, to actually run tests, run:
```bash ```bash
cd borgmatic
tox tox
``` ```

View file

@ -54,6 +54,71 @@ choice](https://torsion.org/borgmatic/docs/how-to/set-up-backups/#autopilot),
each entry using borgmatic's `--config` flag instead of relying on each entry using borgmatic's `--config` flag instead of relying on
`/etc/borgmatic.d`. `/etc/borgmatic.d`.
## Archive naming
If you've got multiple borgmatic configuration files, you might want to create
archives with different naming schemes for each one. This is especially handy
if each configuration file is backing up to the same Borg repository but you
still want to be able to distinguish backup archives for one application from
another.
borgmatic supports this use case with an `archive_name_format` option. The
idea is that you define a string format containing a number of [Borg
placeholders](https://borgbackup.readthedocs.io/en/stable/usage/help.html#borg-placeholders),
and borgmatic uses that format to name any new archive it creates. For
instance:
```yaml
location:
...
archive_name_format: home-directories-{now}
```
This means that when borgmatic creates an archive, its name will start with
the string `home-directories-` and end with a timestamp for its creation time.
If `archive_name_format` is unspecified, the default is
`{hostname}-{now:%Y-%m-%dT%H:%M:%S.%f}`, meaning your system hostname plus a
timestamp in a particular format.
<span class="minilink minilink-addedin">New in version 1.7.11</span> borgmatic
uses the `archive_name_format` option to automatically limit which archives
get used for actions operating on multiple archives. This prevents, for
instance, duplicate archives from showing up in `rlist` or `info` results—even
if the same repository appears in multiple borgmatic configuration files. To
take advantage of this feature, simply use a different `archive_name_format`
in each configuration file.
Under the hood, borgmatic accomplishes this by substituting globs for certain
ephemeral data placeholders in your `archive_name_format`—and using the result
to filter archives when running supported actions.
For instance, let's say that you have this in your configuration:
```yaml
location:
...
archive_name_format: {hostname}-user-data-{now}
```
borgmatic considers `{now}` an emphemeral data placeholder that will probably
change per archive, while `{hostname}` won't. So it turns the example value
into `{hostname}-user-data-*` and applies it to filter down the set of
archives used for actions like `rlist`, `info`, `prune`, `check`, etc.
The end result is that when borgmatic runs the actions for a particular
application-specific configuration file, it only operates on the archives
created for that application. Of course, this doesn't apply to actions like
`compact` that operate on an entire repository.
<span class="minilink minilink-addedin">Prior to 1.7.11</span> The way to
limit the archives used for the `prune` action was a `prefix` option in the
`retention` section for matching against the start of archive names. And the
option for limiting the archives used for the `check` action was a separate
`prefix` in the `consistency` section. Both of these options are deprecated in
favor of the auto-matching behavior in newer versions of borgmatic.
## Configuration includes ## Configuration includes
Once you have multiple different configuration files, you might want to share Once you have multiple different configuration files, you might want to share
@ -272,7 +337,7 @@ Here's an example usage:
```yaml ```yaml
constants: constants:
user: foo user: foo
my_prefix: bar- archive_prefix: bar
location: location:
source_directories: source_directories:
@ -281,20 +346,14 @@ location:
... ...
storage: storage:
archive_name_format: '{my_prefix}{now}' archive_name_format: '{archive_prefix}-{now}'
retention:
prefix: {my_prefix}
consistency:
prefix: {my_prefix}
``` ```
In this example, when borgmatic runs, all instances of `{user}` get replaced In this example, when borgmatic runs, all instances of `{user}` get replaced
with `foo` and all instances of `{my_prefix}` get replaced with `bar-`. (And with `foo` and all instances of `{archive-prefix}` get replaced with `bar-`.
in this particular example, `{now}` doesn't get replaced with anything, but (And in this particular example, `{now}` doesn't get replaced with anything,
gets passed directly to Borg.) After substitution, the logical result looks but gets passed directly to Borg.) After substitution, the logical result
something like this: looks something like this:
```yaml ```yaml
location: location:
@ -305,12 +364,6 @@ location:
storage: storage:
archive_name_format: 'bar-{now}' archive_name_format: 'bar-{now}'
retention:
prefix: bar-
consistency:
prefix: bar-
``` ```
An alternate to constants is passing in your values via [environment An alternate to constants is passing in your values via [environment

View file

@ -90,7 +90,7 @@ installing borgmatic:
* [Fedora unofficial](https://copr.fedorainfracloud.org/coprs/heffer/borgmatic/) * [Fedora unofficial](https://copr.fedorainfracloud.org/coprs/heffer/borgmatic/)
* [Arch Linux](https://www.archlinux.org/packages/community/any/borgmatic/) * [Arch Linux](https://www.archlinux.org/packages/community/any/borgmatic/)
* [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=borgmatic) * [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=borgmatic)
* [OpenBSD](http://ports.su/sysutils/borgmatic) * [OpenBSD](https://openports.pl/path/sysutils/borgmatic)
* [openSUSE](https://software.opensuse.org/package/borgmatic) * [openSUSE](https://software.opensuse.org/package/borgmatic)
* [macOS (via Homebrew)](https://formulae.brew.sh/formula/borgmatic) * [macOS (via Homebrew)](https://formulae.brew.sh/formula/borgmatic)
* [macOS (via MacPorts)](https://ports.macports.org/port/borgmatic/) * [macOS (via MacPorts)](https://ports.macports.org/port/borgmatic/)

View file

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# This script is for running all tests, including end-to-end tests, on a developer machine. It sets # This script is for running end-to-end tests on a developer machine. It sets up database containers
# up database containers to run tests against, runs the tests, and then tears down the containers. # to run tests against, runs the tests, and then tears down the containers.
# #
# Run this script from the root directory of the borgmatic source. # Run this script from the root directory of the borgmatic source.
# #

View file

@ -8,7 +8,14 @@
# For more information, see: # For more information, see:
# https://torsion.org/borgmatic/docs/how-to/develop-on-borgmatic/ # https://torsion.org/borgmatic/docs/how-to/develop-on-borgmatic/
set -ex set -e
if [ -z "$TEST_CONTAINER" ] ; then
echo "This script is designed to work inside a test container and is not intended to"
echo "be run manually. If you're trying to run borgmatic's end-to-end tests, execute"
echo "scripts/run-end-to-end-dev-tests instead."
exit 1
fi
apk add --no-cache python3 py3-pip borgbackup postgresql-client mariadb-client mongodb-tools \ apk add --no-cache python3 py3-pip borgbackup postgresql-client mariadb-client mongodb-tools \
py3-ruamel.yaml py3-ruamel.yaml.clib bash sqlite py3-ruamel.yaml py3-ruamel.yaml.clib bash sqlite

View file

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

View file

@ -17,6 +17,8 @@ services:
MONGO_INITDB_ROOT_PASSWORD: test MONGO_INITDB_ROOT_PASSWORD: test
tests: tests:
image: alpine:3.13 image: alpine:3.13
environment:
TEST_CONTAINER: true
volumes: volumes:
- "../..:/app:ro" - "../..:/app:ro"
tmpfs: tmpfs:
@ -28,3 +30,4 @@ services:
depends_on: depends_on:
- postgresql - postgresql
- mysql - mysql
- mongodb

View file

@ -147,7 +147,7 @@ def test_log_outputs_kills_other_processes_when_one_errors():
['sleep', '2'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT ['sleep', '2'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT
) )
flexmock(module).should_receive('exit_code_indicates_error').with_args( flexmock(module).should_receive('exit_code_indicates_error').with_args(
other_process, None, 'borg' ['sleep', '2'], None, 'borg'
).and_return(False) ).and_return(False)
flexmock(module).should_receive('output_buffer_for_process').with_args(process, ()).and_return( flexmock(module).should_receive('output_buffer_for_process').with_args(process, ()).and_return(
process.stdout process.stdout

View file

@ -10,7 +10,7 @@ def test_run_transfer_does_not_raise():
global_arguments = flexmock(monitoring_verbosity=1, dry_run=False) global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
module.run_transfer( module.run_transfer(
repository='repo', repository={'path': 'repo'},
storage={}, storage={},
local_borg_version=None, local_borg_version=None,
transfer_arguments=transfer_arguments, transfer_arguments=transfer_arguments,

View file

@ -189,150 +189,170 @@ def test_filter_checks_on_frequency_restains_check_with_unelapsed_frequency_and_
def test_make_check_flags_with_repository_check_returns_flag(): def test_make_check_flags_with_repository_check_returns_flag():
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags('1.2.3', ('repository',)) flags = module.make_check_flags('1.2.3', {}, ('repository',))
assert flags == ('--repository-only',) assert flags == ('--repository-only',)
def test_make_check_flags_with_archives_check_returns_flag(): def test_make_check_flags_with_archives_check_returns_flag():
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags('1.2.3', ('archives',)) flags = module.make_check_flags('1.2.3', {}, ('archives',))
assert flags == ('--archives-only',) assert flags == ('--archives-only',)
def test_make_check_flags_with_data_check_returns_flag_and_implies_archives(): def test_make_check_flags_with_data_check_returns_flag_and_implies_archives():
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags('1.2.3', ('data',)) flags = module.make_check_flags('1.2.3', {}, ('data',))
assert flags == ('--archives-only', '--verify-data',) assert flags == ('--archives-only', '--verify-data',)
def test_make_check_flags_with_extract_omits_extract_flag(): def test_make_check_flags_with_extract_omits_extract_flag():
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags('1.2.3', ('extract',)) flags = module.make_check_flags('1.2.3', {}, ('extract',))
assert flags == () assert flags == ()
def test_make_check_flags_with_repository_and_data_checks_does_not_return_repository_only(): def test_make_check_flags_with_repository_and_data_checks_does_not_return_repository_only():
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags('1.2.3', ('repository', 'data',)) flags = module.make_check_flags('1.2.3', {}, ('repository', 'data',))
assert flags == ('--verify-data',) assert flags == ('--verify-data',)
def test_make_check_flags_with_default_checks_and_default_prefix_returns_default_flags(): def test_make_check_flags_with_default_checks_and_prefix_returns_default_flags():
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags('1.2.3', {}, ('repository', 'archives'), prefix='foo',)
assert flags == ('--match-archives', 'sh:foo*')
def test_make_check_flags_with_all_checks_and_prefix_returns_default_flags():
flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags( flags = module.make_check_flags(
'1.2.3', ('repository', 'archives'), prefix=module.DEFAULT_PREFIX '1.2.3', {}, ('repository', 'archives', 'extract'), prefix='foo',
) )
assert flags == ('--match-archives', f'sh:{module.DEFAULT_PREFIX}*') assert flags == ('--match-archives', 'sh:foo*')
def test_make_check_flags_with_all_checks_and_default_prefix_returns_default_flags(): def test_make_check_flags_with_all_checks_and_prefix_without_borg_features_returns_glob_archives_flags():
flexmock(module.feature).should_receive('available').and_return(True)
flags = module.make_check_flags(
'1.2.3', ('repository', 'archives', 'extract'), prefix=module.DEFAULT_PREFIX
)
assert flags == ('--match-archives', f'sh:{module.DEFAULT_PREFIX}*')
def test_make_check_flags_with_all_checks_and_default_prefix_without_borg_features_returns_glob_archives_flags():
flexmock(module.feature).should_receive('available').and_return(False) flexmock(module.feature).should_receive('available').and_return(False)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags( flags = module.make_check_flags(
'1.2.3', ('repository', 'archives', 'extract'), prefix=module.DEFAULT_PREFIX '1.2.3', {}, ('repository', 'archives', 'extract'), prefix='foo',
) )
assert flags == ('--glob-archives', f'{module.DEFAULT_PREFIX}*') assert flags == ('--glob-archives', 'foo*')
def test_make_check_flags_with_archives_check_and_last_includes_last_flag(): def test_make_check_flags_with_archives_check_and_last_includes_last_flag():
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags('1.2.3', ('archives',), check_last=3) flags = module.make_check_flags('1.2.3', {}, ('archives',), check_last=3)
assert flags == ('--archives-only', '--last', '3') assert flags == ('--archives-only', '--last', '3')
def test_make_check_flags_with_data_check_and_last_includes_last_flag(): def test_make_check_flags_with_data_check_and_last_includes_last_flag():
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags('1.2.3', ('data',), check_last=3) flags = module.make_check_flags('1.2.3', {}, ('data',), check_last=3)
assert flags == ('--archives-only', '--last', '3', '--verify-data') assert flags == ('--archives-only', '--last', '3', '--verify-data')
def test_make_check_flags_with_repository_check_and_last_omits_last_flag(): def test_make_check_flags_with_repository_check_and_last_omits_last_flag():
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags('1.2.3', ('repository',), check_last=3) flags = module.make_check_flags('1.2.3', {}, ('repository',), check_last=3)
assert flags == ('--repository-only',) assert flags == ('--repository-only',)
def test_make_check_flags_with_default_checks_and_last_includes_last_flag(): def test_make_check_flags_with_default_checks_and_last_includes_last_flag():
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags('1.2.3', ('repository', 'archives'), check_last=3) flags = module.make_check_flags('1.2.3', {}, ('repository', 'archives'), check_last=3)
assert flags == ('--last', '3') assert flags == ('--last', '3')
def test_make_check_flags_with_archives_check_and_prefix_includes_match_archives_flag(): def test_make_check_flags_with_archives_check_and_prefix_includes_match_archives_flag():
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags('1.2.3', ('archives',), prefix='foo-') flags = module.make_check_flags('1.2.3', {}, ('archives',), prefix='foo-')
assert flags == ('--archives-only', '--match-archives', 'sh:foo-*') assert flags == ('--archives-only', '--match-archives', 'sh:foo-*')
def test_make_check_flags_with_data_check_and_prefix_includes_match_archives_flag(): def test_make_check_flags_with_data_check_and_prefix_includes_match_archives_flag():
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags('1.2.3', ('data',), prefix='foo-') flags = module.make_check_flags('1.2.3', {}, ('data',), prefix='foo-')
assert flags == ('--archives-only', '--match-archives', 'sh:foo-*', '--verify-data') assert flags == ('--archives-only', '--match-archives', 'sh:foo-*', '--verify-data')
def test_make_check_flags_with_archives_check_and_empty_prefix_omits_match_archives_flag(): def test_make_check_flags_with_archives_check_and_empty_prefix_uses_archive_name_format_instead():
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
'bar-{now}', '1.2.3' # noqa: FS003
).and_return(('--match-archives', 'sh:bar-*'))
flags = module.make_check_flags('1.2.3', ('archives',), prefix='') flags = module.make_check_flags(
'1.2.3', {'archive_name_format': 'bar-{now}'}, ('archives',), prefix='' # noqa: FS003
)
assert flags == ('--archives-only',) assert flags == ('--archives-only', '--match-archives', 'sh:bar-*')
def test_make_check_flags_with_archives_check_and_none_prefix_omits_match_archives_flag(): def test_make_check_flags_with_archives_check_and_none_prefix_omits_match_archives_flag():
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags('1.2.3', ('archives',), prefix=None) flags = module.make_check_flags('1.2.3', {}, ('archives',), prefix=None)
assert flags == ('--archives-only',) assert flags == ('--archives-only',)
def test_make_check_flags_with_repository_check_and_prefix_omits_match_archives_flag(): def test_make_check_flags_with_repository_check_and_prefix_omits_match_archives_flag():
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags('1.2.3', ('repository',), prefix='foo-') flags = module.make_check_flags('1.2.3', {}, ('repository',), prefix='foo-')
assert flags == ('--repository-only',) assert flags == ('--repository-only',)
def test_make_check_flags_with_default_checks_and_prefix_includes_match_archives_flag(): def test_make_check_flags_with_default_checks_and_prefix_includes_match_archives_flag():
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flags = module.make_check_flags('1.2.3', ('repository', 'archives'), prefix='foo-') flags = module.make_check_flags('1.2.3', {}, ('repository', 'archives'), prefix='foo-')
assert flags == ('--match-archives', 'sh:foo-*') assert flags == ('--match-archives', 'sh:foo-*')
@ -427,7 +447,7 @@ def test_check_archives_calls_borg_with_parameters(checks):
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
flexmock(module).should_receive('make_check_flags').with_args( flexmock(module).should_receive('make_check_flags').with_args(
'1.2.3', checks, check_last, module.DEFAULT_PREFIX '1.2.3', {}, checks, check_last, prefix=None,
).and_return(()) ).and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(('borg', 'check', 'repo')) insert_execute_command_mock(('borg', 'check', 'repo'))
@ -581,7 +601,7 @@ def test_check_archives_with_local_path_calls_borg_via_local_path():
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
flexmock(module).should_receive('make_check_flags').with_args( flexmock(module).should_receive('make_check_flags').with_args(
'1.2.3', checks, check_last, module.DEFAULT_PREFIX '1.2.3', {}, checks, check_last, prefix=None,
).and_return(()) ).and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(('borg1', 'check', 'repo')) insert_execute_command_mock(('borg1', 'check', 'repo'))
@ -608,7 +628,7 @@ def test_check_archives_with_remote_path_calls_borg_with_remote_path_parameters(
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
flexmock(module).should_receive('make_check_flags').with_args( flexmock(module).should_receive('make_check_flags').with_args(
'1.2.3', checks, check_last, module.DEFAULT_PREFIX '1.2.3', {}, checks, check_last, prefix=None,
).and_return(()) ).and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(('borg', 'check', '--remote-path', 'borg1', 'repo')) insert_execute_command_mock(('borg', 'check', '--remote-path', 'borg1', 'repo'))
@ -628,6 +648,7 @@ def test_check_archives_with_remote_path_calls_borg_with_remote_path_parameters(
def test_check_archives_with_lock_wait_calls_borg_with_lock_wait_parameters(): def test_check_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
checks = ('repository',) checks = ('repository',)
check_last = flexmock() check_last = flexmock()
storage_config = {'lock_wait': 5}
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)
@ -635,7 +656,7 @@ def test_check_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
flexmock(module).should_receive('make_check_flags').with_args( flexmock(module).should_receive('make_check_flags').with_args(
'1.2.3', checks, check_last, module.DEFAULT_PREFIX '1.2.3', storage_config, checks, check_last, None,
).and_return(()) ).and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(('borg', 'check', '--lock-wait', '5', 'repo')) insert_execute_command_mock(('borg', 'check', '--lock-wait', '5', 'repo'))
@ -645,7 +666,7 @@ def test_check_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
module.check_archives( module.check_archives(
repository_path='repo', repository_path='repo',
location_config={}, location_config={},
storage_config={'lock_wait': 5}, storage_config=storage_config,
consistency_config=consistency_config, consistency_config=consistency_config,
local_borg_version='1.2.3', local_borg_version='1.2.3',
) )
@ -662,7 +683,7 @@ def test_check_archives_with_retention_prefix():
'{"repository": {"id": "repo"}}' '{"repository": {"id": "repo"}}'
) )
flexmock(module).should_receive('make_check_flags').with_args( flexmock(module).should_receive('make_check_flags').with_args(
'1.2.3', checks, check_last, prefix '1.2.3', {}, checks, check_last, prefix
).and_return(()) ).and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
insert_execute_command_mock(('borg', 'check', 'repo')) insert_execute_command_mock(('borg', 'check', 'repo'))

View file

@ -1,3 +1,4 @@
import pytest
from flexmock import flexmock from flexmock import flexmock
from borgmatic.borg import flags as module from borgmatic.borg import flags as module
@ -78,3 +79,35 @@ def test_make_repository_archive_flags_with_borg_features_joins_repository_and_a
assert module.make_repository_archive_flags( assert module.make_repository_archive_flags(
repository_path='repo', archive='archive', local_borg_version='1.2.3' repository_path='repo', archive='archive', local_borg_version='1.2.3'
) == ('repo::archive',) ) == ('repo::archive',)
@pytest.mark.parametrize(
'archive_name_format,feature_available,expected_result',
(
(None, True, ()),
('', True, ()),
(
'{hostname}-docs-{now}', # noqa: FS003
True,
('--match-archives', 'sh:{hostname}-docs-*'), # noqa: FS003
),
('{utcnow}-docs-{user}', True, ('--match-archives', 'sh:*-docs-{user}')), # noqa: FS003
('{fqdn}-{pid}', True, ('--match-archives', 'sh:{fqdn}-*')), # noqa: FS003
(
'stuff-{now:%Y-%m-%dT%H:%M:%S.%f}', # noqa: FS003
True,
('--match-archives', 'sh:stuff-*'),
),
('{hostname}-docs-{now}', False, ('--glob-archives', '{hostname}-docs-*')), # noqa: FS003
('{utcnow}-docs-{user}', False, ('--glob-archives', '*-docs-{user}')), # noqa: FS003
),
)
def test_make_match_archives_flags_makes_flags_with_globs(
archive_name_format, feature_available, expected_result
):
flexmock(module.feature).should_receive('available').and_return(feature_available)
assert (
module.make_match_archives_flags(archive_name_format, local_borg_version=flexmock())
== expected_result
)

View file

@ -12,6 +12,9 @@ def test_display_archives_info_calls_borg_with_parameters():
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '2.3.4'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -34,6 +37,9 @@ def test_display_archives_info_with_log_info_calls_borg_with_info_parameter():
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '2.3.4'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -56,6 +62,9 @@ def test_display_archives_info_with_log_info_and_json_suppresses_most_borg_outpu
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '2.3.4'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',)) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',))
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -78,6 +87,9 @@ def test_display_archives_info_with_log_debug_calls_borg_with_debug_parameter():
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '2.3.4'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -101,6 +113,9 @@ def test_display_archives_info_with_log_debug_and_json_suppresses_most_borg_outp
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '2.3.4'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',)) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',))
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -123,6 +138,9 @@ def test_display_archives_info_with_json_calls_borg_with_json_parameter():
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '2.3.4'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',)) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',))
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -147,6 +165,9 @@ def test_display_archives_info_with_archive_calls_borg_with_match_archives_param
flexmock(module.flags).should_receive('make_flags').with_args( flexmock(module.flags).should_receive('make_flags').with_args(
'match-archives', 'archive' 'match-archives', 'archive'
).and_return(('--match-archives', 'archive')) ).and_return(('--match-archives', 'archive'))
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '2.3.4'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -169,6 +190,9 @@ def test_display_archives_info_with_local_path_calls_borg_via_local_path():
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '2.3.4'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -195,6 +219,9 @@ def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_para
flexmock(module.flags).should_receive('make_flags').with_args( flexmock(module.flags).should_receive('make_flags').with_args(
'remote-path', 'borg1' 'remote-path', 'borg1'
).and_return(('--remote-path', 'borg1')) ).and_return(('--remote-path', 'borg1'))
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '2.3.4'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -221,6 +248,9 @@ def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_paramete
flexmock(module.flags).should_receive('make_flags').with_args('lock-wait', 5).and_return( flexmock(module.flags).should_receive('make_flags').with_args('lock-wait', 5).and_return(
('--lock-wait', '5') ('--lock-wait', '5')
) )
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '2.3.4'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
storage_config = {'lock_wait': 5} storage_config = {'lock_wait': 5}
@ -240,13 +270,16 @@ def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_paramete
) )
def test_display_archives_info_with_prefix_calls_borg_with_match_archives_parameters(): def test_display_archives_info_transforms_prefix_into_match_archives_parameters():
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_flags').with_args( flexmock(module.flags).should_receive('make_flags').with_args(
'match-archives', 'sh:foo*' 'match-archives', 'sh:foo*'
).and_return(('--match-archives', 'sh:foo*')) ).and_return(('--match-archives', 'sh:foo*'))
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '2.3.4'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -265,12 +298,68 @@ def test_display_archives_info_with_prefix_calls_borg_with_match_archives_parame
) )
def test_display_archives_info_prefers_prefix_over_archive_name_format():
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_flags').with_args(
'match-archives', 'sh:foo*'
).and_return(('--match-archives', 'sh:foo*'))
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '2.3.4'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--match-archives', 'sh:foo*', '--repo', 'repo'),
output_log_level=module.borgmatic.logger.ANSWER,
borg_local_path='borg',
extra_environment=None,
)
module.display_archives_info(
repository_path='repo',
storage_config={'archive_name_format': 'bar-{now}'}, # noqa: FS003
local_borg_version='2.3.4',
info_arguments=flexmock(archive=None, json=False, prefix='foo'),
)
def test_display_archives_info_transforms_archive_name_format_into_match_archives_parameters():
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
'bar-{now}', '2.3.4' # noqa: FS003
).and_return(('--match-archives', 'sh:bar-*'))
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args(
('borg', 'info', '--match-archives', 'sh:bar-*', '--repo', 'repo'),
output_log_level=module.borgmatic.logger.ANSWER,
borg_local_path='borg',
extra_environment=None,
)
module.display_archives_info(
repository_path='repo',
storage_config={'archive_name_format': 'bar-{now}'}, # noqa: FS003
local_borg_version='2.3.4',
info_arguments=flexmock(archive=None, json=False, prefix=None),
)
@pytest.mark.parametrize('argument_name', ('match_archives', 'sort_by', 'first', 'last')) @pytest.mark.parametrize('argument_name', ('match_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.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flag_name = f"--{argument_name.replace('_', ' ')}" flag_name = f"--{argument_name.replace('_', ' ')}"
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '2.3.4'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return( flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(
(flag_name, 'value') (flag_name, 'value')
) )

View file

@ -18,18 +18,17 @@ def insert_execute_command_mock(prune_command, output_log_level):
).once() ).once()
BASE_PRUNE_FLAGS = (('--keep-daily', '1'), ('--keep-weekly', '2'), ('--keep-monthly', '3')) BASE_PRUNE_FLAGS = ('--keep-daily', '1', '--keep-weekly', '2', '--keep-monthly', '3')
def test_make_prune_flags_returns_flags_from_config_plus_default_prefix_glob(): def test_make_prune_flags_returns_flags_from_config():
retention_config = OrderedDict((('keep_daily', 1), ('keep_weekly', 2), ('keep_monthly', 3))) retention_config = OrderedDict((('keep_daily', 1), ('keep_weekly', 2), ('keep_monthly', 3)))
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
result = module.make_prune_flags(retention_config, local_borg_version='1.2.3') result = module.make_prune_flags({}, retention_config, local_borg_version='1.2.3')
assert tuple(result) == BASE_PRUNE_FLAGS + ( assert result == BASE_PRUNE_FLAGS
('--match-archives', 'sh:{hostname}-*'), # noqa: FS003
)
def test_make_prune_flags_accepts_prefix_with_placeholders(): def test_make_prune_flags_accepts_prefix_with_placeholders():
@ -37,15 +36,18 @@ def test_make_prune_flags_accepts_prefix_with_placeholders():
(('keep_daily', 1), ('prefix', 'Documents_{hostname}-{now}')) # noqa: FS003 (('keep_daily', 1), ('prefix', 'Documents_{hostname}-{now}')) # noqa: FS003
) )
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
result = module.make_prune_flags(retention_config, local_borg_version='1.2.3') result = module.make_prune_flags({}, retention_config, local_borg_version='1.2.3')
expected = ( expected = (
('--keep-daily', '1'), '--keep-daily',
('--match-archives', 'sh:Documents_{hostname}-{now}*'), # noqa: FS003 '1',
'--match-archives',
'sh:Documents_{hostname}-{now}*', # noqa: FS003
) )
assert tuple(result) == expected assert result == expected
def test_make_prune_flags_with_prefix_without_borg_features_uses_glob_archives(): def test_make_prune_flags_with_prefix_without_borg_features_uses_glob_archives():
@ -53,37 +55,38 @@ def test_make_prune_flags_with_prefix_without_borg_features_uses_glob_archives()
(('keep_daily', 1), ('prefix', 'Documents_{hostname}-{now}')) # noqa: FS003 (('keep_daily', 1), ('prefix', 'Documents_{hostname}-{now}')) # noqa: FS003
) )
flexmock(module.feature).should_receive('available').and_return(False) flexmock(module.feature).should_receive('available').and_return(False)
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
result = module.make_prune_flags(retention_config, local_borg_version='1.2.3') result = module.make_prune_flags({}, retention_config, local_borg_version='1.2.3')
expected = ( expected = (
('--keep-daily', '1'), '--keep-daily',
('--glob-archives', 'Documents_{hostname}-{now}*'), # noqa: FS003 '1',
'--glob-archives',
'Documents_{hostname}-{now}*', # noqa: FS003
) )
assert tuple(result) == expected assert result == expected
def test_make_prune_flags_treats_empty_prefix_as_no_prefix(): def test_make_prune_flags_without_prefix_uses_archive_name_format_instead():
retention_config = OrderedDict((('keep_daily', 1), ('prefix', ''))) storage_config = {'archive_name_format': 'bar-{now}'} # noqa: FS003
flexmock(module.feature).should_receive('available').and_return(True)
result = module.make_prune_flags(retention_config, local_borg_version='1.2.3')
expected = (('--keep-daily', '1'),)
assert tuple(result) == expected
def test_make_prune_flags_treats_none_prefix_as_no_prefix():
retention_config = OrderedDict((('keep_daily', 1), ('prefix', None))) retention_config = OrderedDict((('keep_daily', 1), ('prefix', None)))
flexmock(module.feature).should_receive('available').and_return(True) flexmock(module.feature).should_receive('available').and_return(True)
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
'bar-{now}', '1.2.3' # noqa: FS003
).and_return(('--match-archives', 'sh:bar-*'))
result = module.make_prune_flags(retention_config, local_borg_version='1.2.3') result = module.make_prune_flags(storage_config, retention_config, local_borg_version='1.2.3')
expected = (('--keep-daily', '1'),) expected = (
'--keep-daily',
'1',
'--match-archives',
'sh:bar-*', # noqa: FS003
)
assert tuple(result) == expected assert result == expected
PRUNE_COMMAND = ('borg', 'prune', '--keep-daily', '1', '--keep-weekly', '2', '--keep-monthly', '3') PRUNE_COMMAND = ('borg', 'prune', '--keep-daily', '1', '--keep-weekly', '2', '--keep-monthly', '3')

View file

@ -127,6 +127,9 @@ def test_resolve_archive_name_with_lock_wait_calls_borg_with_lock_wait_parameter
def test_make_rlist_command_includes_log_info(): def test_make_rlist_command_includes_log_info():
insert_logging_mock(logging.INFO) insert_logging_mock(logging.INFO)
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '1.2.3'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
@ -143,6 +146,9 @@ def test_make_rlist_command_includes_log_info():
def test_make_rlist_command_includes_json_but_not_info(): def test_make_rlist_command_includes_json_but_not_info():
insert_logging_mock(logging.INFO) insert_logging_mock(logging.INFO)
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '1.2.3'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',)) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',))
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
@ -159,6 +165,9 @@ def test_make_rlist_command_includes_json_but_not_info():
def test_make_rlist_command_includes_log_debug(): def test_make_rlist_command_includes_log_debug():
insert_logging_mock(logging.DEBUG) insert_logging_mock(logging.DEBUG)
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '1.2.3'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
@ -175,6 +184,9 @@ def test_make_rlist_command_includes_log_debug():
def test_make_rlist_command_includes_json_but_not_debug(): def test_make_rlist_command_includes_json_but_not_debug():
insert_logging_mock(logging.DEBUG) insert_logging_mock(logging.DEBUG)
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '1.2.3'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',)) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',))
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
@ -190,6 +202,9 @@ def test_make_rlist_command_includes_json_but_not_debug():
def test_make_rlist_command_includes_json(): def test_make_rlist_command_includes_json():
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '1.2.3'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',)) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--json',))
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
@ -207,6 +222,9 @@ def test_make_rlist_command_includes_lock_wait():
flexmock(module.flags).should_receive('make_flags').and_return(()).and_return( flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(
('--lock-wait', '5') ('--lock-wait', '5')
).and_return(()) ).and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '1.2.3'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
@ -222,6 +240,9 @@ def test_make_rlist_command_includes_lock_wait():
def test_make_rlist_command_includes_local_path(): def test_make_rlist_command_includes_local_path():
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '1.2.3'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
@ -240,6 +261,9 @@ def test_make_rlist_command_includes_remote_path():
flexmock(module.flags).should_receive('make_flags').and_return( flexmock(module.flags).should_receive('make_flags').and_return(
('--remote-path', 'borg2') ('--remote-path', 'borg2')
).and_return(()).and_return(()) ).and_return(()).and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '1.2.3'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
@ -258,6 +282,9 @@ def test_make_rlist_command_transforms_prefix_into_match_archives():
flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(()).and_return( flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(()).and_return(
('--match-archives', 'sh:foo*') ('--match-archives', 'sh:foo*')
) )
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '1.2.3'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
@ -271,8 +298,47 @@ def test_make_rlist_command_transforms_prefix_into_match_archives():
assert command == ('borg', 'list', '--match-archives', 'sh:foo*', 'repo') assert command == ('borg', 'list', '--match-archives', 'sh:foo*', 'repo')
def test_make_rlist_command_prefers_prefix_over_archive_name_format():
flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(()).and_return(
('--match-archives', 'sh:foo*')
)
flexmock(module.flags).should_receive('make_match_archives_flags').never()
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
command = module.make_rlist_command(
repository_path='repo',
storage_config={'archive_name_format': 'bar-{now}'}, # noqa: FS003
local_borg_version='1.2.3',
rlist_arguments=flexmock(archive=None, paths=None, json=False, prefix='foo'),
)
assert command == ('borg', 'list', '--match-archives', 'sh:foo*', 'repo')
def test_make_rlist_command_transforms_archive_name_format_into_match_archives():
flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
'bar-{now}', '1.2.3' # noqa: FS003
).and_return(('--match-archives', 'sh:bar-*'))
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
command = module.make_rlist_command(
repository_path='repo',
storage_config={'archive_name_format': 'bar-{now}'}, # noqa: FS003
local_borg_version='1.2.3',
rlist_arguments=flexmock(archive=None, paths=None, json=False, prefix=None),
)
assert command == ('borg', 'list', '--match-archives', 'sh:bar-*', 'repo')
def test_make_rlist_command_includes_short(): def test_make_rlist_command_includes_short():
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '1.2.3'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--short',)) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(('--short',))
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',)) flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
@ -301,6 +367,9 @@ def test_make_rlist_command_includes_short():
) )
def test_make_rlist_command_includes_additional_flags(argument_name): def test_make_rlist_command_includes_additional_flags(argument_name):
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
None, '1.2.3'
).and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return( flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(
(f"--{argument_name.replace('_', '-')}", 'value') (f"--{argument_name.replace('_', '-')}", 'value')
) )

View file

@ -12,6 +12,7 @@ def test_transfer_archives_calls_borg_with_flags():
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -41,6 +42,7 @@ def test_transfer_archives_with_dry_run_calls_borg_with_dry_run_flag():
flexmock(module.flags).should_receive('make_flags').with_args('dry-run', True).and_return( flexmock(module.flags).should_receive('make_flags').with_args('dry-run', True).and_return(
('--dry-run',) ('--dry-run',)
) )
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -67,6 +69,7 @@ def test_transfer_archives_with_log_info_calls_borg_with_info_flag():
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -93,6 +96,7 @@ def test_transfer_archives_with_log_debug_calls_borg_with_debug_flag():
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -123,6 +127,7 @@ def test_transfer_archives_with_archive_calls_borg_with_match_archives_flag():
flexmock(module.flags).should_receive('make_flags').with_args( flexmock(module.flags).should_receive('make_flags').with_args(
'match-archives', 'archive' 'match-archives', 'archive'
).and_return(('--match-archives', 'archive')) ).and_return(('--match-archives', 'archive'))
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -137,7 +142,7 @@ def test_transfer_archives_with_archive_calls_borg_with_match_archives_flag():
module.transfer_archives( module.transfer_archives(
dry_run=False, dry_run=False,
repository_path='repo', repository_path='repo',
storage_config={}, storage_config={'archive_name_format': 'bar-{now}'}, # noqa: FS003
local_borg_version='2.3.4', local_borg_version='2.3.4',
transfer_arguments=flexmock( transfer_arguments=flexmock(
archive='archive', progress=None, match_archives=None, source_repository=None archive='archive', progress=None, match_archives=None, source_repository=None
@ -152,6 +157,7 @@ def test_transfer_archives_with_match_archives_calls_borg_with_match_archives_fl
flexmock(module.flags).should_receive('make_flags').with_args( flexmock(module.flags).should_receive('make_flags').with_args(
'match-archives', 'sh:foo*' 'match-archives', 'sh:foo*'
).and_return(('--match-archives', 'sh:foo*')) ).and_return(('--match-archives', 'sh:foo*'))
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -166,7 +172,7 @@ def test_transfer_archives_with_match_archives_calls_borg_with_match_archives_fl
module.transfer_archives( module.transfer_archives(
dry_run=False, dry_run=False,
repository_path='repo', repository_path='repo',
storage_config={}, storage_config={'archive_name_format': 'bar-{now}'}, # noqa: FS003
local_borg_version='2.3.4', local_borg_version='2.3.4',
transfer_arguments=flexmock( transfer_arguments=flexmock(
archive=None, progress=None, match_archives='sh:foo*', source_repository=None archive=None, progress=None, match_archives='sh:foo*', source_repository=None
@ -174,10 +180,40 @@ def test_transfer_archives_with_match_archives_calls_borg_with_match_archives_fl
) )
def test_transfer_archives_with_archive_name_format_calls_borg_with_match_archives_flag():
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
'bar-{now}', '2.3.4' # noqa: FS003
).and_return(('--match-archives', 'sh:bar-*'))
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment')
flexmock(module).should_receive('execute_command').with_args(
('borg', 'transfer', '--match-archives', 'sh:bar-*', '--repo', 'repo'),
output_log_level=module.borgmatic.logger.ANSWER,
output_file=None,
borg_local_path='borg',
extra_environment=None,
)
module.transfer_archives(
dry_run=False,
repository_path='repo',
storage_config={'archive_name_format': 'bar-{now}'}, # noqa: FS003
local_borg_version='2.3.4',
transfer_arguments=flexmock(
archive=None, progress=None, match_archives=None, source_repository=None
),
)
def test_transfer_archives_with_local_path_calls_borg_via_local_path(): def test_transfer_archives_with_local_path_calls_borg_via_local_path():
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -208,6 +244,7 @@ def test_transfer_archives_with_remote_path_calls_borg_with_remote_path_flags():
flexmock(module.flags).should_receive('make_flags').with_args( flexmock(module.flags).should_receive('make_flags').with_args(
'remote-path', 'borg2' 'remote-path', 'borg2'
).and_return(('--remote-path', 'borg2')) ).and_return(('--remote-path', 'borg2'))
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -238,6 +275,7 @@ def test_transfer_archives_with_lock_wait_calls_borg_with_lock_wait_flags():
flexmock(module.flags).should_receive('make_flags').with_args('lock-wait', 5).and_return( flexmock(module.flags).should_receive('make_flags').with_args('lock-wait', 5).and_return(
('--lock-wait', '5') ('--lock-wait', '5')
) )
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
storage_config = {'lock_wait': 5} storage_config = {'lock_wait': 5}
@ -265,6 +303,7 @@ def test_transfer_archives_with_progress_calls_borg_with_progress_flag():
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels') flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')
@ -293,6 +332,7 @@ def test_transfer_archives_passes_through_arguments_to_borg(argument_name):
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
flag_name = f"--{argument_name.replace('_', ' ')}" flag_name = f"--{argument_name.replace('_', ' ')}"
flexmock(module.flags).should_receive('make_flags').and_return(()) flexmock(module.flags).should_receive('make_flags').and_return(())
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return( flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(
(flag_name, 'value') (flag_name, 'value')
) )
@ -327,6 +367,7 @@ def test_transfer_archives_with_source_repository_calls_borg_with_other_repo_fla
flexmock(module.flags).should_receive('make_flags').with_args('other-repo', 'other').and_return( flexmock(module.flags).should_receive('make_flags').with_args('other-repo', 'other').and_return(
('--other-repo', 'other') ('--other-repo', 'other')
) )
flexmock(module.flags).should_receive('make_match_archives_flags').and_return(())
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(()) flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo')) flexmock(module.flags).should_receive('make_repository_flags').and_return(('--repo', 'repo'))
flexmock(module.environment).should_receive('make_environment') flexmock(module.environment).should_receive('make_environment')

View file

@ -37,7 +37,7 @@ def test_validation_error_string_contains_errors():
assert 'uh oh' in result assert 'uh oh' in result
def test_apply_locical_validation_raises_if_unknown_repository_in_check_repositories(): def test_apply_logical_validation_raises_if_unknown_repository_in_check_repositories():
flexmock(module).format_json_error = lambda error: error.message flexmock(module).format_json_error = lambda error: error.message
with pytest.raises(module.Validation_error): with pytest.raises(module.Validation_error):
@ -51,17 +51,33 @@ def test_apply_locical_validation_raises_if_unknown_repository_in_check_reposito
) )
def test_apply_locical_validation_does_not_raise_if_known_repository_in_check_repositories(): def test_apply_logical_validation_does_not_raise_if_known_repository_path_in_check_repositories():
module.apply_logical_validation( module.apply_logical_validation(
'config.yaml', 'config.yaml',
{ {
'location': {'repositories': ['repo.borg', 'other.borg']}, 'location': {'repositories': [{'path': 'repo.borg'}, {'path': 'other.borg'}]},
'retention': {'keep_secondly': 1000}, 'retention': {'keep_secondly': 1000},
'consistency': {'check_repositories': ['repo.borg']}, 'consistency': {'check_repositories': ['repo.borg']},
}, },
) )
def test_apply_logical_validation_does_not_raise_if_known_repository_label_in_check_repositories():
module.apply_logical_validation(
'config.yaml',
{
'location': {
'repositories': [
{'path': 'repo.borg', 'label': 'my_repo'},
{'path': 'other.borg', 'label': 'other_repo'},
]
},
'retention': {'keep_secondly': 1000},
'consistency': {'check_repositories': ['my_repo']},
},
)
def test_apply_logical_validation_does_not_raise_if_archive_name_format_and_prefix_present(): def test_apply_logical_validation_does_not_raise_if_archive_name_format_and_prefix_present():
module.apply_logical_validation( module.apply_logical_validation(
'config.yaml', 'config.yaml',