mirror of
https://github.com/borgmatic-collective/borgmatic.git
synced 2026-07-30 21:33:00 +02:00
Several related fixes and improvements in logging, output capturing, and command execution (#1235).
Reviewed-on: https://projects.torsion.org/borgmatic-collective/borgmatic/pulls/1235
This commit is contained in:
@@ -39,6 +39,7 @@
|
||||
* SECURITY: Prevent shell injection attacks via constant interpolation in command hooks. (This was
|
||||
already implemented for deprecated "before_*"/"after_*" command hooks.)
|
||||
* Fix for an error in the "key import" action when importing a key from stdin.
|
||||
* Update the "list" action to support the "--json" flag when the "--archive" flag is also used.
|
||||
* Promote the ZFS, LVM, and Btrfs hooks from beta features to stable.
|
||||
|
||||
2.0.13
|
||||
|
||||
+31
-19
@@ -448,24 +448,22 @@ def collect_spot_check_archive_paths(
|
||||
borgmatic_source_directory = borgmatic.config.paths.get_borgmatic_source_directory(config)
|
||||
|
||||
return tuple(
|
||||
path
|
||||
for line in borgmatic.borg.list.capture_archive_listing(
|
||||
entry['path']
|
||||
for entry in borgmatic.borg.list.capture_archive_listing(
|
||||
repository['path'],
|
||||
archive,
|
||||
config,
|
||||
local_borg_version,
|
||||
global_arguments,
|
||||
path_format='{type} {path}{NUL}',
|
||||
local_path=local_path,
|
||||
remote_path=remote_path,
|
||||
)
|
||||
for (file_type, path) in (line.split(' ', 1),)
|
||||
if file_type not in {BORG_DIRECTORY_FILE_TYPE, BORG_PIPE_FILE_TYPE}
|
||||
if pathlib.Path('borgmatic') not in pathlib.Path(path).parents
|
||||
if entry['type'] not in {BORG_DIRECTORY_FILE_TYPE, BORG_PIPE_FILE_TYPE}
|
||||
if pathlib.Path('borgmatic') not in pathlib.Path(entry['path']).parents
|
||||
if pathlib.Path(borgmatic_source_directory.lstrip(os.path.sep))
|
||||
not in pathlib.Path(path).parents
|
||||
not in pathlib.Path(entry['path']).parents
|
||||
if pathlib.Path(borgmatic_runtime_directory.lstrip(os.path.sep))
|
||||
not in pathlib.Path(path).parents
|
||||
not in pathlib.Path(entry['path']).parents
|
||||
)
|
||||
|
||||
|
||||
@@ -523,20 +521,34 @@ def compare_spot_check_hashes(
|
||||
if not source_sample_paths_subset:
|
||||
break
|
||||
|
||||
hash_paths = tuple(
|
||||
path for path in source_sample_paths_subset if path in hashable_source_sample_path
|
||||
)
|
||||
hash_lines = borgmatic.execute.execute_command_and_capture_output(
|
||||
tuple(
|
||||
shlex.quote(part)
|
||||
for part in shlex.split(spot_check_config.get('xxh64sum_command', 'xxh64sum'))
|
||||
)
|
||||
+ tuple(
|
||||
path for path in source_sample_paths_subset if path in hashable_source_sample_path
|
||||
),
|
||||
+ hash_paths,
|
||||
working_directory=working_directory,
|
||||
)
|
||||
|
||||
source_hashes.update(
|
||||
**dict(
|
||||
(reversed(line.split(' ', 1)) for line in hash_lines),
|
||||
zip(
|
||||
# xxh64sum rewrites/escapes the paths that it returns alongside its hashes, for
|
||||
# instance if they contain special characters. When that happens, they don't
|
||||
# match the original source paths and therefore hash lookups fail. So when
|
||||
# building this lookup dict, use the original unaltered paths we provided as
|
||||
# input to xxh64sum.
|
||||
hash_paths,
|
||||
(
|
||||
# For some reason, xxh64sum prefixes the hash with a backslash if the path
|
||||
# contains a newline. Work around that.
|
||||
line.split(' ', 1)[0].lstrip('\\')
|
||||
for line in hash_lines
|
||||
),
|
||||
),
|
||||
# Represent non-existent files as having empty hashes so the comparison below still
|
||||
# works. Same thing for filesystem links, since Borg produces empty archive hashes
|
||||
# for them.
|
||||
@@ -550,21 +562,21 @@ def compare_spot_check_hashes(
|
||||
|
||||
# Get the hash for each file in the archive.
|
||||
archive_hashes.update(
|
||||
**dict(
|
||||
reversed(line.split(' ', 1))
|
||||
for line in borgmatic.borg.list.capture_archive_listing(
|
||||
**{
|
||||
entry['path']: entry['xxh64']
|
||||
for entry in borgmatic.borg.list.capture_archive_listing(
|
||||
repository['path'],
|
||||
archive,
|
||||
config,
|
||||
local_borg_version,
|
||||
global_arguments,
|
||||
list_paths=source_sample_paths_subset,
|
||||
path_format='{xxh64} {path}{NUL}',
|
||||
path_format='{xxh64}{path}',
|
||||
local_path=local_path,
|
||||
remote_path=remote_path,
|
||||
)
|
||||
if line
|
||||
),
|
||||
if entry
|
||||
},
|
||||
)
|
||||
|
||||
# Compare the source hashes with the archive hashes to see how many match.
|
||||
@@ -799,8 +811,8 @@ def run_check(
|
||||
write_check_time(make_check_time_path(config, repository_id, 'extract'))
|
||||
|
||||
if 'spot' in checks:
|
||||
logger.info('Running spot check')
|
||||
with borgmatic.config.paths.Runtime_directory(config) as borgmatic_runtime_directory:
|
||||
logger.info('Running spot check')
|
||||
spot_check(
|
||||
repository,
|
||||
config,
|
||||
|
||||
@@ -277,7 +277,7 @@ def collect_dumps_from_archive(
|
||||
dumps_from_archive = {} # Use a dict as an ordered set.
|
||||
|
||||
# There is (at most) one dump metadata file per data source hook. Load each.
|
||||
for dumps_metadata_path in borgmatic.borg.list.capture_archive_listing(
|
||||
for dumps_metadata_entry in borgmatic.borg.list.capture_archive_listing(
|
||||
repository,
|
||||
archive,
|
||||
config,
|
||||
@@ -300,7 +300,7 @@ def collect_dumps_from_archive(
|
||||
local_path=local_path,
|
||||
remote_path=remote_path,
|
||||
):
|
||||
if not dumps_metadata_path:
|
||||
if not dumps_metadata_entry.get('path'):
|
||||
continue
|
||||
|
||||
for dump in borgmatic.hooks.data_source.dump.parse_data_source_dumps_metadata(
|
||||
@@ -308,7 +308,7 @@ def collect_dumps_from_archive(
|
||||
global_arguments.dry_run,
|
||||
repository,
|
||||
archive,
|
||||
[dumps_metadata_path],
|
||||
[dumps_metadata_entry['path']],
|
||||
config,
|
||||
local_borg_version,
|
||||
global_arguments,
|
||||
@@ -318,7 +318,7 @@ def collect_dumps_from_archive(
|
||||
)
|
||||
.stdout.read()
|
||||
.decode(),
|
||||
dumps_metadata_path,
|
||||
dumps_metadata_entry['path'],
|
||||
):
|
||||
dumps_from_archive[dump] = None
|
||||
|
||||
@@ -338,7 +338,7 @@ def collect_dumps_from_archive(
|
||||
# Probe for the data source dumps in multiple locations, as the default location has moved to
|
||||
# the borgmatic runtime directory (which gets stored as just "/borgmatic" with Borg 1.4+). But
|
||||
# we still want to support reading dumps from previously created archives as well.
|
||||
dump_paths = borgmatic.borg.list.capture_archive_listing(
|
||||
dump_entries = borgmatic.borg.list.capture_archive_listing(
|
||||
repository,
|
||||
archive,
|
||||
config,
|
||||
@@ -360,7 +360,9 @@ def collect_dumps_from_archive(
|
||||
remote_path=remote_path,
|
||||
)
|
||||
|
||||
for dump_path in dump_paths:
|
||||
for dump_entry in dump_entries:
|
||||
dump_path = dump_entry.get('path')
|
||||
|
||||
if not dump_path:
|
||||
continue
|
||||
|
||||
|
||||
@@ -135,11 +135,7 @@ def extract_archive(
|
||||
+ (('--remote-path', remote_path) if remote_path else ())
|
||||
+ numeric_ids_flags
|
||||
+ (('--umask', str(umask)) if umask else ())
|
||||
+ (
|
||||
('--log-json',)
|
||||
if (config.get('log_json') or (not config.get('progress') and not extract_to_stdout))
|
||||
else ()
|
||||
)
|
||||
+ (('--log-json',) if (config.get('log_json') or not config.get('progress')) else ())
|
||||
+ (('--lock-wait', str(lock_wait)) if lock_wait else ())
|
||||
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
||||
+ (('--debug', '--list', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
||||
|
||||
+29
-34
@@ -1,5 +1,6 @@
|
||||
import argparse
|
||||
import copy
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import shlex
|
||||
@@ -19,6 +20,7 @@ MAKE_FLAGS_EXCLUDES = (
|
||||
'paths',
|
||||
'find_paths',
|
||||
'format',
|
||||
'json',
|
||||
*ARCHIVE_FILTER_FLAGS_MOVED_TO_REPO_LIST,
|
||||
)
|
||||
|
||||
@@ -54,6 +56,7 @@ def make_list_command(
|
||||
+ flags.make_flags('remote-path', remote_path)
|
||||
+ flags.make_flags('umask', config.get('umask'))
|
||||
+ ('--log-json',)
|
||||
+ flags.make_flags('json-lines', list_arguments.json)
|
||||
+ flags.make_flags('lock-wait', config.get('lock_wait'))
|
||||
+ flags.make_flags('format', list_arguments.format or config.get('file_list_format'))
|
||||
+ flags.make_flags_from_arguments(list_arguments, excludes=MAKE_FLAGS_EXCLUDES)
|
||||
@@ -109,39 +112,36 @@ def capture_archive_listing(
|
||||
remote_path=None,
|
||||
):
|
||||
'''
|
||||
Given a local or remote repository path, an archive name, a configuration
|
||||
dict, the local Borg version, global arguments as an argparse.Namespace,
|
||||
the archive paths (or Borg patterns) in which to list files, the Borg path
|
||||
format to use for the output, and local and remote Borg paths, capture the
|
||||
output of listing that archive and return it as a list of file paths.
|
||||
Given a local or remote repository path, an archive name, a configuration dict, the local Borg
|
||||
version, global arguments as an argparse.Namespace, the archive paths (or Borg patterns) in
|
||||
which to list files, the Borg path format indicating keys to include in the output, and local
|
||||
and remote Borg paths, capture the output of listing that archive and return it as a sequence of
|
||||
dicts, one per path.
|
||||
'''
|
||||
return tuple(
|
||||
''.join(
|
||||
execute_command_and_capture_output(
|
||||
make_list_command(
|
||||
repository_path,
|
||||
config,
|
||||
local_borg_version,
|
||||
argparse.Namespace(
|
||||
repository=repository_path,
|
||||
archive=archive,
|
||||
paths=list(list_paths) if list_paths else None,
|
||||
find_paths=None,
|
||||
json=None,
|
||||
format=path_format or '{path}{NUL}',
|
||||
),
|
||||
global_arguments,
|
||||
local_path,
|
||||
remote_path,
|
||||
json.loads(entry)
|
||||
for entry in execute_command_and_capture_output(
|
||||
make_list_command(
|
||||
repository_path,
|
||||
config,
|
||||
local_borg_version,
|
||||
argparse.Namespace(
|
||||
repository=repository_path,
|
||||
archive=archive,
|
||||
paths=list(list_paths) if list_paths else None,
|
||||
find_paths=None,
|
||||
json=True,
|
||||
format=path_format or None,
|
||||
),
|
||||
environment=environment.make_environment(config),
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
borg_local_path=local_path,
|
||||
borg_exit_codes=config.get('borg_exit_codes'),
|
||||
)
|
||||
global_arguments,
|
||||
local_path,
|
||||
remote_path,
|
||||
),
|
||||
environment=environment.make_environment(config),
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
borg_local_path=local_path,
|
||||
borg_exit_codes=config.get('borg_exit_codes'),
|
||||
)
|
||||
.strip('\0')
|
||||
.split('\0'),
|
||||
)
|
||||
|
||||
|
||||
@@ -198,11 +198,6 @@ def list_archive(
|
||||
f"The --{name.replace('_', '-')} flag on the list action is ignored when using the --archive flag.",
|
||||
)
|
||||
|
||||
if list_arguments.json:
|
||||
raise ValueError(
|
||||
'The --json flag on the list action is not supported when using the --archive/--find flags.',
|
||||
)
|
||||
|
||||
borg_exit_codes = config.get('borg_exit_codes')
|
||||
|
||||
# If there are any paths to find (and there's not a single archive already selected), start by
|
||||
|
||||
@@ -37,6 +37,7 @@ def display_repository_info(
|
||||
if feature.available(feature.Feature.REPO_INFO, local_borg_version)
|
||||
else ('info',)
|
||||
)
|
||||
+ (('--critical',) if repo_info_arguments.json else ())
|
||||
+ (
|
||||
('--info',)
|
||||
if logger.getEffectiveLevel() == logging.INFO and not repo_info_arguments.json
|
||||
@@ -50,7 +51,8 @@ def display_repository_info(
|
||||
+ flags.make_flags('remote-path', remote_path)
|
||||
+ flags.make_flags('umask', config.get('umask'))
|
||||
+ flags.make_flags('lock-wait', lock_wait)
|
||||
+ (('--json',) if repo_info_arguments.json else ('--log-json',))
|
||||
+ ('--log-json',)
|
||||
+ (('--json',) if repo_info_arguments.json else ())
|
||||
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||
+ flags.make_repository_flags(repository_path, local_borg_version)
|
||||
)
|
||||
|
||||
@@ -77,6 +77,7 @@ def get_latest_archive(
|
||||
),
|
||||
*flags.make_flags('remote-path', remote_path),
|
||||
*flags.make_flags('umask', config.get('umask')),
|
||||
*('--log-json',),
|
||||
*flags.make_flags('lock-wait', config.get('lock_wait')),
|
||||
*(
|
||||
flags.make_flags('consider-checkpoints', consider_checkpoints)
|
||||
|
||||
@@ -16,6 +16,7 @@ def local_borg_version(config, local_path='borg'):
|
||||
'''
|
||||
full_command = (
|
||||
(local_path, '--version')
|
||||
+ ('--log-json',)
|
||||
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
||||
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
||||
)
|
||||
|
||||
+53
-27
@@ -6,6 +6,7 @@ import logging
|
||||
import select
|
||||
import subprocess
|
||||
import textwrap
|
||||
import time
|
||||
|
||||
import borgmatic.logger
|
||||
|
||||
@@ -101,28 +102,38 @@ def output_buffers_for_process(process, exclude_stdouts):
|
||||
)
|
||||
|
||||
|
||||
def borg_log_line_to_record(line, log_level):
|
||||
def borg_json_log_line_to_record(line, log_level):
|
||||
'''
|
||||
Given a single Borg log entry and a log level, return the line converted to a logging.LogRecord
|
||||
instance.
|
||||
|
||||
If it can't be parsed, then fall back to making a record out of the raw line and the given log
|
||||
level.
|
||||
Given a single Borg "--log-json"-style log line and a log level, return the line converted to a
|
||||
logging.LogRecord instance. Return None if the line can't be parsed as JSON.
|
||||
'''
|
||||
with contextlib.suppress(json.JSONDecodeError, TypeError, KeyError, AttributeError):
|
||||
log_data = json.loads(line)
|
||||
log_type = log_data.get('type')
|
||||
|
||||
return logging.makeLogRecord(
|
||||
dict(
|
||||
levelno=logging._nameToLevel.get(log_data.get('levelname')),
|
||||
created=log_data.get('time'),
|
||||
msg=log_data.get('message'),
|
||||
levelname=log_data.get('levelname'),
|
||||
name=log_data.get('name'),
|
||||
if log_type == 'log_message':
|
||||
return logging.makeLogRecord(
|
||||
dict(
|
||||
levelno=logging._nameToLevel.get(log_data.get('levelname')),
|
||||
created=log_data.get('time'),
|
||||
msg=log_data.get('message'),
|
||||
levelname=log_data.get('levelname'),
|
||||
name=log_data.get('name'),
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
return log_line_to_record(line, log_level)
|
||||
if log_type == 'file_status':
|
||||
return logging.makeLogRecord(
|
||||
dict(
|
||||
levelno=log_level,
|
||||
created=time.time(),
|
||||
msg=f'{log_data.get("status")} {log_data.get("path")}',
|
||||
levelname=logging.getLevelName(log_level),
|
||||
name='borg.file_status',
|
||||
)
|
||||
)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def log_line_to_record(line, log_level):
|
||||
@@ -139,11 +150,11 @@ def log_line_to_record(line, log_level):
|
||||
)
|
||||
|
||||
|
||||
def parse_log_line(line, log_level, came_from_stderr, borg_local_path, command):
|
||||
def parse_log_line(line, log_level, elevate_stderr, borg_local_path, command):
|
||||
'''
|
||||
Given a raw output line from an external program, whether this line came from stderr, the Borg
|
||||
local path, and the command as a sequence, return a logging.LogRecord instance containing its
|
||||
parsed data.
|
||||
Given a raw output line from an external program, whether this line came from stderr and should
|
||||
be elevated to error/warning, the Borg local path, and the command as a sequence, return a
|
||||
logging.LogRecord instance containing its parsed data.
|
||||
|
||||
If the command being run is Borg, and the log line is JSON-formatted log data, then grab the log
|
||||
level from it and log the parsed JSON to be consumed later by a Python logging.Formatter.
|
||||
@@ -153,9 +164,12 @@ def parse_log_line(line, log_level, came_from_stderr, borg_local_path, command):
|
||||
just elevate the log level to a WARN.
|
||||
'''
|
||||
if borg_local_path and command[0] == borg_local_path:
|
||||
return borg_log_line_to_record(line, log_level)
|
||||
log_record = borg_json_log_line_to_record(line, log_level)
|
||||
|
||||
if came_from_stderr:
|
||||
if log_record:
|
||||
return log_record
|
||||
|
||||
if elevate_stderr:
|
||||
return log_line_to_record(
|
||||
line, logging.WARNING if line.lower().startswith('warning:') else logging.ERROR
|
||||
)
|
||||
@@ -182,7 +196,14 @@ def handle_log_record(log_record, last_lines):
|
||||
return log_record
|
||||
|
||||
|
||||
def log_outputs(processes, exclude_stdouts, output_log_level, borg_local_path, borg_exit_codes): # noqa: PLR0912
|
||||
def log_outputs( # noqa: PLR0912
|
||||
processes,
|
||||
exclude_stdouts,
|
||||
output_log_level,
|
||||
borg_local_path,
|
||||
borg_exit_codes,
|
||||
capture_stderr=False,
|
||||
):
|
||||
'''
|
||||
Given a sequence of subprocess.Popen() instances for multiple processes, log the outputs (stderr
|
||||
and stdout). Use the requested output log level for stdout, but always log stderr to the ERROR
|
||||
@@ -190,8 +211,8 @@ def log_outputs(processes, exclude_stdouts, output_log_level, borg_local_path, b
|
||||
warning for exit code 1, if that process does not match the Borg local path).
|
||||
|
||||
If the output log level is None, then instead of logging, capture the output for the last
|
||||
process given and yield it one line at a time. But if the output log level is not None, don't
|
||||
yield anything.
|
||||
process given and yield it one line at a time. This includes stderr if capture stderr is set.
|
||||
But if the output log level is not None, don't yield anything.
|
||||
|
||||
This yielding means that this function is a generator, and must be consumed in order to execute.
|
||||
|
||||
@@ -250,7 +271,9 @@ def log_outputs(processes, exclude_stdouts, output_log_level, borg_local_path, b
|
||||
parse_log_line(
|
||||
line=line,
|
||||
log_level=output_log_level,
|
||||
came_from_stderr=(ready_buffer == ready_process.stderr),
|
||||
elevate_stderr=(
|
||||
ready_buffer == ready_process.stderr and not capture_stderr
|
||||
),
|
||||
borg_local_path=borg_local_path,
|
||||
command=command,
|
||||
),
|
||||
@@ -292,7 +315,9 @@ def log_outputs(processes, exclude_stdouts, output_log_level, borg_local_path, b
|
||||
parse_log_line(
|
||||
line=line,
|
||||
log_level=output_log_level,
|
||||
came_from_stderr=(output_buffer == process.stderr),
|
||||
elevate_stderr=(
|
||||
output_buffer == process.stderr and not capture_stderr
|
||||
),
|
||||
borg_local_path=borg_local_path,
|
||||
command=command,
|
||||
),
|
||||
@@ -471,7 +496,7 @@ def execute_command_and_capture_output(
|
||||
command,
|
||||
stdin=input_file,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE if capture_stderr else None,
|
||||
stderr=subprocess.PIPE,
|
||||
shell=shell,
|
||||
env=environment,
|
||||
cwd=working_directory,
|
||||
@@ -496,6 +521,7 @@ def execute_command_and_capture_output(
|
||||
None,
|
||||
borg_local_path,
|
||||
borg_exit_codes,
|
||||
capture_stderr=capture_stderr,
|
||||
)
|
||||
|
||||
yield from captured_lines
|
||||
|
||||
@@ -524,5 +524,6 @@ def restore_data_source_dump(
|
||||
input_file=extract_process.stdout,
|
||||
environment=environment,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
borg_local_path=config.get('local_path', 'borg'),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -296,6 +296,7 @@ def restore_data_source_dump(
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout if extract_process else None,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
borg_local_path=config.get('local_path', 'borg'),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
@@ -461,5 +461,6 @@ def restore_data_source_dump(
|
||||
input_file=extract_process.stdout,
|
||||
environment=environment,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
borg_local_path=config.get('local_path', 'borg'),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -442,6 +442,7 @@ def restore_data_source_dump(
|
||||
input_file=extract_process.stdout if extract_process else None,
|
||||
environment=environment,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
borg_local_path=config.get('local_path', 'borg'),
|
||||
)
|
||||
)
|
||||
execute_command(
|
||||
|
||||
@@ -215,6 +215,7 @@ def restore_data_source_dump(
|
||||
for part in shlex.split(data_source.get('sqlite_restore_command') or 'sqlite3')
|
||||
)
|
||||
restore_command = (*sqlite_restore_command, '-bail', shlex.quote(database_path))
|
||||
|
||||
# Don't give Borg local path so as to error on warnings, as "borg extract" only gives a warning
|
||||
# if the restore paths don't exist in the archive.
|
||||
tuple(
|
||||
@@ -224,5 +225,6 @@ def restore_data_source_dump(
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
borg_local_path=config.get('local_path', 'borg'),
|
||||
)
|
||||
)
|
||||
|
||||
@@ -850,13 +850,11 @@ def test_collect_spot_check_archive_paths_excludes_directories_and_pipes():
|
||||
flexmock(module.borgmatic.config.paths).should_receive(
|
||||
'get_borgmatic_source_directory',
|
||||
).and_return('/home/user/.borgmatic')
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
(
|
||||
'f etc/path',
|
||||
'p var/pipe',
|
||||
'f etc/other',
|
||||
'd etc/dir',
|
||||
),
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_yield(
|
||||
{'type': 'f', 'path': 'etc/path'},
|
||||
{'type': 'p', 'path': 'var/pipe'},
|
||||
{'type': 'f', 'path': 'etc/other'},
|
||||
{'type': 'd', 'path': 'etc/dir'},
|
||||
)
|
||||
|
||||
assert module.collect_spot_check_archive_paths(
|
||||
@@ -875,11 +873,9 @@ def test_collect_spot_check_archive_paths_excludes_file_in_borgmatic_runtime_dir
|
||||
flexmock(module.borgmatic.config.paths).should_receive(
|
||||
'get_borgmatic_source_directory',
|
||||
).and_return('/root/.borgmatic')
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
(
|
||||
'f etc/path',
|
||||
'f borgmatic/some/thing',
|
||||
),
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_yield(
|
||||
{'type': 'f', 'path': 'etc/path'},
|
||||
{'type': 'f', 'path': 'borgmatic/some/thing'},
|
||||
)
|
||||
|
||||
assert module.collect_spot_check_archive_paths(
|
||||
@@ -898,11 +894,9 @@ def test_collect_spot_check_archive_paths_excludes_file_in_borgmatic_source_dire
|
||||
flexmock(module.borgmatic.config.paths).should_receive(
|
||||
'get_borgmatic_source_directory',
|
||||
).and_return('/root/.borgmatic')
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
(
|
||||
'f etc/path',
|
||||
'f root/.borgmatic/some/thing',
|
||||
),
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_yield(
|
||||
{'type': 'f', 'path': 'etc/path'},
|
||||
{'type': 'f', 'path': 'root/.borgmatic/some/thing'},
|
||||
)
|
||||
|
||||
assert module.collect_spot_check_archive_paths(
|
||||
@@ -921,11 +915,9 @@ def test_collect_spot_check_archive_paths_excludes_file_in_borgmatic_runtime_dir
|
||||
flexmock(module.borgmatic.config.paths).should_receive(
|
||||
'get_borgmatic_source_directory',
|
||||
).and_return('/root.borgmatic')
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
(
|
||||
'f etc/path',
|
||||
'f run/user/0/borgmatic/some/thing',
|
||||
),
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_yield(
|
||||
{'type': 'f', 'path': 'etc/path'},
|
||||
{'type': 'f', 'path': 'run/user/0/borgmatic/some/thing'},
|
||||
)
|
||||
|
||||
assert module.collect_spot_check_archive_paths(
|
||||
@@ -1009,8 +1001,95 @@ def test_compare_spot_check_hashes_returns_paths_having_failing_hashes():
|
||||
'hash1 /foo',
|
||||
'hash2 /bar',
|
||||
)
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
['hash1 foo', 'nothash2 bar'],
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_yield(
|
||||
{'xxh64': 'hash1', 'path': 'foo'},
|
||||
{'xxh64': 'nothash2', 'path': 'bar'},
|
||||
)
|
||||
|
||||
assert module.compare_spot_check_hashes(
|
||||
repository={'path': 'repo'},
|
||||
archive='archive',
|
||||
config={
|
||||
'checks': [
|
||||
{
|
||||
'name': 'archives',
|
||||
'frequency': '2 weeks',
|
||||
},
|
||||
{
|
||||
'name': 'spot',
|
||||
'data_sample_percentage': 50,
|
||||
},
|
||||
],
|
||||
},
|
||||
local_borg_version=flexmock(),
|
||||
global_arguments=flexmock(),
|
||||
local_path=flexmock(),
|
||||
remote_path=flexmock(),
|
||||
source_paths=('/foo', '/bar', '/baz', '/quux'),
|
||||
) == ('/bar',)
|
||||
|
||||
|
||||
def test_compare_spot_check_hashes_handles_weird_backslashed_hashes_from_xxh64sum():
|
||||
flexmock(module.random).should_receive('SystemRandom').and_return(
|
||||
flexmock(sample=lambda population, count: population[:count]),
|
||||
)
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
|
||||
None,
|
||||
)
|
||||
flexmock(module.os.path).should_receive('exists').and_return(True)
|
||||
flexmock(module.os.path).should_receive('islink').and_return(False)
|
||||
flexmock(module.borgmatic.execute).should_receive(
|
||||
'execute_command_and_capture_output',
|
||||
).with_args(('xxh64sum', '/foo', '/bar'), working_directory=None).and_yield(
|
||||
'\\hash1 /foo',
|
||||
'\\hash2 /bar',
|
||||
)
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_yield(
|
||||
{'xxh64': 'hash1', 'path': 'foo'},
|
||||
{'xxh64': 'nothash2', 'path': 'bar'},
|
||||
)
|
||||
|
||||
assert module.compare_spot_check_hashes(
|
||||
repository={'path': 'repo'},
|
||||
archive='archive',
|
||||
config={
|
||||
'checks': [
|
||||
{
|
||||
'name': 'archives',
|
||||
'frequency': '2 weeks',
|
||||
},
|
||||
{
|
||||
'name': 'spot',
|
||||
'data_sample_percentage': 50,
|
||||
},
|
||||
],
|
||||
},
|
||||
local_borg_version=flexmock(),
|
||||
global_arguments=flexmock(),
|
||||
local_path=flexmock(),
|
||||
remote_path=flexmock(),
|
||||
source_paths=('/foo', '/bar', '/baz', '/quux'),
|
||||
) == ('/bar',)
|
||||
|
||||
|
||||
def test_compare_spot_check_hashes_handles_incorrect_path_names_from_xxh64sum():
|
||||
flexmock(module.random).should_receive('SystemRandom').and_return(
|
||||
flexmock(sample=lambda population, count: population[:count]),
|
||||
)
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
|
||||
None,
|
||||
)
|
||||
flexmock(module.os.path).should_receive('exists').and_return(True)
|
||||
flexmock(module.os.path).should_receive('islink').and_return(False)
|
||||
flexmock(module.borgmatic.execute).should_receive(
|
||||
'execute_command_and_capture_output',
|
||||
).with_args(('xxh64sum', '/foo', '/bar'), working_directory=None).and_yield(
|
||||
'hash1 /foo/wrong/path',
|
||||
'hash2 /bar/wrong/path',
|
||||
)
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_yield(
|
||||
{'xxh64': 'hash1', 'path': 'foo'},
|
||||
{'xxh64': 'nothash2', 'path': 'bar'},
|
||||
)
|
||||
|
||||
assert module.compare_spot_check_hashes(
|
||||
@@ -1051,8 +1130,9 @@ def test_compare_spot_check_hashes_returns_relative_paths_having_failing_hashes(
|
||||
'hash1 foo',
|
||||
'hash2 bar',
|
||||
)
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
['hash1 foo', 'nothash2 bar'],
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_yield(
|
||||
{'xxh64': 'hash1', 'path': 'foo'},
|
||||
{'xxh64': 'nothash2', 'path': 'bar'},
|
||||
)
|
||||
|
||||
assert module.compare_spot_check_hashes(
|
||||
@@ -1093,8 +1173,9 @@ def test_compare_spot_check_hashes_handles_data_sample_percentage_above_100():
|
||||
'hash1 /foo',
|
||||
'hash2 /bar',
|
||||
)
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
['nothash1 foo', 'nothash2 bar'],
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_yield(
|
||||
{'xxh64': 'nothash1', 'path': 'foo'},
|
||||
{'xxh64': 'nothash2', 'path': 'bar'},
|
||||
)
|
||||
|
||||
assert module.compare_spot_check_hashes(
|
||||
@@ -1135,8 +1216,9 @@ def test_compare_spot_check_hashes_uses_xxh64sum_command_option():
|
||||
('/usr/local/bin/xxhsum', '-H64', '/foo', '/bar'),
|
||||
working_directory=None,
|
||||
).and_yield('hash1 /foo', 'hash2 /bar')
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
['hash1 foo', 'nothash2 bar'],
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_yield(
|
||||
{'xxh64': 'hash1', 'path': 'foo'},
|
||||
{'xxh64': 'nothash2', 'path': 'bar'},
|
||||
)
|
||||
|
||||
assert module.compare_spot_check_hashes(
|
||||
@@ -1174,8 +1256,8 @@ def test_compare_spot_check_hashes_considers_path_missing_from_archive_as_not_ma
|
||||
'hash1 /foo',
|
||||
'hash2 /bar',
|
||||
)
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
['hash1 foo'],
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_yield(
|
||||
{'xxh64': 'hash1', 'path': 'foo'}
|
||||
)
|
||||
|
||||
assert module.compare_spot_check_hashes(
|
||||
@@ -1210,8 +1292,9 @@ def test_compare_spot_check_hashes_considers_symlink_path_as_not_matching():
|
||||
flexmock(module.borgmatic.execute).should_receive(
|
||||
'execute_command_and_capture_output',
|
||||
).with_args(('xxh64sum', '/foo'), working_directory=None).and_yield('hash1 /foo')
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
['hash1 foo', 'hash2 bar'],
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_yield(
|
||||
{'xxh64': 'hash1', 'path': 'foo'},
|
||||
{'xxh64': 'hash2', 'path': 'bar'},
|
||||
)
|
||||
|
||||
assert module.compare_spot_check_hashes(
|
||||
@@ -1246,8 +1329,9 @@ def test_compare_spot_check_hashes_considers_non_existent_path_as_not_matching()
|
||||
flexmock(module.borgmatic.execute).should_receive(
|
||||
'execute_command_and_capture_output',
|
||||
).with_args(('xxh64sum', '/foo'), working_directory=None).and_yield('hash1 /foo')
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
['hash1 foo', 'hash2 bar'],
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_yield(
|
||||
{'xxh64': 'hash1', 'path': 'foo'},
|
||||
{'xxh64': 'hash2', 'path': 'bar'},
|
||||
)
|
||||
|
||||
assert module.compare_spot_check_hashes(
|
||||
@@ -1291,9 +1375,13 @@ def test_compare_spot_check_hashes_with_too_many_paths_feeds_them_to_commands_in
|
||||
'hash3 /baz',
|
||||
'hash4 /quux',
|
||||
)
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
['hash1 foo', 'hash2 bar'],
|
||||
).and_return(['hash3 baz', 'nothash4 quux'])
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_yield(
|
||||
{'xxh64': 'hash1', 'path': 'foo'},
|
||||
{'xxh64': 'hash2', 'path': 'bar'},
|
||||
).and_yield(
|
||||
{'xxh64': 'hash3', 'path': 'baz'},
|
||||
{'xxh64': 'nothash4', 'path': 'quux'},
|
||||
)
|
||||
|
||||
assert module.compare_spot_check_hashes(
|
||||
repository={'path': 'repo'},
|
||||
@@ -1334,8 +1422,9 @@ def test_compare_spot_check_hashes_uses_working_directory_to_access_source_paths
|
||||
'hash1 foo',
|
||||
'hash2 bar',
|
||||
)
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
['hash1 foo', 'nothash2 bar'],
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_yield(
|
||||
{'xxh64': 'hash1', 'path': 'foo'},
|
||||
{'xxh64': 'nothash2', 'path': 'bar'},
|
||||
)
|
||||
|
||||
assert module.compare_spot_check_hashes(
|
||||
|
||||
@@ -513,10 +513,10 @@ def test_collect_dumps_from_archive_with_dumps_metadata_parses_it():
|
||||
'make_runtime_directory_glob'
|
||||
).and_return('')
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
[
|
||||
'borgmatic/postgresql_databases/dumps.json',
|
||||
'borgmatic/mysql_databases/dumps.json',
|
||||
],
|
||||
(
|
||||
{'path': 'borgmatic/postgresql_databases/dumps.json'},
|
||||
{'path': 'borgmatic/mysql_databases/dumps.json'},
|
||||
),
|
||||
)
|
||||
flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').and_return(
|
||||
flexmock(stdout=flexmock(read=lambda: b''))
|
||||
@@ -550,13 +550,13 @@ def test_collect_dumps_from_archive_with_empty_dumps_metadata_path_falls_back_to
|
||||
'make_runtime_directory_glob'
|
||||
).and_return('')
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
['']
|
||||
({'path': ''},)
|
||||
).and_return(
|
||||
[
|
||||
'borgmatic/postgresql_databases/localhost/foo',
|
||||
'borgmatic/postgresql_databases/host:1234/bar',
|
||||
'borgmatic/mysql_databases/localhost/quux',
|
||||
],
|
||||
(
|
||||
{'path': 'borgmatic/postgresql_databases/localhost/foo'},
|
||||
{'path': 'borgmatic/postgresql_databases/host:1234/bar'},
|
||||
{'path': 'borgmatic/mysql_databases/localhost/quux'},
|
||||
),
|
||||
)
|
||||
flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').never()
|
||||
flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
|
||||
@@ -592,13 +592,13 @@ def test_collect_dumps_from_archive_without_dumps_metadata_falls_back_to_parsing
|
||||
'make_runtime_directory_glob'
|
||||
).and_return('')
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
[]
|
||||
()
|
||||
).and_return(
|
||||
[
|
||||
'borgmatic/postgresql_databases/localhost/foo',
|
||||
'borgmatic/postgresql_databases/host:1234/bar',
|
||||
'borgmatic/mysql_databases/localhost/quux',
|
||||
],
|
||||
(
|
||||
{'path': 'borgmatic/postgresql_databases/localhost/foo'},
|
||||
{'path': 'borgmatic/postgresql_databases/host:1234/bar'},
|
||||
{'path': 'borgmatic/mysql_databases/localhost/quux'},
|
||||
),
|
||||
)
|
||||
flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').never()
|
||||
flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
|
||||
@@ -634,14 +634,14 @@ def test_collect_dumps_from_archive_parses_archive_paths_with_different_base_dir
|
||||
'make_runtime_directory_glob'
|
||||
).and_return('')
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
[]
|
||||
()
|
||||
).and_return(
|
||||
[
|
||||
'borgmatic/postgresql_databases/localhost/foo',
|
||||
'.borgmatic/postgresql_databases/localhost/bar',
|
||||
'/root/.borgmatic/postgresql_databases/localhost/baz',
|
||||
'/var/run/0/borgmatic/mysql_databases/localhost/quux',
|
||||
],
|
||||
(
|
||||
{'path': 'borgmatic/postgresql_databases/localhost/foo'},
|
||||
{'path': '.borgmatic/postgresql_databases/localhost/bar'},
|
||||
{'path': '/root/.borgmatic/postgresql_databases/localhost/baz'},
|
||||
{'path': '/var/run/0/borgmatic/mysql_databases/localhost/quux'},
|
||||
),
|
||||
)
|
||||
flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').never()
|
||||
flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
|
||||
@@ -678,12 +678,12 @@ def test_collect_dumps_from_archive_parses_directory_format_archive_paths():
|
||||
'make_runtime_directory_glob'
|
||||
).and_return('')
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
[]
|
||||
()
|
||||
).and_return(
|
||||
[
|
||||
'borgmatic/postgresql_databases/localhost/foo/table1',
|
||||
'borgmatic/postgresql_databases/localhost/foo/table2',
|
||||
],
|
||||
(
|
||||
{'path': 'borgmatic/postgresql_databases/localhost/foo/table1'},
|
||||
{'path': 'borgmatic/postgresql_databases/localhost/foo/table2'},
|
||||
),
|
||||
)
|
||||
flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').never()
|
||||
flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
|
||||
@@ -715,15 +715,15 @@ def test_collect_dumps_from_archive_skips_bad_archive_paths_or_bad_path_componen
|
||||
'make_runtime_directory_glob'
|
||||
).and_return('')
|
||||
flexmock(module.borgmatic.borg.list).should_receive('capture_archive_listing').and_return(
|
||||
[]
|
||||
()
|
||||
).and_return(
|
||||
[
|
||||
'borgmatic/postgresql_databases/localhost/foo',
|
||||
'borgmatic/postgresql_databases/localhost:abcd/bar',
|
||||
'borgmatic/invalid',
|
||||
'invalid/as/well',
|
||||
'',
|
||||
],
|
||||
(
|
||||
{'path': 'borgmatic/postgresql_databases/localhost/foo'},
|
||||
{'path': 'borgmatic/postgresql_databases/localhost:abcd/bar'},
|
||||
{'path': 'borgmatic/invalid'},
|
||||
{'path': 'invalid/as/well'},
|
||||
{'path': ''},
|
||||
)
|
||||
)
|
||||
flexmock(module.borgmatic.borg.extract).should_receive('extract_archive').never()
|
||||
flexmock(module.borgmatic.hooks.data_source.dump).should_receive(
|
||||
|
||||
@@ -715,7 +715,7 @@ def test_extract_archive_calls_borg_with_extract_to_stdout_returns_process():
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
('borg', 'extract', '--stdout', 'repo::archive'),
|
||||
('borg', 'extract', '--log-json', '--stdout', 'repo::archive'),
|
||||
output_file=module.subprocess.PIPE,
|
||||
run_to_completion=False,
|
||||
environment=None,
|
||||
|
||||
@@ -26,10 +26,10 @@ def test_make_list_command_includes_log_info():
|
||||
assert command == ('borg', 'list', '--info', '--log-json', 'repo')
|
||||
|
||||
|
||||
def test_make_list_command_includes_json_but_not_info():
|
||||
def test_make_list_command_includes_json_lines_but_not_info():
|
||||
insert_logging_mock(logging.INFO)
|
||||
flexmock(module.flags).should_receive('make_flags').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-lines',))
|
||||
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
|
||||
|
||||
command = module.make_list_command(
|
||||
@@ -40,7 +40,7 @@ def test_make_list_command_includes_json_but_not_info():
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
assert command == ('borg', 'list', '--log-json', '--json', 'repo')
|
||||
assert command == ('borg', 'list', '--log-json', '--json-lines', 'repo')
|
||||
|
||||
|
||||
def test_make_list_command_includes_log_debug():
|
||||
@@ -332,7 +332,7 @@ def test_make_find_paths_adds_globs_to_path_fragments():
|
||||
def test_capture_archive_listing_does_not_raise():
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').and_yield('')
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').and_yield('{}', '{}')
|
||||
flexmock(module).should_receive('make_list_command')
|
||||
|
||||
module.capture_archive_listing(
|
||||
@@ -393,24 +393,6 @@ def test_list_archive_calls_borg_with_flags():
|
||||
)
|
||||
|
||||
|
||||
def test_list_archive_with_archive_and_json_errors():
|
||||
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
|
||||
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
|
||||
flexmock(module.logger).answer = lambda message: None
|
||||
list_arguments = argparse.Namespace(archive='archive', paths=None, json=True, find_paths=None)
|
||||
|
||||
flexmock(module.feature).should_receive('available').and_return(False)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
module.list_archive(
|
||||
repository_path='repo',
|
||||
config={},
|
||||
local_borg_version='1.2.3',
|
||||
list_arguments=list_arguments,
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
|
||||
def test_list_archive_calls_borg_with_local_path():
|
||||
flexmock(module.borgmatic.logger).should_receive('add_custom_log_levels')
|
||||
flexmock(module.logging).ANSWER = module.borgmatic.logger.ANSWER
|
||||
|
||||
@@ -141,7 +141,7 @@ def test_display_repository_info_with_log_info_and_json_suppresses_most_borg_out
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
('borg', 'repo-info', '--json', '--repo', 'repo'),
|
||||
('borg', 'repo-info', '--critical', '--log-json', '--json', '--repo', 'repo'),
|
||||
environment=None,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
@@ -219,7 +219,7 @@ def test_display_repository_info_with_log_debug_and_json_suppresses_most_borg_ou
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
('borg', 'repo-info', '--json', '--repo', 'repo'),
|
||||
('borg', 'repo-info', '--critical', '--log-json', '--json', '--repo', 'repo'),
|
||||
environment=None,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
@@ -255,7 +255,7 @@ def test_display_repository_info_with_json_calls_borg_with_json_flag():
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
('borg', 'repo-info', '--json', '--repo', 'repo'),
|
||||
('borg', 'repo-info', '--critical', '--log-json', '--json', '--repo', 'repo'),
|
||||
environment=None,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
@@ -542,6 +542,7 @@ def test_display_repository_info_with_feature_available_calls_borg_with_repo_inf
|
||||
'--log-json',
|
||||
'--extra',
|
||||
'value with space',
|
||||
'--log-json',
|
||||
'--json',
|
||||
'--repo',
|
||||
'repo',
|
||||
|
||||
@@ -119,7 +119,7 @@ def test_get_latest_archive_calls_borg_with_flags():
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
('borg', 'list', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
('borg', 'list', '--log-json', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
borg_local_path='borg',
|
||||
borg_exit_codes=None,
|
||||
environment=None,
|
||||
@@ -148,7 +148,7 @@ def test_get_latest_archive_with_log_info_calls_borg_without_info_flag():
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
('borg', 'list', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
('borg', 'list', '--log-json', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
environment=None,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
@@ -178,7 +178,7 @@ def test_get_latest_archive_with_log_debug_calls_borg_without_debug_flag():
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
('borg', 'list', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
('borg', 'list', '--log-json', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
environment=None,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
@@ -208,7 +208,7 @@ def test_get_latest_archive_with_local_path_calls_borg_via_local_path():
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
('borg1', 'list', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
('borg1', 'list', '--log-json', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
environment=None,
|
||||
working_directory=None,
|
||||
borg_local_path='borg1',
|
||||
@@ -239,7 +239,7 @@ def test_get_latest_archive_with_exit_codes_calls_borg_using_them():
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
borg_exit_codes = flexmock()
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
('borg', 'list', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
('borg', 'list', '--log-json', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
environment=None,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
@@ -271,7 +271,7 @@ def test_get_latest_archive_with_remote_path_calls_borg_with_remote_path_flags()
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
('borg', 'list', '--remote-path', 'borg1', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
('borg', 'list', '--remote-path', 'borg1', '--log-json', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
environment=None,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
@@ -304,7 +304,7 @@ def test_get_latest_archive_with_umask_calls_borg_with_umask_flags():
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
('borg', 'list', '--umask', '077', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
('borg', 'list', '--umask', '077', '--log-json', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
environment=None,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
@@ -332,7 +332,7 @@ def test_get_latest_archive_without_archives_raises():
|
||||
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
('borg', 'list', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
('borg', 'list', '--log-json', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
environment=None,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
@@ -362,7 +362,7 @@ def test_get_latest_archive_with_lock_wait_calls_borg_with_lock_wait_flags():
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
('borg', 'list', '--lock-wait', 'okay', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
('borg', 'list', '--log-json', '--lock-wait', 'okay', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
environment=None,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
@@ -394,6 +394,7 @@ def test_get_latest_archive_calls_borg_with_list_extra_borg_options():
|
||||
(
|
||||
'borg',
|
||||
'list',
|
||||
'--log-json',
|
||||
*BORG_LIST_LATEST_ARGUMENTS[:-1],
|
||||
'--extra',
|
||||
'value with space',
|
||||
@@ -430,6 +431,7 @@ def test_get_latest_archive_with_feature_available_calls_borg_with_repo_list_ext
|
||||
(
|
||||
'borg',
|
||||
'repo-list',
|
||||
'--log-json',
|
||||
*BORG_LIST_LATEST_ARGUMENTS[:-1],
|
||||
'--extra',
|
||||
'value with space',
|
||||
@@ -466,7 +468,7 @@ def test_get_latest_archive_with_consider_checkpoints_calls_borg_with_consider_c
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
('borg', 'list', '--consider-checkpoints', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
('borg', 'list', '--log-json', '--consider-checkpoints', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
environment=None,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
@@ -499,7 +501,7 @@ def test_get_latest_archive_with_consider_checkpoints_and_feature_available_call
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
('borg', 'repo-list', *BORG_REPO_LIST_LATEST_ARGUMENTS),
|
||||
('borg', 'repo-list', '--log-json', *BORG_REPO_LIST_LATEST_ARGUMENTS),
|
||||
environment=None,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
@@ -531,7 +533,7 @@ def test_get_latest_archive_calls_borg_with_working_directory():
|
||||
'/working/dir',
|
||||
)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
('borg', 'list', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
('borg', 'list', '--log-json', *BORG_LIST_LATEST_ARGUMENTS),
|
||||
borg_local_path='borg',
|
||||
borg_exit_codes=None,
|
||||
environment=None,
|
||||
|
||||
@@ -31,14 +31,14 @@ def insert_execute_command_and_capture_output_mock(
|
||||
|
||||
|
||||
def test_local_borg_version_calls_borg_with_required_parameters():
|
||||
insert_execute_command_and_capture_output_mock(('borg', '--version'))
|
||||
insert_execute_command_and_capture_output_mock(('borg', '--version', '--log-json'))
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
|
||||
assert module.local_borg_version({}) == VERSION
|
||||
|
||||
|
||||
def test_local_borg_version_with_log_info_calls_borg_with_info_parameter():
|
||||
insert_execute_command_and_capture_output_mock(('borg', '--version', '--info'))
|
||||
insert_execute_command_and_capture_output_mock(('borg', '--version', '--log-json', '--info'))
|
||||
insert_logging_mock(logging.INFO)
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
|
||||
@@ -46,7 +46,9 @@ def test_local_borg_version_with_log_info_calls_borg_with_info_parameter():
|
||||
|
||||
|
||||
def test_local_borg_version_with_log_debug_calls_borg_with_debug_parameters():
|
||||
insert_execute_command_and_capture_output_mock(('borg', '--version', '--debug', '--show-rc'))
|
||||
insert_execute_command_and_capture_output_mock(
|
||||
('borg', '--version', '--log-json', '--debug', '--show-rc')
|
||||
)
|
||||
insert_logging_mock(logging.DEBUG)
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
|
||||
@@ -54,7 +56,9 @@ def test_local_borg_version_with_log_debug_calls_borg_with_debug_parameters():
|
||||
|
||||
|
||||
def test_local_borg_version_with_local_borg_path_calls_borg_with_it():
|
||||
insert_execute_command_and_capture_output_mock(('borg1', '--version'), borg_local_path='borg1')
|
||||
insert_execute_command_and_capture_output_mock(
|
||||
('borg1', '--version', '--log-json'), borg_local_path='borg1'
|
||||
)
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
|
||||
assert module.local_borg_version({}, 'borg1') == VERSION
|
||||
@@ -63,7 +67,7 @@ def test_local_borg_version_with_local_borg_path_calls_borg_with_it():
|
||||
def test_local_borg_version_with_borg_exit_codes_calls_using_with_them():
|
||||
borg_exit_codes = flexmock()
|
||||
insert_execute_command_and_capture_output_mock(
|
||||
('borg', '--version'),
|
||||
('borg', '--version', '--log-json'),
|
||||
borg_exit_codes=borg_exit_codes,
|
||||
)
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
@@ -72,7 +76,9 @@ def test_local_borg_version_with_borg_exit_codes_calls_using_with_them():
|
||||
|
||||
|
||||
def test_local_borg_version_with_invalid_version_raises():
|
||||
insert_execute_command_and_capture_output_mock(('borg', '--version'), version_output='wtf')
|
||||
insert_execute_command_and_capture_output_mock(
|
||||
('borg', '--version', '--log-json'), version_output='wtf'
|
||||
)
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
@@ -81,7 +87,7 @@ def test_local_borg_version_with_invalid_version_raises():
|
||||
|
||||
def test_local_borg_version_calls_borg_with_working_directory():
|
||||
insert_execute_command_and_capture_output_mock(
|
||||
('borg', '--version'),
|
||||
('borg', '--version', '--log-json'),
|
||||
working_directory='/working/dir',
|
||||
)
|
||||
flexmock(module.environment).should_receive('make_environment')
|
||||
|
||||
@@ -1353,6 +1353,7 @@ def test_restore_data_source_dump_runs_mariadb_to_restore():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1394,6 +1395,7 @@ def test_restore_data_source_dump_runs_mariadb_with_options():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1437,6 +1439,7 @@ def test_restore_data_source_dump_runs_non_default_mariadb_with_options():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1489,6 +1492,7 @@ def test_restore_data_source_dump_runs_mariadb_with_hostname_and_port():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1537,6 +1541,7 @@ def test_restore_data_source_dump_runs_mariadb_with_socket_path():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1584,6 +1589,7 @@ def test_restore_data_source_dump_runs_mariadb_with_tls():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1631,6 +1637,7 @@ def test_restore_data_source_dump_runs_mariadb_without_tls():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1674,6 +1681,7 @@ def test_restore_data_source_dump_runs_mariadb_with_username_and_password():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1722,6 +1730,7 @@ def test_restore_data_source_with_environment_password_transport_skips_defaults_
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root', 'MYSQL_PWD': 'trustsome1'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1787,6 +1796,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1857,6 +1867,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
|
||||
@@ -394,6 +394,7 @@ def test_restore_data_source_dump_runs_mongorestore():
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -438,6 +439,7 @@ def test_restore_data_source_dump_runs_mongorestore_with_hostname_and_port():
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -493,6 +495,7 @@ def test_restore_data_source_dump_runs_mongorestore_with_username_and_password()
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -556,6 +559,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -619,6 +623,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -653,6 +658,7 @@ def test_restore_data_source_dump_runs_mongorestore_with_options():
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -695,6 +701,7 @@ def test_restore_databases_dump_runs_mongorestore_with_schemas():
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -729,6 +736,7 @@ def test_restore_data_source_dump_runs_psql_for_all_database_dump():
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -788,6 +796,7 @@ def test_restore_data_source_dump_without_extract_process_restores_from_disk():
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=None,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -901,6 +910,7 @@ def test_restore_data_source_dump_uses_custom_mongorestore_command():
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
|
||||
@@ -1235,6 +1235,7 @@ def test_restore_data_source_dump_runs_mysql_to_restore():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1276,6 +1277,7 @@ def test_restore_data_source_dump_runs_mysql_with_options():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1317,6 +1319,7 @@ def test_restore_data_source_dump_runs_non_default_mysql_with_options():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1369,6 +1372,7 @@ def test_restore_data_source_dump_runs_mysql_with_hostname_and_port():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1421,6 +1425,7 @@ def test_restore_data_source_dump_runs_mysql_with_socket_path():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1468,6 +1473,7 @@ def test_restore_data_source_dump_runs_mysql_with_tls():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1515,6 +1521,7 @@ def test_restore_data_source_dump_runs_mysql_without_tls():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1558,6 +1565,7 @@ def test_restore_data_source_dump_runs_mysql_with_username_and_password():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1610,6 +1618,7 @@ def test_restore_data_source_with_environment_password_transport_skips_defaults_
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root', 'MYSQL_PWD': 'trustsome1'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1677,6 +1686,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1749,6 +1759,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
|
||||
@@ -995,6 +995,7 @@ def test_restore_data_source_dump_runs_pg_restore():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'PGSSLMODE': 'disable'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
@@ -1059,6 +1060,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_hostname_and_port():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'PGSSLMODE': 'disable'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
@@ -1127,6 +1129,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_username_and_password():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'PGPASSWORD': 'trustsome1', 'PGSSLMODE': 'disable'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
@@ -1208,6 +1211,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
|
||||
input_file=extract_process.stdout,
|
||||
environment={'PGPASSWORD': 'clipassword', 'PGSSLMODE': 'disable'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
@@ -1293,6 +1297,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
|
||||
input_file=extract_process.stdout,
|
||||
environment={'PGPASSWORD': 'restorepassword', 'PGSSLMODE': 'disable'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
@@ -1365,6 +1370,7 @@ def test_restore_data_source_dump_runs_pg_restore_with_options():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'PGSSLMODE': 'disable'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
@@ -1420,6 +1426,7 @@ def test_restore_data_source_dump_runs_psql_for_all_database_dump():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'PGSSLMODE': 'disable'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
('psql', '--no-password', '--no-psqlrc', '--quiet', '--command', 'ANALYZE'),
|
||||
@@ -1461,6 +1468,7 @@ def test_restore_data_source_dump_runs_psql_for_plain_database_dump():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'PGSSLMODE': 'disable'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
@@ -1531,6 +1539,7 @@ def test_restore_data_source_dump_runs_non_default_pg_restore_and_psql():
|
||||
input_file=extract_process.stdout,
|
||||
environment={'PGSSLMODE': 'disable'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
@@ -1621,6 +1630,7 @@ def test_restore_data_source_dump_without_extract_process_restores_from_disk():
|
||||
input_file=None,
|
||||
environment={'PGSSLMODE': 'disable'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
@@ -1683,6 +1693,7 @@ def test_restore_data_source_dump_with_schemas_restores_schemas():
|
||||
input_file=None,
|
||||
environment={'PGSSLMODE': 'disable'},
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
|
||||
@@ -341,6 +341,7 @@ def test_restore_data_source_dump_restores_database():
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
flexmock(module.os).should_receive('remove').once()
|
||||
@@ -379,6 +380,7 @@ def test_restore_data_source_dump_runs_non_default_sqlite_restores_database():
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
flexmock(module.os).should_receive('remove').once()
|
||||
@@ -415,6 +417,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
flexmock(module.os).should_receive('remove').once()
|
||||
@@ -451,6 +454,7 @@ def test_restore_data_source_dump_runs_non_default_sqlite_with_connection_params
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
flexmock(module.os).should_receive('remove').once()
|
||||
@@ -489,6 +493,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
flexmock(module.os).should_receive('remove').once()
|
||||
@@ -526,6 +531,7 @@ def test_restore_data_source_dump_runs_non_default_sqlite_without_connection_par
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
borg_local_path='borg',
|
||||
).and_yield().once()
|
||||
|
||||
flexmock(module.os).should_receive('remove').once()
|
||||
|
||||
+42
-55
@@ -104,11 +104,10 @@ def test_output_buffers_for_process_returns_stderr_only_when_stdout_excluded():
|
||||
)
|
||||
|
||||
|
||||
def test_borg_log_line_to_record_parses_line():
|
||||
flexmock(module).should_receive('log_line_to_record').never()
|
||||
line = '{"levelname": "INFO", "time": 12345, "message": "All done", "name": "borg.something"}'
|
||||
def test_borg_json_log_line_to_record_parses_log_message_line():
|
||||
line = '{"type": "log_message", "levelname": "INFO", "time": 12345, "message": "All done", "name": "borg.something"}'
|
||||
|
||||
record = module.borg_log_line_to_record(line, module.logging.INFO)
|
||||
record = module.borg_json_log_line_to_record(line, module.logging.INFO)
|
||||
|
||||
assert record.levelno == module.logging.INFO
|
||||
assert record.created == 12345
|
||||
@@ -117,24 +116,35 @@ def test_borg_log_line_to_record_parses_line():
|
||||
assert record.name == 'borg.something'
|
||||
|
||||
|
||||
def test_borg_log_line_to_record_with_invalid_json_falls_back_to_raw_line():
|
||||
record = flexmock()
|
||||
def test_borg_json_log_line_to_record_parses_file_status_line():
|
||||
flexmock(module.time).should_receive('time').and_return(12345)
|
||||
line = '{"type": "file_status", "status": "-", "path": "/foo/bar"}'
|
||||
|
||||
record = module.borg_json_log_line_to_record(line, module.logging.INFO)
|
||||
|
||||
assert record.levelno == module.logging.INFO
|
||||
assert record.created == 12345
|
||||
assert record.msg == '- /foo/bar'
|
||||
assert record.levelname == 'INFO'
|
||||
assert record.name == 'borg.file_status'
|
||||
|
||||
|
||||
def test_borg_json_log_line_to_record_handles_invalid_json():
|
||||
line = '{invalid'
|
||||
flexmock(module).should_receive('log_line_to_record').with_args(
|
||||
line, module.logging.INFO
|
||||
).and_return(record).once()
|
||||
|
||||
assert module.borg_log_line_to_record(line, module.logging.INFO) == record
|
||||
assert module.borg_json_log_line_to_record(line, module.logging.INFO) is None
|
||||
|
||||
|
||||
def test_borg_log_line_to_record_with_non_dict_json_falls_back_to_raw_line():
|
||||
record = flexmock()
|
||||
def test_borg_json_log_line_to_record_handles_non_dict_json():
|
||||
line = '[]'
|
||||
flexmock(module).should_receive('log_line_to_record').with_args(
|
||||
line, module.logging.INFO
|
||||
).and_return(record).once()
|
||||
|
||||
assert module.borg_log_line_to_record(line, module.logging.INFO) == record
|
||||
assert module.borg_json_log_line_to_record(line, module.logging.INFO) is None
|
||||
|
||||
|
||||
def test_borg_json_log_line_to_record_handles_json_dict_without_type():
|
||||
line = '{"status": "-", "path": "/foo/bar"}'
|
||||
|
||||
assert module.borg_json_log_line_to_record(line, module.logging.INFO) is None
|
||||
|
||||
|
||||
def test_log_line_to_record_makes_log_record():
|
||||
@@ -149,14 +159,14 @@ def test_log_line_to_record_makes_log_record():
|
||||
|
||||
def test_parse_log_line_with_borg_command_parses_borg_log_line():
|
||||
record = flexmock()
|
||||
flexmock(module).should_receive('borg_log_line_to_record').and_return(record).once()
|
||||
flexmock(module).should_receive('borg_json_log_line_to_record').and_return(record).once()
|
||||
flexmock(module).should_receive('log_line_to_record').never()
|
||||
|
||||
assert (
|
||||
module.parse_log_line(
|
||||
'All done',
|
||||
module.logging.INFO,
|
||||
came_from_stderr=False,
|
||||
elevate_stderr=False,
|
||||
borg_local_path='borg',
|
||||
command=['borg', 'do-stuff'],
|
||||
)
|
||||
@@ -166,14 +176,14 @@ def test_parse_log_line_with_borg_command_parses_borg_log_line():
|
||||
|
||||
def test_parse_log_line_without_borg_command_parses_plain_log_line():
|
||||
record = flexmock()
|
||||
flexmock(module).should_receive('borg_log_line_to_record').never()
|
||||
flexmock(module).should_receive('borg_json_log_line_to_record').never()
|
||||
flexmock(module).should_receive('log_line_to_record').and_return(record).once()
|
||||
|
||||
assert (
|
||||
module.parse_log_line(
|
||||
'All done',
|
||||
module.logging.INFO,
|
||||
came_from_stderr=False,
|
||||
elevate_stderr=False,
|
||||
borg_local_path='borg',
|
||||
command=['totally-not-borg', 'do-stuff'],
|
||||
)
|
||||
@@ -181,9 +191,9 @@ def test_parse_log_line_without_borg_command_parses_plain_log_line():
|
||||
)
|
||||
|
||||
|
||||
def test_parse_log_line_with_came_from_stderr_makes_error_record():
|
||||
def test_parse_log_line_with_elevate_stderr_makes_error_record():
|
||||
record = flexmock()
|
||||
flexmock(module).should_receive('borg_log_line_to_record').never()
|
||||
flexmock(module).should_receive('borg_json_log_line_to_record').never()
|
||||
flexmock(module).should_receive('log_line_to_record').with_args(
|
||||
'All done', module.logging.ERROR
|
||||
).and_return(record).once()
|
||||
@@ -192,7 +202,7 @@ def test_parse_log_line_with_came_from_stderr_makes_error_record():
|
||||
module.parse_log_line(
|
||||
'All done',
|
||||
module.logging.INFO,
|
||||
came_from_stderr=True,
|
||||
elevate_stderr=True,
|
||||
borg_local_path='borg',
|
||||
command=['totally-not-borg', 'do-stuff'],
|
||||
)
|
||||
@@ -200,9 +210,9 @@ def test_parse_log_line_with_came_from_stderr_makes_error_record():
|
||||
)
|
||||
|
||||
|
||||
def test_parse_log_line_with_came_from_stderr_and_warning_prefix_makes_warning_record():
|
||||
def test_parse_log_line_with_elevate_stderr_and_warning_prefix_makes_warning_record():
|
||||
record = flexmock()
|
||||
flexmock(module).should_receive('borg_log_line_to_record').never()
|
||||
flexmock(module).should_receive('borg_json_log_line_to_record').never()
|
||||
flexmock(module).should_receive('log_line_to_record').with_args(
|
||||
'warning: All done', module.logging.WARNING
|
||||
).and_return(record).once()
|
||||
@@ -211,7 +221,7 @@ def test_parse_log_line_with_came_from_stderr_and_warning_prefix_makes_warning_r
|
||||
module.parse_log_line(
|
||||
'warning: All done',
|
||||
module.logging.INFO,
|
||||
came_from_stderr=True,
|
||||
elevate_stderr=True,
|
||||
borg_local_path='borg',
|
||||
command=['totally-not-borg', 'do-stuff'],
|
||||
)
|
||||
@@ -497,7 +507,7 @@ def test_execute_command_and_capture_output_returns_stdout():
|
||||
full_command,
|
||||
stdin=None,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=None,
|
||||
stderr=subprocess.PIPE,
|
||||
shell=False,
|
||||
env=None,
|
||||
cwd=None,
|
||||
@@ -534,29 +544,6 @@ def test_execute_command_and_capture_output_with_capture_stderr_returns_stderr()
|
||||
assert output_lines == ('out',)
|
||||
|
||||
|
||||
def test_execute_command_and_capture_output_without_capture_stderr_omits_stderr():
|
||||
full_command = ['foo', 'bar']
|
||||
process = flexmock()
|
||||
flexmock(module.subprocess).should_receive('Popen').with_args(
|
||||
full_command,
|
||||
stdin=None,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=None,
|
||||
shell=False,
|
||||
env=None,
|
||||
cwd=None,
|
||||
close_fds=False,
|
||||
).and_return(process).once()
|
||||
flexmock(module.borgmatic.logger).should_receive('Log_prefix').and_return(flexmock())
|
||||
flexmock(module).should_receive('log_outputs').and_yield('out')
|
||||
|
||||
output_lines = tuple(
|
||||
module.execute_command_and_capture_output(full_command, capture_stderr=False)
|
||||
)
|
||||
|
||||
assert output_lines == ('out',)
|
||||
|
||||
|
||||
def test_execute_command_and_capture_output_returns_output_when_process_error_is_not_considered_an_error():
|
||||
full_command = ['foo', 'bar']
|
||||
err_output = b'[]'
|
||||
@@ -565,7 +552,7 @@ def test_execute_command_and_capture_output_returns_output_when_process_error_is
|
||||
full_command,
|
||||
stdin=None,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=None,
|
||||
stderr=subprocess.PIPE,
|
||||
shell=False,
|
||||
env=None,
|
||||
cwd=None,
|
||||
@@ -587,7 +574,7 @@ def test_execute_command_and_capture_output_raises_when_command_errors():
|
||||
full_command,
|
||||
stdin=None,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=None,
|
||||
stderr=subprocess.PIPE,
|
||||
shell=False,
|
||||
env=None,
|
||||
cwd=None,
|
||||
@@ -609,7 +596,7 @@ def test_execute_command_and_capture_output_with_shell_returns_output():
|
||||
'foo bar',
|
||||
stdin=None,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=None,
|
||||
stderr=subprocess.PIPE,
|
||||
shell=True,
|
||||
env=None,
|
||||
cwd=None,
|
||||
@@ -631,7 +618,7 @@ def test_execute_command_and_capture_output_with_enviroment_returns_output():
|
||||
full_command,
|
||||
stdin=None,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=None,
|
||||
stderr=subprocess.PIPE,
|
||||
shell=False,
|
||||
env={'a': 'b'},
|
||||
cwd=None,
|
||||
@@ -659,7 +646,7 @@ def test_execute_command_and_capture_output_returns_output_with_working_director
|
||||
full_command,
|
||||
stdin=None,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=None,
|
||||
stderr=subprocess.PIPE,
|
||||
shell=False,
|
||||
env=None,
|
||||
cwd='/working',
|
||||
|
||||
Reference in New Issue
Block a user