Support for Borg 2's rcreate and rinfo sub-commands (#557).
This commit is contained in:
parent
22149c6401
commit
622caa0c21
18 changed files with 434 additions and 239 deletions
3
NEWS
3
NEWS
|
@ -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.
|
||||||
|
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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),
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -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(
|
||||||
|
|
|
@ -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'])
|
||||||
|
|
||||||
|
|
|
@ -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',
|
||||||
)
|
)
|
||||||
|
|
|
@ -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'}),
|
||||||
)
|
)
|
||||||
|
|
|
@ -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',
|
|
||||||
)
|
|
183
tests/unit/borg/test_rcreate.py
Normal file
183
tests/unit/borg/test_rcreate.py
Normal 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',
|
||||||
|
)
|
|
@ -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()
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue