mirror of
https://github.com/borgmatic-collective/borgmatic.git
synced 2026-07-23 02:23:01 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08ff743cd7 | ||
|
|
406b533b15 | ||
|
|
18a1d4f00b | ||
|
|
54d955bc95 | ||
|
|
a72f9b24b5 | ||
|
|
1b471f5346 | ||
|
|
9428a92297 | ||
|
|
c0d4489c25 | ||
|
|
b4ea45ab74 | ||
|
|
6ae9b973fc | ||
|
|
01c7d0a0db | ||
|
|
a31f913f54 | ||
|
|
0b086a3c18 | ||
|
|
dfdb1cb15e | ||
|
|
c8307065fa | ||
|
|
ae00770c35 | ||
|
|
77782bc215 | ||
|
|
ff2009fe19 | ||
|
|
141f2d68c1 | ||
|
|
772c4c1b83 | ||
|
|
a77de447d4 | ||
|
|
50a062611e | ||
|
|
42bf6c6f8e | ||
|
|
e95d652abb | ||
|
|
2a97ac0174 | ||
|
|
5029ab1ffd | ||
|
|
7dc902f5db | ||
|
|
8134bdec7f | ||
|
|
5e7d14e419 | ||
|
|
8f2ae5e9a1 | ||
|
|
928bb3fb8c | ||
|
|
31623f9c91 | ||
|
|
f158d42b28 | ||
|
|
dec47298bd | ||
|
|
4976352fd3 | ||
|
|
f95fbaa6aa | ||
|
|
7dc7b77f6c | ||
|
|
47ce12e4b6 | ||
|
|
e72ace5294 | ||
|
|
72c3e524be | ||
|
|
399576e7c9 | ||
|
|
a7356f2360 | ||
|
|
1f8c39fb17 | ||
|
|
66e567f78c | ||
|
|
ea45a37db8 | ||
|
|
a229fc7f92 | ||
|
|
573405bb88 | ||
|
|
94dc4c497a | ||
|
|
0e9193c46b | ||
|
|
151becbaea | ||
|
|
ce7f0226be | ||
|
|
51ef9c7708 | ||
|
|
14a8055e71 | ||
|
|
fcfc7ee726 | ||
|
|
859c338d06 | ||
|
|
d1f2d8a061 |
@@ -29,5 +29,3 @@ jobs:
|
||||
PASSWORD: "${{ secrets.REGISTRY_PASSWORD }}"
|
||||
- run: podman build --tag "$IMAGE_NAME" --file docs/Dockerfile --storage-opt "overlay.mount_program=/usr/bin/fuse-overlayfs" .
|
||||
- run: podman push "$IMAGE_NAME"
|
||||
- run: scripts/export-docs-from-image
|
||||
- run: curl --user "${{ secrets.REGISTRY_USERNAME }}:${{ secrets.REGISTRY_PASSWORD }}" --upload-file borgmatic-docs.tar.gz https://projects.torsion.org/api/packages/borgmatic-collective/generic/borgmatic-docs/$(head --lines=1 NEWS)/borgmatic-docs.tar.gz
|
||||
|
||||
@@ -1,3 +1,51 @@
|
||||
2.0.13
|
||||
* #1054: Allow the Btrfs hook to create and delete snapshots even when running
|
||||
as a non-root user. See the documentation for more information:
|
||||
https://torsion.org/borgmatic/reference/configuration/data-sources/btrfs/#non-root-user
|
||||
* #1122: To prevent the user from inadvertently excluding the "bootstrap" action's manifest, always
|
||||
error and exit when the borgmatic runtime directory overlaps with the configured excludes.
|
||||
* #1179: Add a "file_list_format" option for setting the "list" action's output format and an
|
||||
"archive_list_format" option for setting the "repo-list" action's format.
|
||||
* #1192: Fix for over-aggressive deduplication of source directories that contain the borgmatic
|
||||
runtime directory, potentially resulting in data loss (data not getting backed up) when
|
||||
snapshotting these source directories.
|
||||
* #1192, #1163: Document potential interactions between security settings in borgmatic's sample
|
||||
systemd service file and the ZFS, LVM, and Btrfs hooks.
|
||||
* #1193: In the documentation for the MariaDB/MySQL database hooks, clarify how to set custom
|
||||
command-line flags for database commands.
|
||||
* #1193: For the MariaDB and MySQL database hooks, add a "socket_path" option for Unix socket
|
||||
database connections.
|
||||
* #1193: For the MariaDB and MySQL database hooks, creates a consistent snapshot by dumping all
|
||||
tables in a single transaction.
|
||||
* #1194: Fix for an incorrect diff command shown when running the "generate config" action with a
|
||||
source configuration file.
|
||||
* #1195: Fix a regression in the ZFS, LVM, and Btrfs hooks in which snapshotted paths ignored
|
||||
global excludes.
|
||||
* #1201: Document a problematic interaction between borgmatic and systemd-tmpfiles:
|
||||
https://torsion.org/borgmatic/reference/configuration/runtime-directory/#systemd-tmpfiles
|
||||
* #1203: Fix that errors and exits when the borgmatic runtime directory is partially excluded by
|
||||
configured excludes. Previously, borgmatic only errored when the runtime directory was completely
|
||||
excluded.
|
||||
* #1206: Adjust Btrfs snapshot paths so that Borg 1.x gets file cache hits when backing them up,
|
||||
improving performance.
|
||||
* Update the sample systemd timer with a shorter random delay when catching up on a missed run.
|
||||
|
||||
2.0.12
|
||||
* #1127: Fix for the database hooks not respecting the "working_directory" option.
|
||||
* #1181: Add an "ask_for_password" option to the KeePassXC credential hook for disabling
|
||||
KeePassXC's password prompt, e.g. if you're only using a key file to decrypt your database.
|
||||
* #1184: Fix the fish shell completion's detection of version mismatches.
|
||||
* #1186: Fix a regression in the Btrfs hook in which subvolume snapshots didn't get cleaned up
|
||||
until the start of the next borgmatic run.
|
||||
* In the SQLite database hook, run SQLite such that it exits upon encountering an error instead of,
|
||||
you know, not doing that.
|
||||
* Add documentation on repositories, including SSH, Rclone, S3, and B2:
|
||||
https://torsion.org/borgmatic/reference/configuration/repositories/
|
||||
* Improve documentation search results for individual configuration options.
|
||||
* Add borgmatic release artifacts (wheel and tarball) to each release on the releases page:
|
||||
https://projects.torsion.org/borgmatic-collective/borgmatic/releases
|
||||
* Move the tarball of borgmatic's HTML documentation from the packages page to the releases page.
|
||||
|
||||
2.0.11
|
||||
* #957: Document borgmatic's limitations around parallelism—both its own and Borg's. See the
|
||||
documentation for more information:
|
||||
|
||||
@@ -20,10 +20,8 @@ import borgmatic.borg.extract
|
||||
import borgmatic.borg.list
|
||||
import borgmatic.borg.pattern
|
||||
import borgmatic.borg.repo_list
|
||||
import borgmatic.borg.state
|
||||
import borgmatic.config.paths
|
||||
import borgmatic.execute
|
||||
import borgmatic.hooks.command
|
||||
|
||||
DEFAULT_CHECKS = (
|
||||
{'name': 'repository', 'frequency': '1 month'},
|
||||
|
||||
@@ -2,7 +2,6 @@ import logging
|
||||
|
||||
import borgmatic.borg.compact
|
||||
import borgmatic.borg.feature
|
||||
import borgmatic.hooks.command
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import os
|
||||
import borgmatic.borg.extract
|
||||
import borgmatic.borg.repo_list
|
||||
import borgmatic.config.paths
|
||||
import borgmatic.hooks.command
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -36,12 +36,12 @@ def run_generate(generate_arguments, global_arguments):
|
||||
if generate_arguments.source_filename:
|
||||
logger.answer(
|
||||
f'''
|
||||
Merged in the contents of configuration file at: {generate_arguments.source_filename}'''
|
||||
Merged the contents of the source configuration file: {generate_arguments.source_filename}'''
|
||||
)
|
||||
|
||||
if not generate_arguments.split:
|
||||
logger.answer(
|
||||
'''To review the changes made, run:
|
||||
f'''To review the changes made, run:
|
||||
|
||||
diff --unified {generate_arguments.source_filename} {generate_arguments.destination_path}''',
|
||||
)
|
||||
|
||||
@@ -49,12 +49,14 @@ def run_create(
|
||||
working_directory,
|
||||
borgmatic_runtime_directory,
|
||||
)
|
||||
|
||||
original_patterns = list(patterns)
|
||||
borgmatic.hooks.dispatch.call_hooks_even_if_unconfigured(
|
||||
'remove_data_source_dumps',
|
||||
config,
|
||||
borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE,
|
||||
borgmatic_runtime_directory,
|
||||
patterns,
|
||||
original_patterns,
|
||||
global_arguments.dry_run,
|
||||
)
|
||||
active_dumps = borgmatic.hooks.dispatch.call_hooks(
|
||||
@@ -129,12 +131,14 @@ def run_create(
|
||||
|
||||
yield output
|
||||
|
||||
# Use the original patterns so as to disregard any modifications made by any data source
|
||||
# hooks, e.g. via dump_data_sources() above.
|
||||
borgmatic.hooks.dispatch.call_hooks_even_if_unconfigured(
|
||||
'remove_data_source_dumps',
|
||||
config,
|
||||
borgmatic.hooks.dispatch.Hook_type.DATA_SOURCE,
|
||||
borgmatic_runtime_directory,
|
||||
patterns,
|
||||
original_patterns,
|
||||
global_arguments.dry_run,
|
||||
)
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import logging
|
||||
|
||||
import borgmatic.actions.arguments
|
||||
import borgmatic.borg.delete
|
||||
import borgmatic.borg.repo_delete
|
||||
import borgmatic.borg.repo_list
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -2,7 +2,6 @@ import logging
|
||||
|
||||
import borgmatic.borg.extract
|
||||
import borgmatic.borg.repo_list
|
||||
import borgmatic.hooks.command
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -259,31 +259,37 @@ def deduplicate_runtime_directory_patterns(patterns, config, borgmatic_runtime_d
|
||||
return patterns
|
||||
|
||||
deduplicated = {} # Use just the keys as an ordered set.
|
||||
runtime_directory_parents = set(pathlib.PurePath(borgmatic_runtime_directory).parents).union(
|
||||
{pathlib.PurePath(borgmatic_runtime_directory)}
|
||||
)
|
||||
|
||||
for pattern in patterns:
|
||||
if pattern.type != borgmatic.borg.pattern.Pattern_type.ROOT:
|
||||
deduplicated[pattern] = True
|
||||
continue
|
||||
|
||||
parents = pathlib.PurePath(pattern.path).parents
|
||||
pattern_parents = pathlib.PurePath(pattern.path).parents
|
||||
|
||||
# If another directory in the given list is a parent of current directory (even n levels up)
|
||||
# and both are on the same filesystem (or one_file_system is not set), then the current
|
||||
# directory is a duplicate.
|
||||
# If:
|
||||
#
|
||||
# 1. another pattern is a parent of the current pattern (even n levels up),
|
||||
# 2. both patterns are parents of the runtime directory (even n levels up),
|
||||
# 3. and both patterns are on the same filesystem (or one_file_system is not set)
|
||||
#
|
||||
# ... then consider the current pattern as a duplicate.
|
||||
for other_pattern in patterns:
|
||||
if other_pattern.type != borgmatic.borg.pattern.Pattern_type.ROOT:
|
||||
continue
|
||||
|
||||
if any(
|
||||
pathlib.PurePath(other_pattern.path) == parent
|
||||
and pathlib.PurePosixPath(other_pattern.path)
|
||||
in pathlib.PurePath(borgmatic_runtime_directory).parents
|
||||
and pattern.device is not None
|
||||
and (
|
||||
other_pattern.device == pattern.device
|
||||
or config.get('one_file_system') is not True
|
||||
)
|
||||
for parent in parents
|
||||
device_matches = pattern.device is not None and (
|
||||
other_pattern.device == pattern.device or config.get('one_file_system') is not True
|
||||
)
|
||||
|
||||
if (
|
||||
pathlib.PurePath(other_pattern.path) in pattern_parents
|
||||
and pathlib.PurePosixPath(other_pattern.path) in runtime_directory_parents
|
||||
and pathlib.PurePosixPath(pattern.path) in runtime_directory_parents
|
||||
and device_matches
|
||||
):
|
||||
break
|
||||
else:
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import logging
|
||||
|
||||
import borgmatic.borg.prune
|
||||
import borgmatic.hooks.command
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -8,7 +8,6 @@ import tempfile
|
||||
import borgmatic.actions.pattern
|
||||
import borgmatic.borg.extract
|
||||
import borgmatic.borg.list
|
||||
import borgmatic.borg.mount
|
||||
import borgmatic.borg.repo_list
|
||||
import borgmatic.config.paths
|
||||
import borgmatic.hooks.data_source.dump
|
||||
|
||||
+16
-19
@@ -96,32 +96,29 @@ def validate_planned_backup_paths(
|
||||
if path_line and path_line.startswith(('- ', '+ '))
|
||||
)
|
||||
|
||||
# These are the subset of output paths contained within the borgmatic runtime directory.
|
||||
paths_inside_runtime_directory = {
|
||||
path for path in paths if any_parent_directories(path, (borgmatic_runtime_directory,))
|
||||
}
|
||||
|
||||
# If the runtime directory isn't present in the source patterns, then we shouldn't expect it to
|
||||
# be in the paths output from the Borg dry run.
|
||||
runtime_directory_present_in_patterns = any(
|
||||
runtime_directory_root_patterns = tuple(
|
||||
pattern
|
||||
for pattern in patterns
|
||||
if any_parent_directories(pattern.path, (borgmatic_runtime_directory,))
|
||||
if pattern.type == borgmatic.borg.pattern.Pattern_type.ROOT
|
||||
)
|
||||
|
||||
# If no paths to backup are inside the runtime directory, it must've been excluded.
|
||||
if (
|
||||
not paths_inside_runtime_directory
|
||||
and runtime_directory_present_in_patterns
|
||||
and not dry_run
|
||||
and os.path.exists(borgmatic_runtime_directory)
|
||||
):
|
||||
raise ValueError(
|
||||
f'The runtime directory {os.path.normpath(borgmatic_runtime_directory)} overlaps with the configured excludes or patterns with excludes. Please ensure the runtime directory is not excluded.',
|
||||
)
|
||||
if not dry_run and os.path.exists(borgmatic_runtime_directory):
|
||||
# If there are any root patterns in the runtime directory that are missing from the paths
|
||||
# Borg is planning to backup, then they must've gotten excluded, e.g. by user-configured
|
||||
# excludes. Error accordingly.
|
||||
for pattern in runtime_directory_root_patterns:
|
||||
if not any(any_parent_directories(path, (pattern.path,)) for path in paths):
|
||||
raise ValueError(
|
||||
f'The runtime directory {os.path.normpath(borgmatic_runtime_directory)} overlaps with the configured excludes or patterns with excludes. Please ensure the runtime directory is not excluded.',
|
||||
)
|
||||
|
||||
return tuple(path for path in paths if path not in paths_inside_runtime_directory)
|
||||
# Return the subset of output paths *not* contained within the borgmatic runtime directory. The
|
||||
# intent is that any downstream checks using these paths should skip runtime paths that
|
||||
# borgmatic uses for its own bookkeeping, instead focusing on user-configured paths.
|
||||
return tuple(
|
||||
path for path in paths if not any_parent_directories(path, (borgmatic_runtime_directory,))
|
||||
)
|
||||
|
||||
|
||||
MAX_SPECIAL_FILE_PATHS_LENGTH = 1000
|
||||
|
||||
@@ -3,7 +3,6 @@ import os
|
||||
import shlex
|
||||
|
||||
import borgmatic.config.paths
|
||||
import borgmatic.logger
|
||||
from borgmatic.borg import environment, flags
|
||||
from borgmatic.execute import DO_NOT_CAPTURE, execute_command
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ MAKE_FLAGS_EXCLUDES = (
|
||||
'archive',
|
||||
'paths',
|
||||
'find_paths',
|
||||
'format',
|
||||
*ARCHIVE_FILTER_FLAGS_MOVED_TO_REPO_LIST,
|
||||
)
|
||||
|
||||
@@ -54,6 +55,7 @@ def make_list_command(
|
||||
+ flags.make_flags('umask', config.get('umask'))
|
||||
+ flags.make_flags('log-json', config.get('log_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)
|
||||
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||
+ (
|
||||
|
||||
@@ -110,7 +110,7 @@ def get_latest_archive(
|
||||
return latest_archive
|
||||
|
||||
|
||||
MAKE_FLAGS_EXCLUDES = ('repository', 'prefix', 'match_archives')
|
||||
MAKE_FLAGS_EXCLUDES = ('repository', 'format', 'prefix', 'match_archives')
|
||||
|
||||
|
||||
def make_repo_list_command(
|
||||
@@ -170,6 +170,9 @@ def make_repo_list_command(
|
||||
)
|
||||
)
|
||||
)
|
||||
+ flags.make_flags(
|
||||
'format', repo_list_arguments.format or config.get('archive_list_format')
|
||||
)
|
||||
+ flags.make_flags_from_arguments(repo_list_arguments, excludes=MAKE_FLAGS_EXCLUDES)
|
||||
+ (tuple(shlex.split(extra_borg_options)) if extra_borg_options else ())
|
||||
+ flags.make_repository_flags(repository_path, local_borg_version)
|
||||
|
||||
@@ -1 +1 @@
|
||||
DEFAULT_BORGMATIC_SOURCE_DIRECTORY = '~/.borgmatic'
|
||||
DEFAULT_BORGMATIC_SOURCE_DIRECTORY = '~/.borgmatic' # pragma: nocover
|
||||
|
||||
@@ -297,7 +297,14 @@ def parse_arguments_for_actions(unparsed_arguments, action_parsers, global_parse
|
||||
)
|
||||
|
||||
|
||||
OMITTED_FLAG_NAMES = {'match-archives', 'progress', 'statistics', 'list-details'}
|
||||
OMITTED_FLAG_NAMES = {
|
||||
'match-archives',
|
||||
'progress',
|
||||
'statistics',
|
||||
'list-details',
|
||||
'file-list-format',
|
||||
'archive-list-format',
|
||||
}
|
||||
|
||||
|
||||
def make_argument_description(schema, flag_name):
|
||||
@@ -568,8 +575,7 @@ def make_parsers(schema, unparsed_arguments): # noqa: PLR0915
|
||||
ignoring actions, and the combined parser is handy for displaying help that includes everything:
|
||||
global flags, a list of actions, etc.
|
||||
'''
|
||||
config_paths = collect.get_default_config_paths(expand_home=True)
|
||||
unexpanded_config_paths = collect.get_default_config_paths(expand_home=False)
|
||||
config_paths = collect.get_default_config_paths()
|
||||
|
||||
# Using allow_abbrev=False here prevents the global parser from erroring about "ambiguous"
|
||||
# options like --encryption. Such options are intended for an action parser rather than the
|
||||
@@ -582,7 +588,7 @@ def make_parsers(schema, unparsed_arguments): # noqa: PLR0915
|
||||
'--config',
|
||||
dest='config_paths',
|
||||
action='append',
|
||||
help=f"Configuration filename or directory, can specify flag multiple times, defaults to: -c {' -c '.join(unexpanded_config_paths)}",
|
||||
help='Configuration filename or directory, can specify flag multiple times, defaults to /etc/borgmatic/config.yaml, /etc/borgmatic.d, $XDG_CONFIG_HOME/borgmatic/config.yaml, and $XDG_CONFIG_HOME/borgmatic.d, where $XDG_CONFIG_HOME defaults to $HOME/.config',
|
||||
)
|
||||
global_group.add_argument(
|
||||
'-n',
|
||||
@@ -1215,7 +1221,7 @@ def make_parsers(schema, unparsed_arguments): # noqa: PLR0915
|
||||
'--destination',
|
||||
dest='destination_path',
|
||||
default=config_paths[0],
|
||||
help=f'Destination configuration file (or directory if using --split), default: {unexpanded_config_paths[0]}',
|
||||
help='Destination configuration file (or directory if using --split), default: /etc/borgmatic/config.yaml',
|
||||
)
|
||||
config_generate_group.add_argument(
|
||||
'--overwrite',
|
||||
@@ -1542,7 +1548,7 @@ def make_parsers(schema, unparsed_arguments): # noqa: PLR0915
|
||||
action='store_true',
|
||||
help='Output only archive names',
|
||||
)
|
||||
repo_list_group.add_argument('--format', help='Format for archive listing')
|
||||
repo_list_group.add_argument('--format', help='Borg format for the archive listing')
|
||||
repo_list_group.add_argument(
|
||||
'--json',
|
||||
default=False,
|
||||
@@ -1645,7 +1651,7 @@ def make_parsers(schema, unparsed_arguments): # noqa: PLR0915
|
||||
action='store_true',
|
||||
help='Output only path names',
|
||||
)
|
||||
list_group.add_argument('--format', help='Format for file listing')
|
||||
list_group.add_argument('--format', help='Borg format for the file listing')
|
||||
list_group.add_argument(
|
||||
'--json',
|
||||
default=False,
|
||||
@@ -2015,7 +2021,7 @@ def parse_arguments(schema, *unparsed_arguments):
|
||||
)
|
||||
|
||||
if not arguments['global'].config_paths:
|
||||
arguments['global'].config_paths = collect.get_default_config_paths(expand_home=True)
|
||||
arguments['global'].config_paths = collect.get_default_config_paths()
|
||||
|
||||
for action_name in ('bootstrap', 'generate', 'validate'):
|
||||
if action_name in arguments and len(arguments) > HIGHLANDER_ACTION_ARGUMENTS_COUNT:
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import os
|
||||
|
||||
|
||||
def get_default_config_paths(expand_home=True):
|
||||
def get_default_config_paths():
|
||||
'''
|
||||
Based on the value of the XDG_CONFIG_HOME and HOME environment variables, return a list of
|
||||
default configuration paths. This includes both system-wide configuration and configuration in
|
||||
the current user's home directory.
|
||||
|
||||
Don't expand the home directory ($HOME) if the expand home flag is False.
|
||||
Return a list of default configuration paths. This includes both system-wide
|
||||
configuration and configuration in the current user's home directory.
|
||||
'''
|
||||
user_config_directory = os.getenv('XDG_CONFIG_HOME') or os.path.join('$HOME', '.config')
|
||||
if expand_home:
|
||||
user_config_directory = os.path.expandvars(user_config_directory)
|
||||
user_config_directory = os.path.expandvars(
|
||||
os.getenv('XDG_CONFIG_HOME') or os.path.join('$HOME', '.config')
|
||||
)
|
||||
|
||||
return [
|
||||
'/etc/borgmatic/config.yaml',
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import collections
|
||||
import contextlib
|
||||
import io
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
@@ -316,6 +317,24 @@ def merge_source_configuration_into_destination(destination_config, source_confi
|
||||
return destination_config
|
||||
|
||||
|
||||
def get_configuration_subset(config, option_name): # pragma: no cover
|
||||
'''
|
||||
Given configuration as a ruamel.yaml.CommentedMap and an option name found within it at the top
|
||||
level, return a new CommentedMap containing a subset of the configuration with only the given
|
||||
option and no other top-level options.
|
||||
|
||||
This is useful when generating the sample configuration for a single option instead of a whole
|
||||
configuration file.
|
||||
'''
|
||||
option_config = ruamel.yaml.CommentedMap({option_name: config[option_name]})
|
||||
|
||||
# Due to a quirk of ruamel.yaml, the comment right before a top-level key is not on that key and
|
||||
# needs to get copied separately.
|
||||
option_config.ca.items[option_name] = config.ca.items[option_name]
|
||||
|
||||
return option_config
|
||||
|
||||
|
||||
def generate_sample_configuration(
|
||||
dry_run,
|
||||
source_filename,
|
||||
@@ -359,16 +378,22 @@ def generate_sample_configuration(
|
||||
|
||||
os.makedirs(destination_path, exist_ok=True)
|
||||
|
||||
for option_name, option_config in destination_config.items():
|
||||
for option_name in destination_config:
|
||||
write_configuration(
|
||||
os.path.join(destination_path, f'{option_name}.yaml'),
|
||||
transform_optional_configuration(
|
||||
render_configuration({option_name: option_config}),
|
||||
render_configuration(get_configuration_subset(destination_config, option_name)),
|
||||
comment_out=False,
|
||||
),
|
||||
).strip(),
|
||||
overwrite=overwrite,
|
||||
)
|
||||
|
||||
# Also dump a manifest listing all the options we've written.
|
||||
json.dump(
|
||||
{'option_names': list(destination_config.keys())},
|
||||
open(os.path.join(destination_path, 'options.json'), 'w', encoding='utf-8'),
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
if os.path.exists(destination_path) and not os.path.isfile(destination_path):
|
||||
|
||||
@@ -177,7 +177,9 @@ properties:
|
||||
as "source_directories"; they tell Borg which paths to backup
|
||||
(modulo any excludes). Globs are expanded. (Tildes are not.) See
|
||||
the output of "borg help patterns" for more details. Quote any value
|
||||
if it contains leading punctuation, so it parses correctly.
|
||||
if it contains leading punctuation, so it parses correctly. Also use
|
||||
leading slashes in absolute paths, or data source hooks may be
|
||||
unable to rewrite patterns as needed.
|
||||
example:
|
||||
- 'R /'
|
||||
- '- /home/*/.cache'
|
||||
@@ -492,6 +494,27 @@ properties:
|
||||
If match_archives is not specified, borgmatic defaults to deriving
|
||||
the match_archives value from archive_name_format.
|
||||
example: "sh:{hostname}-*"
|
||||
file_list_format:
|
||||
type: string
|
||||
description: |
|
||||
Borg format for the files listing of the "list" action. Corresponds
|
||||
to the "--format" flag. Defaults to "{mode} {user:6} {group:6}
|
||||
{size:8} {mtime} {path}{extra}{NL}". With "--json", the form of the
|
||||
format is ignored, but the keys used in it are added to the JSON
|
||||
output. See
|
||||
https://borgbackup.readthedocs.io/en/stable/usage/list.html for
|
||||
details.
|
||||
example: "{path}{extra}{NL}"
|
||||
archive_list_format:
|
||||
type: string
|
||||
description: |
|
||||
Borg format for the archives listing of the "repo-list" action.
|
||||
Corresponds to the "--format" flag. Defaults to "{archive:<36}
|
||||
{time} [{id}]{NL}". With "--json", the form of the format is
|
||||
ignored, but the keys used in it are added to the JSON output. See
|
||||
https://borgbackup.readthedocs.io/en/stable/usage/list.html for
|
||||
details.
|
||||
example: "{archive}{NL}"
|
||||
relocated_repo_access_is_ok:
|
||||
type: boolean
|
||||
description: |
|
||||
@@ -998,6 +1021,8 @@ properties:
|
||||
type: string
|
||||
description: |
|
||||
Python format string used for log messages written to the log file.
|
||||
See https://docs.python.org/3/library/logging.html (and specifically
|
||||
the LogRecord attributes with "{}-formatting") for details.
|
||||
example: "[{asctime}] {levelname}: {prefix}{message}"
|
||||
monitoring_verbosity:
|
||||
type: integer
|
||||
@@ -1694,6 +1719,20 @@ properties:
|
||||
description: |
|
||||
Port to restore to. Defaults to the "port" option.
|
||||
example: 5433
|
||||
socket_path:
|
||||
type: string
|
||||
description: |
|
||||
Path of a Unix socket to connect to instead of a remote
|
||||
hostname. Ignored when "hostname" is set and not
|
||||
"localhost". Defaults to "/run/mysqld/mysqld.sock".
|
||||
example: database.example.org
|
||||
restore_socket_path:
|
||||
type: string
|
||||
description: |
|
||||
Path of a Unix socket to connect to instead of a remote
|
||||
hostname. Ignored when "hostname" is set and not
|
||||
"localhost". Defaults to the "socket_path" option.
|
||||
example: database.example.org
|
||||
username:
|
||||
type: string
|
||||
description: |
|
||||
@@ -1757,18 +1796,21 @@ properties:
|
||||
mariadb_dump_command:
|
||||
type: string
|
||||
description: |
|
||||
Command to use instead of "mariadb-dump". This can be
|
||||
used to run a specific mariadb_dump version (e.g., one
|
||||
inside a running container). If you run it from within a
|
||||
container, make sure to mount the path in the
|
||||
"user_runtime_directory" option from the host into the
|
||||
container at the same location. Defaults to
|
||||
"mariadb-dump".
|
||||
Command to use instead of "mariadb-dump". But set any
|
||||
additional command-line flags in "options", not here.
|
||||
This command can be used to run a specific mariadb_dump
|
||||
version (e.g., one inside a running container). If you
|
||||
run it from within a container, make sure to mount the
|
||||
path in the "user_runtime_directory" option from the
|
||||
host into the container at the same location. Defaults
|
||||
to "mariadb-dump".
|
||||
example: docker exec mariadb_container mariadb-dump
|
||||
mariadb_command:
|
||||
type: string
|
||||
description: |
|
||||
Command to run instead of "mariadb". This can be used to
|
||||
Command to run instead of "mariadb". But set any
|
||||
additional command-line flags in "list_options" or
|
||||
"restore_options", not here. This command can be used to
|
||||
run a specific mariadb version (e.g., one inside a
|
||||
running container). Defaults to "mariadb".
|
||||
example: docker exec mariadb_container mariadb
|
||||
@@ -1885,6 +1927,20 @@ properties:
|
||||
description: |
|
||||
Port to restore to. Defaults to the "port" option.
|
||||
example: 5433
|
||||
socket_path:
|
||||
type: string
|
||||
description: |
|
||||
Path of a Unix socket to connect to instead of a remote
|
||||
hostname. Ignored when "hostname" is set and not
|
||||
"localhost". Defaults to "/run/mysqld/mysqld.sock".
|
||||
example: database.example.org
|
||||
restore_socket_path:
|
||||
type: string
|
||||
description: |
|
||||
Path of a Unix socket to connect to instead of a remote
|
||||
hostname. Ignored when "hostname" is set and not
|
||||
"localhost". Defaults to the "socket_path" option.
|
||||
example: database.example.org
|
||||
username:
|
||||
type: string
|
||||
description: |
|
||||
@@ -1948,17 +2004,21 @@ properties:
|
||||
mysql_dump_command:
|
||||
type: string
|
||||
description: |
|
||||
Command to use instead of "mysqldump". This can be used
|
||||
to run a specific mysql_dump version (e.g., one inside a
|
||||
running container). If you run it from within a
|
||||
container, make sure to mount the path in the
|
||||
"user_runtime_directory" option from the host into the
|
||||
container at the same location. Defaults to "mysqldump".
|
||||
Command to use instead of "mysqldump". But set any
|
||||
additional command-line flags in "options", not here.
|
||||
This command can be used to run a specific mysql_dump
|
||||
version (e.g., one inside a running container). If you
|
||||
run it from within a container, make sure to mount the
|
||||
path in the "user_runtime_directory" option from the
|
||||
host into the container at the same location. Defaults
|
||||
to "mysqldump".
|
||||
example: docker exec mysql_container mysqldump
|
||||
mysql_command:
|
||||
type: string
|
||||
description: |
|
||||
Command to run instead of "mysql". This can be used to
|
||||
Command to run instead of "mysql". But set any
|
||||
additional command-line flags in "list_options" or
|
||||
"restore_options", not here. This command can be used to
|
||||
run a specific mysql version (e.g., one inside a running
|
||||
container). Defaults to "mysql".
|
||||
example: docker exec mysql_container mysql
|
||||
@@ -3172,6 +3232,14 @@ properties:
|
||||
description: |
|
||||
Command to use instead of "keepassxc-cli".
|
||||
example: /usr/local/bin/keepassxc-cli
|
||||
ask_for_password:
|
||||
type: boolean
|
||||
description: |
|
||||
Whether keepassxc-cli should prompt the user for a password.
|
||||
Disabling this is only really useful if you're unlocking
|
||||
your KeePassXC database with a key file instead of a
|
||||
password. Defaults to true.
|
||||
example: false
|
||||
key_file:
|
||||
type: string
|
||||
description: |
|
||||
|
||||
@@ -29,6 +29,7 @@ def load_credential(hook_config, config, credential_parameters):
|
||||
command = (
|
||||
tuple(shlex.split((hook_config or {}).get('keepassxc_cli_command', 'keepassxc-cli')))
|
||||
+ ('show', '--show-protected', '--attributes', 'Password')
|
||||
+ (('--no-password',) if not (hook_config or {}).get('ask_for_password', True) else ())
|
||||
+ (
|
||||
('--key-file', hook_config['key_file'])
|
||||
if hook_config and hook_config.get('key_file')
|
||||
|
||||
@@ -23,33 +23,24 @@ def use_streaming(hook_config, config): # pragma: no cover
|
||||
return False
|
||||
|
||||
|
||||
BTRFS_SUBVOLUME_INODE_NUMBER = 256
|
||||
|
||||
|
||||
@functools.cache
|
||||
def path_is_a_subvolume(btrfs_command, path):
|
||||
def path_is_a_subvolume(path):
|
||||
'''
|
||||
Given a btrfs command and a path, return whether the path is a Btrfs subvolume. Return False if
|
||||
the btrfs command errors, which probably indicates there isn't a containing Btrfs subvolume for
|
||||
the given path.
|
||||
Given a path, return whether it is a Btrfs subvolume. Return False if the path doesn't exist.
|
||||
|
||||
As a performance optimization, multiple calls to this function with the same arguments are
|
||||
cached.
|
||||
As a performance optimization, multiple calls to this function with the same path are cached.
|
||||
'''
|
||||
if path == os.environ.get('BTRFS_TEST_SUBVOLUME_PATH'): # pragma: no cover
|
||||
return True
|
||||
|
||||
try:
|
||||
borgmatic.execute.execute_command(
|
||||
(
|
||||
*btrfs_command.split(' '),
|
||||
'subvolume',
|
||||
'show',
|
||||
path,
|
||||
),
|
||||
output_log_level=None,
|
||||
close_fds=True,
|
||||
)
|
||||
# An error from the command (probably) indicates that the path is not actually a subvolume.
|
||||
except subprocess.CalledProcessError:
|
||||
return os.stat(path).st_ino == BTRFS_SUBVOLUME_INODE_NUMBER
|
||||
except FileNotFoundError:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@functools.cache
|
||||
def get_subvolume_property(btrfs_command, subvolume_path, property_name):
|
||||
@@ -99,7 +90,7 @@ def get_containing_subvolume_path(btrfs_command, path):
|
||||
path,
|
||||
*tuple(str(ancestor) for ancestor in pathlib.PurePath(path).parents),
|
||||
):
|
||||
if not path_is_a_subvolume(btrfs_command, candidate_path):
|
||||
if not path_is_a_subvolume(candidate_path):
|
||||
continue
|
||||
|
||||
try:
|
||||
@@ -146,8 +137,8 @@ Subvolume = collections.namedtuple('Subvolume', ('path', 'contained_patterns'),
|
||||
def get_subvolumes(btrfs_command, patterns):
|
||||
'''
|
||||
Given a Btrfs command to run and a sequence of configured patterns, find the intersection
|
||||
between the current Btrfs filesystem and subvolume paths and the paths of any patterns. The
|
||||
idea is that these pattern paths represent the requested subvolumes to snapshot.
|
||||
between the current Btrfs filesystem/subvolume paths and the paths of any patterns. The idea is
|
||||
that these pattern paths represent the requested subvolumes to snapshot.
|
||||
|
||||
Only include subvolumes that contain at least one root pattern sourced from borgmatic
|
||||
configuration (as opposed to generated elsewhere in borgmatic).
|
||||
@@ -180,7 +171,7 @@ def get_subvolumes(btrfs_command, patterns):
|
||||
return tuple(sorted(subvolumes, key=lambda subvolume: subvolume.path))
|
||||
|
||||
|
||||
BORGMATIC_SNAPSHOT_PREFIX = '.borgmatic-snapshot-'
|
||||
BORGMATIC_SNAPSHOT_PREFIX = '.borgmatic-snapshot'
|
||||
|
||||
|
||||
def make_snapshot_path(subvolume_path):
|
||||
@@ -189,7 +180,7 @@ def make_snapshot_path(subvolume_path):
|
||||
'''
|
||||
return os.path.join(
|
||||
subvolume_path,
|
||||
f'{BORGMATIC_SNAPSHOT_PREFIX}{os.getpid()}',
|
||||
f'{BORGMATIC_SNAPSHOT_PREFIX}',
|
||||
# Included so that the snapshot ends up in the Borg archive at the "original" subvolume path.
|
||||
) + subvolume_path.rstrip(os.path.sep)
|
||||
|
||||
@@ -202,16 +193,16 @@ def make_snapshot_exclude_pattern(subvolume_path): # pragma: no cover
|
||||
directory within the snapshot itself. For instance, if you have a Btrfs subvolume at /mnt and
|
||||
make a snapshot of it at:
|
||||
|
||||
/mnt/.borgmatic-snapshot-1234/mnt
|
||||
/mnt/.borgmatic-snapshot/mnt
|
||||
|
||||
... then the snapshot itself will have an empty directory at:
|
||||
|
||||
/mnt/.borgmatic-snapshot-1234/mnt/.borgmatic-snapshot-1234
|
||||
/mnt/.borgmatic-snapshot/mnt/.borgmatic-snapshot
|
||||
|
||||
So to prevent that from ending up in the Borg archive, this function produces an exclude pattern
|
||||
to exclude that path.
|
||||
'''
|
||||
snapshot_directory = f'{BORGMATIC_SNAPSHOT_PREFIX}{os.getpid()}'
|
||||
snapshot_directory = f'{BORGMATIC_SNAPSHOT_PREFIX}'
|
||||
|
||||
return borgmatic.borg.pattern.Pattern(
|
||||
os.path.join(
|
||||
@@ -244,7 +235,7 @@ def make_borg_snapshot_pattern(subvolume_path, pattern):
|
||||
|
||||
rewritten_path = initial_caret + os.path.join(
|
||||
subvolume_path,
|
||||
f'{BORGMATIC_SNAPSHOT_PREFIX}{os.getpid()}',
|
||||
f'{BORGMATIC_SNAPSHOT_PREFIX}',
|
||||
# Use the Borg 1.4+ "slashdot" hack to prevent the snapshot path prefix from getting
|
||||
# included in the archive—but only if there's not already a slashdot hack present in the
|
||||
# pattern.
|
||||
@@ -274,7 +265,6 @@ def snapshot_subvolume(btrfs_command, subvolume_path, snapshot_path): # pragma:
|
||||
*btrfs_command.split(' '),
|
||||
'subvolume',
|
||||
'snapshot',
|
||||
'-r', # Read-only.
|
||||
subvolume_path,
|
||||
snapshot_path,
|
||||
),
|
||||
@@ -329,15 +319,9 @@ def dump_data_sources(
|
||||
|
||||
snapshot_subvolume(btrfs_command, subvolume.path, snapshot_path)
|
||||
|
||||
last_contained_pattern_index = borgmatic.hooks.data_source.config.get_last_pattern_index(
|
||||
patterns, subvolume.contained_patterns
|
||||
)
|
||||
|
||||
for pattern in subvolume.contained_patterns:
|
||||
snapshot_pattern = make_borg_snapshot_pattern(subvolume.path, pattern)
|
||||
borgmatic.hooks.data_source.config.replace_pattern(
|
||||
patterns, pattern, snapshot_pattern, last_contained_pattern_index
|
||||
)
|
||||
borgmatic.hooks.data_source.config.replace_pattern(patterns, pattern, snapshot_pattern)
|
||||
|
||||
borgmatic.hooks.data_source.config.inject_pattern(
|
||||
patterns, make_snapshot_exclude_pattern(subvolume.path)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import contextlib
|
||||
import json
|
||||
import logging
|
||||
import shutil
|
||||
@@ -106,18 +105,20 @@ def get_ip_from_container(container):
|
||||
)
|
||||
|
||||
|
||||
def inject_pattern(patterns, data_source_pattern):
|
||||
def inject_pattern(patterns, data_source_pattern, override_excludes=True):
|
||||
'''
|
||||
Given a list of borgmatic.borg.pattern.Pattern instances representing the configured patterns,
|
||||
insert the given data source pattern at the start of the list. The idea is that borgmatic is
|
||||
injecting its own custom pattern specific to a data source hook into the user's configured
|
||||
patterns so that the hook's data gets included in the backup.
|
||||
|
||||
As part of this injection, if the data source pattern is a root pattern, also insert an
|
||||
"include" version of the given root pattern, in an attempt to preempt any of the user's
|
||||
configured exclude patterns that may follow.
|
||||
As part of this injection, if the data source pattern is a root pattern and override_excludes is
|
||||
True, also insert an "include" version of the given root pattern, in an attempt to preempt any
|
||||
of the user's configured exclude patterns that may follow. The is to support use cases like
|
||||
borgmatic injecting its own patterns for things like database dumps or bootstrap metadata, where
|
||||
we don't want them to get accidentally excluded.
|
||||
'''
|
||||
if data_source_pattern.type == borgmatic.borg.pattern.Pattern_type.ROOT:
|
||||
if data_source_pattern.type == borgmatic.borg.pattern.Pattern_type.ROOT and override_excludes:
|
||||
patterns.insert(
|
||||
0,
|
||||
borgmatic.borg.pattern.Pattern(
|
||||
@@ -132,77 +133,21 @@ def inject_pattern(patterns, data_source_pattern):
|
||||
patterns.insert(0, data_source_pattern)
|
||||
|
||||
|
||||
def get_last_pattern_index(patterns, patterns_subset):
|
||||
'''
|
||||
Given a sequence of all patterns and a subset of those patterns, find each subset pattern in the
|
||||
all patterns sequence and return the highest (last) index.
|
||||
'''
|
||||
last_pattern_index = 0
|
||||
|
||||
for pattern in patterns_subset:
|
||||
with contextlib.suppress(ValueError):
|
||||
last_pattern_index = max(patterns.index(pattern), last_pattern_index)
|
||||
|
||||
return last_pattern_index
|
||||
|
||||
|
||||
def replace_pattern(
|
||||
patterns, pattern_to_replace, data_source_pattern, last_contained_pattern_index
|
||||
):
|
||||
def replace_pattern(patterns, pattern_to_replace, data_source_pattern):
|
||||
'''
|
||||
Given a list of borgmatic.borg.pattern.Pattern instances representing the configured patterns,
|
||||
replace the given pattern with the given data source pattern. The idea is that borgmatic is
|
||||
replacing a configured pattern with its own modified pattern specific to a data source hook so
|
||||
that the hook's data gets included in the backup.
|
||||
|
||||
As part of this replacement, if the data source pattern is a root pattern, also insert an
|
||||
"include" version of the given root pattern right after the given last contained pattern index
|
||||
in an attempt to preempt any of the user's configured global exclude patterns that may follow.
|
||||
But we don't want to preempt any intentional partial excludes of the data source pattern itself,
|
||||
which is why the include goes after the last contained pattern index.
|
||||
|
||||
For instance, let's say that the patterns are effectively:
|
||||
|
||||
R /foo
|
||||
R /bar
|
||||
- /bar/.cache
|
||||
R /baz
|
||||
- **
|
||||
|
||||
... and "R /bar" is the pattern to replace, data source pattern is "R /bar/snapshot", and the
|
||||
last contained pattern index is 2 (corresponding to "- /bar/.cache"). The resulting patterns
|
||||
after calling this function would be:
|
||||
|
||||
R /foo
|
||||
R /bar/snapshot
|
||||
- /bar/snapshot/.cache
|
||||
+ /bar/snapshot
|
||||
R /baz
|
||||
- **
|
||||
|
||||
Note that the positioning of "+ /bar/snapshot" means that it overrides the "- **" global exclude
|
||||
but not the "- /bar/snapshot/.cache" contained pattern exclude.
|
||||
|
||||
If the pattern to replace can't be found in the given patterns, then just inject the data source
|
||||
pattern at the start of the list.
|
||||
'''
|
||||
try:
|
||||
index = patterns.index(pattern_to_replace)
|
||||
except ValueError:
|
||||
inject_pattern(patterns, data_source_pattern)
|
||||
inject_pattern(patterns, data_source_pattern, override_excludes=False)
|
||||
|
||||
return
|
||||
|
||||
patterns[index] = data_source_pattern
|
||||
|
||||
if data_source_pattern.type == borgmatic.borg.pattern.Pattern_type.ROOT:
|
||||
patterns.insert(
|
||||
last_contained_pattern_index + 1,
|
||||
borgmatic.borg.pattern.Pattern(
|
||||
path=data_source_pattern.path,
|
||||
type=borgmatic.borg.pattern.Pattern_type.INCLUDE,
|
||||
style=data_source_pattern.style,
|
||||
device=data_source_pattern.device,
|
||||
source=borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -269,10 +269,6 @@ def dump_data_sources(
|
||||
snapshot_mount_path,
|
||||
)
|
||||
|
||||
last_contained_pattern_index = borgmatic.hooks.data_source.config.get_last_pattern_index(
|
||||
patterns, logical_volume.contained_patterns
|
||||
)
|
||||
|
||||
for pattern in logical_volume.contained_patterns:
|
||||
snapshot_pattern = make_borg_snapshot_pattern(
|
||||
pattern,
|
||||
@@ -280,9 +276,7 @@ def dump_data_sources(
|
||||
normalized_runtime_directory,
|
||||
)
|
||||
|
||||
borgmatic.hooks.data_source.config.replace_pattern(
|
||||
patterns, pattern, snapshot_pattern, last_contained_pattern_index
|
||||
)
|
||||
borgmatic.hooks.data_source.config.replace_pattern(patterns, pattern, snapshot_pattern)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
@@ -132,6 +132,8 @@ def database_names_to_dump(database, config, username, password, environment, dr
|
||||
extra_options, defaults_extra_filename = parse_extra_options(database.get('list_options'))
|
||||
password_transport = database.get('password_transport', 'pipe')
|
||||
hostname = database_config.resolve_database_option('hostname', database)
|
||||
socket_path = database.get('socket_path')
|
||||
|
||||
show_command = (
|
||||
mariadb_show_command
|
||||
+ (
|
||||
@@ -143,6 +145,7 @@ def database_names_to_dump(database, config, username, password, environment, dr
|
||||
+ (('--host', hostname) if hostname else ())
|
||||
+ (('--port', str(database['port'])) if 'port' in database else ())
|
||||
+ (('--protocol', 'tcp') if hostname or 'port' in database else ())
|
||||
+ (('--socket', socket_path) if socket_path else ())
|
||||
+ (('--user', username) if username and password_transport == 'environment' else ())
|
||||
+ (('--ssl',) if database.get('tls') is True else ())
|
||||
+ (('--skip-ssl',) if database.get('tls') is False else ())
|
||||
@@ -155,7 +158,11 @@ def database_names_to_dump(database, config, username, password, environment, dr
|
||||
if skip_names:
|
||||
logger.debug(f'Skipping database names: {", ".join(skip_names)}')
|
||||
|
||||
show_output = execute_command_and_capture_output(show_command, environment=environment)
|
||||
show_output = execute_command_and_capture_output(
|
||||
show_command,
|
||||
environment=environment,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
)
|
||||
|
||||
return tuple(
|
||||
show_name
|
||||
@@ -209,6 +216,8 @@ def execute_dump_command(
|
||||
extra_options, defaults_extra_filename = parse_extra_options(database.get('options'))
|
||||
password_transport = database.get('password_transport', 'pipe')
|
||||
hostname = database_config.resolve_database_option('hostname', database)
|
||||
socket_path = database.get('socket_path')
|
||||
|
||||
dump_command = (
|
||||
mariadb_dump_command
|
||||
+ (
|
||||
@@ -218,9 +227,11 @@ def execute_dump_command(
|
||||
)
|
||||
+ extra_options
|
||||
+ (('--add-drop-database',) if database.get('add_drop_database', True) else ())
|
||||
+ ('--single-transaction',)
|
||||
+ (('--host', hostname) if hostname else ())
|
||||
+ (('--port', str(database['port'])) if 'port' in database else ())
|
||||
+ (('--protocol', 'tcp') if hostname or 'port' in database else ())
|
||||
+ (('--socket', socket_path) if socket_path else ())
|
||||
+ (('--user', username) if username and password_transport == 'environment' else ())
|
||||
+ (('--ssl',) if database.get('tls') is True else ())
|
||||
+ (('--skip-ssl',) if database.get('tls') is False else ())
|
||||
@@ -239,6 +250,7 @@ def execute_dump_command(
|
||||
dump_command,
|
||||
environment=environment,
|
||||
run_to_completion=False,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
)
|
||||
|
||||
|
||||
@@ -443,6 +455,7 @@ def restore_data_source_dump(
|
||||
port = database_config.resolve_database_option(
|
||||
'port', data_source, connection_params, restore=True
|
||||
)
|
||||
socket_path = database_config.resolve_database_option('socket_path', data_source, restore=True)
|
||||
tls = database_config.resolve_database_option('tls', data_source, restore=True)
|
||||
username = borgmatic.hooks.credential.parse.resolve_credential(
|
||||
database_config.resolve_database_option(
|
||||
@@ -462,6 +475,7 @@ def restore_data_source_dump(
|
||||
)
|
||||
extra_options, defaults_extra_filename = parse_extra_options(data_source.get('restore_options'))
|
||||
password_transport = data_source.get('password_transport', 'pipe')
|
||||
|
||||
restore_command = (
|
||||
mariadb_restore_command
|
||||
+ (
|
||||
@@ -474,6 +488,7 @@ def restore_data_source_dump(
|
||||
+ (('--host', hostname) if hostname else ())
|
||||
+ (('--port', str(port)) if port else ())
|
||||
+ (('--protocol', 'tcp') if hostname or port else ())
|
||||
+ (('--socket', socket_path) if socket_path else ())
|
||||
+ (('--user', username) if username and password_transport == 'environment' else ())
|
||||
+ (('--ssl',) if tls is True else ())
|
||||
+ (('--skip-ssl',) if tls is False else ())
|
||||
@@ -495,4 +510,5 @@ def restore_data_source_dump(
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment=environment,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
)
|
||||
|
||||
@@ -90,11 +90,20 @@ def dump_data_sources(
|
||||
|
||||
if dump_format == 'directory':
|
||||
dump.create_parent_directory_for_dump(dump_filename)
|
||||
execute_command(command, shell=True) # noqa: S604
|
||||
execute_command( # noqa: S604
|
||||
command,
|
||||
shell=True,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
)
|
||||
else:
|
||||
dump.create_named_pipe_for_dump(dump_filename)
|
||||
processes.append(
|
||||
execute_command(command, shell=True, run_to_completion=False), # noqa: S604
|
||||
execute_command( # noqa: S604
|
||||
command,
|
||||
shell=True,
|
||||
run_to_completion=False,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
),
|
||||
)
|
||||
|
||||
if not dry_run:
|
||||
@@ -273,6 +282,7 @@ def restore_data_source_dump(
|
||||
[extract_process] if extract_process else [],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout if extract_process else None,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -57,6 +57,8 @@ def database_names_to_dump(database, config, username, password, environment, dr
|
||||
)
|
||||
password_transport = database.get('password_transport', 'pipe')
|
||||
hostname = database_config.resolve_database_option('hostname', database)
|
||||
socket_path = database.get('socket_path')
|
||||
|
||||
show_command = (
|
||||
mysql_show_command
|
||||
+ (
|
||||
@@ -72,6 +74,7 @@ def database_names_to_dump(database, config, username, password, environment, dr
|
||||
+ (('--host', hostname) if hostname else ())
|
||||
+ (('--port', str(database['port'])) if 'port' in database else ())
|
||||
+ (('--protocol', 'tcp') if hostname or 'port' in database else ())
|
||||
+ (('--socket', socket_path) if socket_path else ())
|
||||
+ (('--user', username) if username and password_transport == 'environment' else ())
|
||||
+ (('--ssl',) if database.get('tls') is True else ())
|
||||
+ (('--skip-ssl',) if database.get('tls') is False else ())
|
||||
@@ -84,7 +87,11 @@ def database_names_to_dump(database, config, username, password, environment, dr
|
||||
if skip_names:
|
||||
logger.debug(f'Skipping database names: {", ".join(skip_names)}')
|
||||
|
||||
show_output = execute_command_and_capture_output(show_command, environment=environment)
|
||||
show_output = execute_command_and_capture_output(
|
||||
show_command,
|
||||
environment=environment,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
)
|
||||
|
||||
return tuple(
|
||||
show_name
|
||||
@@ -136,6 +143,8 @@ def execute_dump_command(
|
||||
)
|
||||
password_transport = database.get('password_transport', 'pipe')
|
||||
hostname = database_config.resolve_database_option('hostname', database)
|
||||
socket_path = database.get('socket_path')
|
||||
|
||||
dump_command = (
|
||||
mysql_dump_command
|
||||
+ (
|
||||
@@ -149,9 +158,11 @@ def execute_dump_command(
|
||||
)
|
||||
+ extra_options
|
||||
+ (('--add-drop-database',) if database.get('add_drop_database', True) else ())
|
||||
+ ('--single-transaction',)
|
||||
+ (('--host', hostname) if hostname else ())
|
||||
+ (('--port', str(database['port'])) if 'port' in database else ())
|
||||
+ (('--protocol', 'tcp') if hostname or 'port' in database else ())
|
||||
+ (('--socket', socket_path) if socket_path else ())
|
||||
+ (('--user', username) if username and password_transport == 'environment' else ())
|
||||
+ (('--ssl',) if database.get('tls') is True else ())
|
||||
+ (('--skip-ssl',) if database.get('tls') is False else ())
|
||||
@@ -170,6 +181,7 @@ def execute_dump_command(
|
||||
dump_command,
|
||||
environment=environment,
|
||||
run_to_completion=False,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
)
|
||||
|
||||
|
||||
@@ -374,6 +386,7 @@ def restore_data_source_dump(
|
||||
port = database_config.resolve_database_option(
|
||||
'port', data_source, connection_params, restore=True
|
||||
)
|
||||
socket_path = database_config.resolve_database_option('socket_path', data_source, restore=True)
|
||||
tls = database_config.resolve_database_option('tls', data_source, restore=True)
|
||||
username = borgmatic.hooks.credential.parse.resolve_credential(
|
||||
database_config.resolve_database_option(
|
||||
@@ -395,6 +408,7 @@ def restore_data_source_dump(
|
||||
borgmatic.hooks.data_source.mariadb.parse_extra_options(data_source.get('restore_options'))
|
||||
)
|
||||
password_transport = data_source.get('password_transport', 'pipe')
|
||||
|
||||
restore_command = (
|
||||
mysql_restore_command
|
||||
+ (
|
||||
@@ -411,6 +425,7 @@ def restore_data_source_dump(
|
||||
+ (('--host', hostname) if hostname else ())
|
||||
+ (('--port', str(port)) if port else ())
|
||||
+ (('--protocol', 'tcp') if hostname or port else ())
|
||||
+ (('--socket', socket_path) if socket_path else ())
|
||||
+ (('--user', username) if username and password_transport == 'environment' else ())
|
||||
+ (('--ssl',) if tls is True else ())
|
||||
+ (('--skip-ssl',) if tls is False else ())
|
||||
@@ -432,4 +447,5 @@ def restore_data_source_dump(
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment=environment,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
)
|
||||
|
||||
@@ -103,7 +103,11 @@ def database_names_to_dump(database, config, environment, dry_run):
|
||||
+ (tuple(database['list_options'].split(' ')) if 'list_options' in database else ())
|
||||
)
|
||||
logger.debug('Querying for "all" PostgreSQL databases to dump')
|
||||
list_output = execute_command_and_capture_output(list_command, environment=environment)
|
||||
list_output = execute_command_and_capture_output(
|
||||
list_command,
|
||||
environment=environment,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
)
|
||||
|
||||
return tuple(
|
||||
row[0]
|
||||
@@ -245,6 +249,7 @@ def dump_data_sources(
|
||||
command,
|
||||
shell=True,
|
||||
environment=environment,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
)
|
||||
else:
|
||||
dump.create_named_pipe_for_dump(dump_filename)
|
||||
@@ -254,6 +259,7 @@ def dump_data_sources(
|
||||
shell=True,
|
||||
environment=environment,
|
||||
run_to_completion=False,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
),
|
||||
)
|
||||
|
||||
@@ -422,5 +428,10 @@ def restore_data_source_dump(
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout if extract_process else None,
|
||||
environment=environment,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
)
|
||||
execute_command(
|
||||
analyze_command,
|
||||
environment=environment,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
)
|
||||
execute_command(analyze_command, environment=environment)
|
||||
|
||||
@@ -86,6 +86,7 @@ def dump_data_sources(
|
||||
)
|
||||
command = (
|
||||
*sqlite_command,
|
||||
'-bail',
|
||||
shlex.quote(database_path),
|
||||
'.dump',
|
||||
'>',
|
||||
@@ -100,7 +101,12 @@ def dump_data_sources(
|
||||
|
||||
dump.create_named_pipe_for_dump(dump_filename)
|
||||
processes.append(
|
||||
execute_command(command, shell=True, run_to_completion=False), # noqa: S604
|
||||
execute_command( # noqa: S604
|
||||
command,
|
||||
shell=True,
|
||||
run_to_completion=False,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
),
|
||||
)
|
||||
|
||||
if not dry_run:
|
||||
@@ -196,7 +202,7 @@ def restore_data_source_dump(
|
||||
shlex.quote(part)
|
||||
for part in shlex.split(data_source.get('sqlite_restore_command') or 'sqlite3')
|
||||
)
|
||||
restore_command = (*sqlite_restore_command, shlex.quote(database_path))
|
||||
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.
|
||||
execute_command_with_processes(
|
||||
@@ -204,4 +210,5 @@ def restore_data_source_dump(
|
||||
[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=borgmatic.config.paths.get_working_directory(config),
|
||||
)
|
||||
|
||||
@@ -300,10 +300,6 @@ def dump_data_sources(
|
||||
snapshot_mount_path,
|
||||
)
|
||||
|
||||
last_contained_pattern_index = borgmatic.hooks.data_source.config.get_last_pattern_index(
|
||||
patterns, dataset.contained_patterns
|
||||
)
|
||||
|
||||
for pattern in dataset.contained_patterns:
|
||||
snapshot_pattern = make_borg_snapshot_pattern(
|
||||
pattern,
|
||||
@@ -311,9 +307,7 @@ def dump_data_sources(
|
||||
normalized_runtime_directory,
|
||||
)
|
||||
|
||||
borgmatic.hooks.data_source.config.replace_pattern(
|
||||
patterns, pattern, snapshot_pattern, last_contained_pattern_index
|
||||
)
|
||||
borgmatic.hooks.data_source.config.replace_pattern(patterns, pattern, snapshot_pattern)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import importlib
|
||||
import logging
|
||||
import pkgutil
|
||||
|
||||
import borgmatic.hooks.command
|
||||
import borgmatic.hooks.credential
|
||||
import borgmatic.hooks.data_source
|
||||
import borgmatic.hooks.monitoring
|
||||
|
||||
@@ -24,6 +24,7 @@ RUN npm install @11ty/eleventy \
|
||||
markdown-it-anchor \
|
||||
markdown-it-replace-link
|
||||
COPY --from=borgmatic /etc/borgmatic/* /source/docs/_includes/borgmatic/
|
||||
COPY --from=borgmatic /etc/borgmatic/options.json /source/docs/reference/configuration/index.json
|
||||
COPY --from=borgmatic /command-line/* /source/docs/_includes/borgmatic/command-line/
|
||||
COPY --from=borgmatic /contributors.html /source/docs/_includes/borgmatic/contributors.html
|
||||
COPY . /source
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: How to add preparation and cleanup steps to backups
|
||||
title: 🧹 How to add preparation and cleanup steps to backups
|
||||
eleventyNavigation:
|
||||
key: 🧹 Add preparation and cleanup steps
|
||||
parent: How-to guides
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: How to backup to a removable drive or an intermittent server
|
||||
title: 💾 How to backup to a removable drive or an intermittent server
|
||||
eleventyNavigation:
|
||||
key: 💾 Backup to a removable drive/server
|
||||
parent: How-to guides
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: How to backup your databases
|
||||
title: 🗄️ How to backup your databases
|
||||
eleventyNavigation:
|
||||
key: 🗄️ Backup your databases
|
||||
parent: How-to guides
|
||||
@@ -249,6 +249,10 @@ container separate from borgmatic.
|
||||
A similar approach can work with MySQL, using `mysql_dump_command` instead of
|
||||
`mariadb_dump_command` to run `mysqldump` in a container.
|
||||
|
||||
Note: With MariaDB and MySQL, use `options`, `list_options`, and/or
|
||||
`restore_options` to override command-line flags rather than putting flags into
|
||||
`mariadb_dump_command`, `mysql_dump_command`, etc.
|
||||
|
||||
|
||||
#### Database client in a temporary container
|
||||
|
||||
@@ -544,12 +548,13 @@ Or from the configuration file:
|
||||
postgresql_databases:
|
||||
- name: users
|
||||
hostname: database1.example.org
|
||||
restore_hostname: database1.example.org
|
||||
restore_hostname: database2.example.org
|
||||
restore_port: 5433
|
||||
restore_username: postgres
|
||||
restore_password: trustsome1
|
||||
```
|
||||
|
||||
|
||||
### Manual restoration
|
||||
|
||||
If you prefer to restore a database without the help of borgmatic, first
|
||||
@@ -654,9 +659,14 @@ configuration. Here's an example with MariaDB:
|
||||
```yaml
|
||||
mariadb_databases:
|
||||
- name: posts
|
||||
options: "--single-transaction --quick"
|
||||
options: "--single-transaction"
|
||||
```
|
||||
|
||||
<span class="minilink minilink-addedin">New in version 2.0.13</span> borgmatic
|
||||
passes `--single-transaction` to MariaDB/MySQL by default, and you no longer
|
||||
need to set that in `options:`.
|
||||
|
||||
|
||||
### borgmatic hangs during backup
|
||||
|
||||
See Limitations above about `read_special`. You may need to exclude certain
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: How to customize warnings and errors
|
||||
title: 💥 How to customize warnings and errors
|
||||
eleventyNavigation:
|
||||
key: 💥 Customize warnings/errors
|
||||
parent: How-to guides
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: How to deal with very large backups
|
||||
title: 📏 How to deal with very large backups
|
||||
eleventyNavigation:
|
||||
key: 📏 Deal with very large backups
|
||||
parent: How-to guides
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: How to develop on borgmatic
|
||||
title: 🏗️ How to develop on borgmatic
|
||||
eleventyNavigation:
|
||||
key: 🏗️ Develop on borgmatic
|
||||
parent: How-to guides
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: How to extract a backup
|
||||
title: 📤 How to extract a backup
|
||||
eleventyNavigation:
|
||||
key: 📤 Extract a backup
|
||||
parent: How-to guides
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: How to inspect your backups
|
||||
title: 🔎 How to inspect your backups
|
||||
eleventyNavigation:
|
||||
key: 🔎 Inspect your backups
|
||||
parent: How-to guides
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: How to make backups redundant
|
||||
title: ☁️ How to make backups redundant
|
||||
eleventyNavigation:
|
||||
key: ☁️ Make backups redundant
|
||||
parent: How-to guides
|
||||
@@ -40,10 +40,11 @@ Here's a way of visualizing what borgmatic does with the above configuration:
|
||||
This gives you redundancy of your data across repositories and even
|
||||
potentially across providers.
|
||||
|
||||
See [Borg repository URLs
|
||||
documentation](https://borgbackup.readthedocs.io/en/stable/usage/general.html#repository-urls)
|
||||
See [repositories
|
||||
documentation](https://torsion.org/borgmatic/reference/configuration/repositories/)
|
||||
for more information on how to specify local and remote repository paths.
|
||||
|
||||
|
||||
### Different options per repository
|
||||
|
||||
What if you want borgmatic to backup to multiple repositories—while also
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: How to make per-application backups
|
||||
title: 🗂️ How to make per-application backups
|
||||
eleventyNavigation:
|
||||
key: 🗂️ Make per-application backups
|
||||
parent: How-to guides
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: How to monitor your backups
|
||||
title: 🚨 How to monitor your backups
|
||||
eleventyNavigation:
|
||||
key: 🚨 Monitor your backups
|
||||
parent: How-to guides
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: How to provide your passwords
|
||||
title: 🔒 How to provide your passwords
|
||||
eleventyNavigation:
|
||||
key: 🔒 Provide your passwords
|
||||
parent: How-to guides
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: How to run arbitrary Borg commands
|
||||
title: 🔧 How to run arbitrary Borg commands
|
||||
eleventyNavigation:
|
||||
key: 🔧 Run arbitrary Borg commands
|
||||
parent: How-to guides
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: How to set up backups
|
||||
title: 📥 How to set up backups
|
||||
eleventyNavigation:
|
||||
key: 📥 Set up backups
|
||||
parent: How-to guides
|
||||
@@ -214,15 +214,20 @@ edits are valid.
|
||||
|
||||
Before you can create backups with borgmatic, you first need to create a Borg
|
||||
repository so you have a destination for your backup archives. (But skip this
|
||||
step if you already have a Borg repository.) To create a repository, run a
|
||||
command like the following with Borg 1.x:
|
||||
step if you already have a Borg repository.) To create a repository, assuming
|
||||
it's already [specified in borgmatic's
|
||||
configuration](https://torsion.org/borgmatic/reference/configuration/repositories/),
|
||||
run a command like the following with Borg 1.x:
|
||||
|
||||
```bash
|
||||
sudo borgmatic init --encryption repokey
|
||||
sudo borgmatic repo-create --encryption repokey
|
||||
```
|
||||
|
||||
<span class="minilink minilink-addedin">New in borgmatic version 1.9.0</span>
|
||||
Or, with Borg 2.x:
|
||||
<span class="minilink minilink-addedin">Prior to borgmatic version 1.9.0</span>
|
||||
The `repo-create` action was called `init`.
|
||||
|
||||
<span class="minilink minilink-addedin">With Borg version 2.x</span> Borg 2.x
|
||||
uses more specific encryption modes like `repokey-aes-ocb`. For example:
|
||||
|
||||
```bash
|
||||
sudo borgmatic repo-create --encryption repokey-aes-ocb
|
||||
@@ -350,48 +355,18 @@ Access](https://projects.torsion.org/borgmatic-collective/borgmatic/issues/293).
|
||||
|
||||
## Niceties
|
||||
|
||||
<a id="shell-completion"></a>
|
||||
<a id="bash"></a>
|
||||
<a id="fish"></a>
|
||||
|
||||
### Shell completion
|
||||
|
||||
borgmatic includes a shell completion script (currently only for Bash and Fish) to
|
||||
support tab-completing borgmatic command-line actions and flags. Depending on
|
||||
how you installed borgmatic, this may be enabled by default.
|
||||
### Command-line autocompletion
|
||||
|
||||
#### Bash
|
||||
borgmatic includes autocompletion scripts for various command-line shells to
|
||||
support tab-completing command-line actions and flags. See the [autocompletion
|
||||
documentation](https://torsion.org/borgmatic/reference/command-line/autocompletion/)
|
||||
for details.
|
||||
|
||||
If completions aren't enabled, start by installing the `bash-completion` Linux package or the
|
||||
[`bash-completion@2`](https://formulae.brew.sh/formula/bash-completion@2)
|
||||
macOS Homebrew formula. Then, install the shell completion script globally:
|
||||
|
||||
```bash
|
||||
sudo su -c "borgmatic --bash-completion > $(pkg-config --variable=completionsdir bash-completion)/borgmatic"
|
||||
```
|
||||
|
||||
If you don't have `pkg-config` installed, you can try the following path
|
||||
instead:
|
||||
|
||||
```bash
|
||||
sudo su -c "borgmatic --bash-completion > /usr/share/bash-completion/completions/borgmatic"
|
||||
```
|
||||
|
||||
Or, if you'd like to install the script for only the current user:
|
||||
|
||||
```bash
|
||||
mkdir --parents ~/.local/share/bash-completion/completions
|
||||
borgmatic --bash-completion > ~/.local/share/bash-completion/completions/borgmatic
|
||||
```
|
||||
|
||||
Finally, restart your shell (`exit` and open a new shell) so the completions
|
||||
take effect.
|
||||
|
||||
#### fish
|
||||
|
||||
To add completions for fish, install the completions file globally:
|
||||
|
||||
```fish
|
||||
borgmatic --fish-completion | sudo tee /usr/share/fish/vendor_completions.d/borgmatic.fish
|
||||
source /usr/share/fish/vendor_completions.d/borgmatic.fish
|
||||
```
|
||||
|
||||
### Colored output
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: How to snapshot your filesystems
|
||||
title: 📸 How to snapshot your filesystems
|
||||
eleventyNavigation:
|
||||
key: 📸 Snapshot your filesystems
|
||||
parent: How-to guides
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: How to upgrade borgmatic and Borg
|
||||
title: 📦 How to upgrade borgmatic and Borg
|
||||
eleventyNavigation:
|
||||
key: 📦 Upgrade borgmatic/Borg
|
||||
parent: How-to guides
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Actions
|
||||
title: 🎬 Actions
|
||||
eleventyNavigation:
|
||||
key: 🎬 Actions
|
||||
parent: 💻 Command-line
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
title: bash
|
||||
eleventyNavigation:
|
||||
key: bash
|
||||
parent: ➡️ Autocompletion
|
||||
---
|
||||
borgmatic includes an autocompletion script for
|
||||
[Bash](https://www.gnu.org/software/bash/) to support tab-completing
|
||||
command-line actions and flags. Depending on how you installed borgmatic, this
|
||||
may be enabled by default.
|
||||
|
||||
If completions aren't enabled, start by installing the `bash-completion` Linux
|
||||
package or the
|
||||
[`bash-completion@2`](https://formulae.brew.sh/formula/bash-completion@2) macOS
|
||||
Homebrew formula. Then, install the shell completion script globally:
|
||||
|
||||
```bash
|
||||
sudo su -c "borgmatic --bash-completion > $(pkg-config --variable=completionsdir bash-completion)/borgmatic"
|
||||
```
|
||||
|
||||
If you don't have `pkg-config` installed, you can try the following path
|
||||
instead:
|
||||
|
||||
```bash
|
||||
sudo su -c "borgmatic --bash-completion > /usr/share/bash-completion/completions/borgmatic"
|
||||
```
|
||||
|
||||
Or, if you'd like to install the script for only the current user:
|
||||
|
||||
```bash
|
||||
mkdir --parents ~/.local/share/bash-completion/completions
|
||||
borgmatic --bash-completion > ~/.local/share/bash-completion/completions/borgmatic
|
||||
```
|
||||
|
||||
Finally, restart your shell (`exit` and open a new shell) so the completions
|
||||
take effect.
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
title: fish
|
||||
eleventyNavigation:
|
||||
key: fish
|
||||
parent: ➡️ Autocompletion
|
||||
---
|
||||
borgmatic includes an autocompletion script for [fish
|
||||
shell](https://fishshell.com/) to support tab-completing command-line actions
|
||||
and flags. Depending on how you installed borgmatic, this may be enabled by
|
||||
default.
|
||||
|
||||
If completions aren't enabled, install the completions file globally:
|
||||
|
||||
```shell
|
||||
borgmatic --fish-completion | sudo tee /usr/share/fish/vendor_completions.d/borgmatic.fish
|
||||
source /usr/share/fish/vendor_completions.d/borgmatic.fish
|
||||
```
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
title: ➡️ Autocompletion
|
||||
eleventyNavigation:
|
||||
key: ➡️ Autocompletion
|
||||
parent: 💻 Command-line
|
||||
---
|
||||
|
||||
borgmatic includes autocompletion scripts for various command-line shells to
|
||||
support tab-completing command-line actions and flags. Depending on how you
|
||||
installed borgmatic, this may be enabled by default. Here are the supported
|
||||
shells and how to configure borgmatic autocompletion for each:
|
||||
|
||||
{{ collections.all | eleventyNavigation: "➡️ Autocompletion" | eleventyNavigationToHtml | replace: 'href="/reference/', 'href="/borgmatic/reference/' }}
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Command-line
|
||||
title: 💻 Command-line
|
||||
eleventyNavigation:
|
||||
key: 💻 Command-line
|
||||
parent: Reference guides
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Logging
|
||||
title: 🪵 Logging
|
||||
eleventyNavigation:
|
||||
key: 🪵 Logging
|
||||
parent: 💻 Command-line
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Overrides
|
||||
title: 🔄 Overrides
|
||||
eleventyNavigation:
|
||||
key: 🔄 Overrides
|
||||
parent: 💻 Command-line
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Archive name format
|
||||
title: 📛 Archive name format
|
||||
eleventyNavigation:
|
||||
key: 📛 Archive name format
|
||||
parent: ⚙️ Configuration
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Command hooks
|
||||
title: 🪝 Command hooks
|
||||
eleventyNavigation:
|
||||
key: 🪝 Command hooks
|
||||
parent: ⚙️ Configuration
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Consistency checks
|
||||
title: ✅ Consistency checks
|
||||
eleventyNavigation:
|
||||
key: ✅ Consistency checks
|
||||
parent: ⚙️ Configuration
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Constants
|
||||
title: 🟰 Constants
|
||||
eleventyNavigation:
|
||||
key: 🟰 Constants
|
||||
parent: ⚙️ Configuration
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Credentials
|
||||
title: 🔒 Credentials
|
||||
eleventyNavigation:
|
||||
key: 🔒 Credentials
|
||||
parent: ⚙️ Configuration
|
||||
|
||||
@@ -42,6 +42,9 @@ postgresql_databases:
|
||||
password: "{credential keepassxc /etc/keys.kdbx database}"
|
||||
```
|
||||
|
||||
|
||||
### Custom command
|
||||
|
||||
You can also optionally override the `keepassxc-cli` command that borgmatic calls to load
|
||||
passwords:
|
||||
|
||||
@@ -49,3 +52,49 @@ passwords:
|
||||
keepassxc:
|
||||
keepassxc_cli_command: /usr/local/bin/keepassxc-cli
|
||||
```
|
||||
|
||||
Another example:
|
||||
|
||||
```yaml
|
||||
keepassxc:
|
||||
keepassxc_cli_command: docker exec keepassxc keepassxc-cli
|
||||
```
|
||||
|
||||
### Key file
|
||||
|
||||
<span class="minilink minilink-addedin">New in version 2.0.0</span>KeePassXC
|
||||
supports unlocking a database with a separate [key
|
||||
file](https://keepassxc.org/docs/#faq-keyfile-howto) instead of or in addition
|
||||
to a password. To configure borgmatic for that, use the `key_file` option:
|
||||
|
||||
```yaml
|
||||
keepassxc:
|
||||
key_file: /path/to/keyfile
|
||||
```
|
||||
|
||||
<span class="minilink minilink-addedin">New in version 2.0.12</span>By default,
|
||||
keepassxc-cli prompts the user for the password to unlock a database. But if you
|
||||
only want to provide a key file to unlock your database and not a password, for
|
||||
instance to support unattended backups, use the `ask_for_password` option:
|
||||
|
||||
```yaml
|
||||
keepassxc:
|
||||
ask_for_password: false
|
||||
key_file: /path/to/keyfile
|
||||
```
|
||||
|
||||
### YubiKey
|
||||
|
||||
<span class="minilink minilink-addedin">New in version 2.0.0</span>KeePassXC
|
||||
also supports unlocking a database with the help of [a
|
||||
YubiKey](https://keepassxc.org/docs/#faq-yubikey-2fa). To configure borgmatic
|
||||
for that, use the `yubikey` option:
|
||||
|
||||
```yaml
|
||||
keepassxc:
|
||||
yubikey: 1:7370001
|
||||
```
|
||||
|
||||
The value here is the YubiKey slot number (e.g., `1` or `2`) and optional serial
|
||||
number (e.g., `7370001`) used to access the KeePassXC database. Join the two
|
||||
values with a colon, but omit the colon if you're leaving out the serial number.
|
||||
|
||||
@@ -60,7 +60,7 @@ Additionally, borgmatic rewrites the snapshot file paths so that they appear at
|
||||
their original subvolume locations in a Borg archive. For instance, if your
|
||||
subvolume path is `/var/subvolume`, then the snapshotted files will appear in an
|
||||
archive at `/var/subvolume` as well—even if borgmatic has to mount the snapshot
|
||||
somewhere in `/var/subvolume/.borgmatic-snapshot-1234/` to perform the backup.
|
||||
somewhere in `/var/subvolume/.borgmatic-snapshot/` to perform the backup.
|
||||
|
||||
<span class="minilink minilink-addedin">With Borg version 1.2 and
|
||||
earlier</span>Snapshotted files are instead stored at a path dependent on the
|
||||
@@ -70,18 +70,46 @@ temporary snapshot directory in use at the time the archive was created, as Borg
|
||||
|
||||
## Performance
|
||||
|
||||
<span class="minilink minilink-addedin">With Borg version 1.x</span> Because of
|
||||
the way that Btrfs snapshot paths change from one borgmatic invocation to the
|
||||
next, the [Borg file
|
||||
<span class="minilink minilink-addedin">New in borgmatic version 2.0.13, with
|
||||
Borg version 1.x</span> borgmatic uses consistent snapshot paths between
|
||||
invocations, so Btrfs snapshots are cached correctly. No configuration is
|
||||
necessary.
|
||||
|
||||
<span class="minilink minilink-addedin">Prior to borgmatic version 2.0.13, with
|
||||
Borg version 1.x</span> Because of the way that Btrfs snapshot paths change from
|
||||
one borgmatic invocation to the next, the [Borg file
|
||||
cache](https://borgbackup.readthedocs.io/en/stable/internals/data-structures.html#cache)
|
||||
will never get cache hits on snapshotted files. This makes backing up Btrfs
|
||||
never gets cache hits on snapshotted files. This makes backing up Btrfs
|
||||
snapshots a little slower than non-snapshotted files that have consistent paths.
|
||||
If this is an issue for you, upgrade to borgmatic to 2.0.13+.
|
||||
|
||||
<span class="minilink minilink-addedin">With Borg version 2.x</span> Even
|
||||
snapshotted files should get cache hits, because Borg 2.x is smarter about how
|
||||
it looks up file paths in its cache—it constructs the cache key with the path
|
||||
*as it's seen in the archive* (which is consistent across runs) rather than the
|
||||
full absolute source path (which changes).
|
||||
snapshotted files get cache hits, because Borg 2.x is smarter about how it looks
|
||||
up file paths in its cache—it constructs the cache key with the path *as it's
|
||||
seen in the archive* (which is consistent across runs) rather than the full
|
||||
absolute source path (which changes).
|
||||
|
||||
|
||||
## systemd settings
|
||||
|
||||
If you're using [systemd to run
|
||||
borgmatic](https://torsion.org/borgmatic/how-to/set-up-backups/#systemd), you
|
||||
may need to disable particular security settings like `CapabilityBoundingSet` to
|
||||
allow the Btrfs feature to work. See the comments in the sample systemd service
|
||||
file for details.
|
||||
|
||||
|
||||
## non-root user
|
||||
|
||||
<span class="minilink minilink-addedin">New in version 2.0.13</span> If you'd
|
||||
like borgmatic to snapshot a Btrfs subvolume when running as a non-root user,
|
||||
make the following changes to the subvolume in question:
|
||||
|
||||
1. `chown` the subvolume to be owned by your non-root user.
|
||||
2. Mount the subvolume with the `user_subvol_rm_allowed` mount option.
|
||||
|
||||
These changes allow the non-root user to create and delete snapshots of the
|
||||
subvolume, which is necessary for the borgmatic Btrfs hook to work.
|
||||
|
||||
|
||||
## Full configuration
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Data sources
|
||||
title: 🗄️ Data sources
|
||||
eleventyNavigation:
|
||||
key: 🗄️ Data sources
|
||||
parent: ⚙️ Configuration
|
||||
|
||||
@@ -114,6 +114,15 @@ the cache key with the path *as it's seen in the archive* (which is consistent
|
||||
across runs) rather than the full absolute source path (which can change).
|
||||
|
||||
|
||||
## systemd settings
|
||||
|
||||
If you're using [systemd to run
|
||||
borgmatic](https://torsion.org/borgmatic/how-to/set-up-backups/#systemd), you
|
||||
may need to disable particular security settings like `ProtectKernelModules`,
|
||||
`CapabilityBoundingSet`, and/or `PrivateDevices` to allow the LVM feature to
|
||||
work. See the comments in the sample systemd service file for details.
|
||||
|
||||
|
||||
## Full configuration
|
||||
|
||||
```yaml
|
||||
|
||||
@@ -91,7 +91,7 @@ cache](https://borgbackup.readthedocs.io/en/stable/internals/data-structures.htm
|
||||
may not get cache hits on snapshotted files. This makes backing up ZFS snapshots
|
||||
a little slower than non-snapshotted files that have consistent paths. You can
|
||||
mitigate this by setting a fixed [runtime
|
||||
directory](https://torsion.org/borgmatic/reference/configuration/runtime-directory/))
|
||||
directory](https://torsion.org/borgmatic/reference/configuration/runtime-directory/)
|
||||
(that's not located in `/tmp`). This allows borgmatic to use a consistent
|
||||
snapshot path from one run to the next, thereby resulting in Borg files cache
|
||||
hits.
|
||||
@@ -103,6 +103,15 @@ the cache key with the path *as it's seen in the archive* (which is consistent
|
||||
across runs) rather than the full absolute source path (which can change).
|
||||
|
||||
|
||||
## systemd settings
|
||||
|
||||
If you're using [systemd to run
|
||||
borgmatic](https://torsion.org/borgmatic/how-to/set-up-backups/#systemd), you
|
||||
may need to disable particular security settings like `CapabilityBoundingSet`
|
||||
and/or `PrivateDevices` to allow the ZFS feature to work. See the comments in
|
||||
the sample systemd service file for details.
|
||||
|
||||
|
||||
## Full configuration
|
||||
|
||||
```yaml
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Environment variables
|
||||
title: 💲 Environment variables
|
||||
eleventyNavigation:
|
||||
key: 💲 Environment variables
|
||||
parent: ⚙️ Configuration
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Includes
|
||||
title: ❗ Includes
|
||||
eleventyNavigation:
|
||||
key: ❗ Includes
|
||||
parent: ⚙️ Configuration
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
title: Configuration
|
||||
title: ⚙️ Configuration
|
||||
eleventyNavigation:
|
||||
key: ⚙️ Configuration
|
||||
parent: Reference guides
|
||||
order: 0
|
||||
---
|
||||
Below is a sample borgmatic configuration file including all available options
|
||||
for the [most recent version of
|
||||
Below is a sample borgmatic configuration snippet for every available option in
|
||||
the [most recent version of
|
||||
borgmatic](https://projects.torsion.org/borgmatic-collective/borgmatic/releases).
|
||||
This file is also [available for
|
||||
A full example configuration file is also [available for
|
||||
download](https://torsion.org/borgmatic/reference/config.yaml).
|
||||
|
||||
If you're using an older version of borgmatic, some of these options may not
|
||||
@@ -16,6 +16,11 @@ work, and you should instead [generate a sample configuration file specific to
|
||||
your borgmatic
|
||||
version](https://torsion.org/borgmatic/how-to/set-up-backups/#configuration).
|
||||
|
||||
<span data-pagefind-weight="6.0">
|
||||
{% for option_name in option_names %}
|
||||
### {{ option_name }} option
|
||||
```yaml
|
||||
{% include borgmatic/config.yaml %}
|
||||
{% include borgmatic/{{ option_name }}.yaml %}
|
||||
```
|
||||
{% endfor %}
|
||||
</span>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Monitoring
|
||||
title: 🚨 Monitoring
|
||||
eleventyNavigation:
|
||||
key: 🚨 Monitoring
|
||||
parent: ⚙️ Configuration
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Patterns and excludes
|
||||
title: ⛔ Patterns and excludes
|
||||
eleventyNavigation:
|
||||
key: ⛔ Patterns and excludes
|
||||
parent: ⚙️ Configuration
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
---
|
||||
title: 🗃️ Repositories
|
||||
eleventyNavigation:
|
||||
key: 🗃️ Repositories
|
||||
parent: ⚙️ Configuration
|
||||
---
|
||||
|
||||
Borg repositories are where your backups get stored. You can define them in
|
||||
borgmatic's configuration via the `repositories` option, something like:
|
||||
|
||||
```yaml
|
||||
repositories:
|
||||
- path: /path/to/first.borg
|
||||
label: first
|
||||
- path: /path/to/second.borg
|
||||
label: second
|
||||
```
|
||||
|
||||
Each repository has a `path` and an optional `label`. The [Borg repository URLs
|
||||
documentation](https://borgbackup.readthedocs.io/en/stable/usage/general.html#repository-urls)
|
||||
has examples of valid repositories paths, but see below for some
|
||||
borgmatic-specific examples.
|
||||
|
||||
The `label` shows up in [logged
|
||||
messages](https://torsion.org/borgmatic/reference/command-line/logging/) about
|
||||
the repository and also serves as a way to refer to the repository via
|
||||
the `--repository` flag on the command-line for supported
|
||||
[actions](https://torsion.org/borgmatic/reference/command-line/actions/).
|
||||
|
||||
When you run borgmatic's [`create`
|
||||
action](https://torsion.org/borgmatic/reference/command-line/actions/create/),
|
||||
it invokes Borg once for each configured repository in sequence. (So, not in
|
||||
parallel.) That means—in each repository—borgmatic creates a single new backup
|
||||
archive containing all of your [source
|
||||
directories](https://torsion.org/borgmatic/reference/configuration/patterns-and-excludes/).
|
||||
|
||||
|
||||
## SSH
|
||||
|
||||
Backing up to a remote server via
|
||||
[SSH](https://en.wikipedia.org/wiki/Secure_Shell) looks like:
|
||||
|
||||
```yaml
|
||||
repositories:
|
||||
- path: ssh://user@host:port/./absolute/path/to/repo
|
||||
```
|
||||
|
||||
Or relative to the remote user's home directory:
|
||||
|
||||
```yaml
|
||||
repositories:
|
||||
- path: ssh://user@host:port/~/relative/path/to/repo
|
||||
```
|
||||
|
||||
This assumes that you've already configured SSH access (e.g. public keys, known
|
||||
hosts, authorized hosts, etc.) outside of borgmatic and that Borg is installed
|
||||
on the server.
|
||||
|
||||
<span class="minilink minilink-addedin">With Borg version 2.x</span>The SSH
|
||||
syntax is a little different:
|
||||
|
||||
```yaml
|
||||
repositories:
|
||||
- path: ssh://user@host:port//absolute/path/to/repo
|
||||
```
|
||||
|
||||
Or relative to the remote user's home directory:
|
||||
|
||||
|
||||
```yaml
|
||||
repositories:
|
||||
- path: ssh://user@host:port/relative/path/to/repo
|
||||
```
|
||||
|
||||
Also see the [`ssh_command` configuration
|
||||
option](https://torsion.org/borgmatic/reference/configuration/) for overriding
|
||||
the path to the SSH binary or passing it custom flags. For example:
|
||||
|
||||
```yaml
|
||||
ssh_command: ssh -i /path/to/private/key
|
||||
```
|
||||
|
||||
|
||||
### SFTP
|
||||
|
||||
[SFTP](https://en.wikipedia.org/wiki/SSH_File_Transfer_Protocol) repositories
|
||||
work just like SSH repositories, but with `sftp://` substituted for `ssh://`.
|
||||
|
||||
|
||||
## Rclone
|
||||
|
||||
<span class="minilink minilink-addedin">New in Borg version 2.x</span> If you're
|
||||
using Borg 2, you can backup to repositories via [Rclone](https://rclone.org/),
|
||||
which supports a large number of [cloud
|
||||
providers](https://rclone.org/#providers). This means that Borg, via Rclone,
|
||||
backs up directly to a cloud provider without having to create an intermediate
|
||||
repository.
|
||||
|
||||
The borgmatic configuration for Rclone looks like:
|
||||
|
||||
```yaml
|
||||
repositories:
|
||||
- path: rclone:remote:path
|
||||
```
|
||||
|
||||
Note the lack of "`//`" after `rclone:`.
|
||||
|
||||
This configuration assumes that you've already [configured a corresponding
|
||||
Rclone remote](https://rclone.org/docs/).
|
||||
|
||||
|
||||
## S3 / B2
|
||||
|
||||
<span class="minilink minilink-addedin">New in Borg version 2.x</span> Borg 2
|
||||
supports storing repositories directly on [Amazon
|
||||
S3](https://aws.amazon.com/s3/), [Backblaze
|
||||
B2](https://www.backblaze.com/cloud-storage), or an S3-alike service, even
|
||||
without the use of Rclone or an intermediate repository. The configuration for
|
||||
that might look like one of the following:
|
||||
|
||||
```yaml
|
||||
repositories:
|
||||
- path: s3:access_key_id:access_key_secret@/bucket/path
|
||||
- path: b2:access_key_id:access_key_secret@schema://hostname:port/bucket/path
|
||||
```
|
||||
|
||||
Note the lack of "`//`" after `s3:` or `b2:`.
|
||||
|
||||
When selecting your cloud hosting provider, be aware that Amazon in particular
|
||||
has [financially
|
||||
supported](https://en.wikipedia.org/wiki/White_House_State_Ballroom) the Trump
|
||||
regime.
|
||||
|
||||
|
||||
## Related documentation
|
||||
|
||||
* [How to make backups redundant](https://torsion.org/borgmatic/how-to/make-backups-redundant/)
|
||||
* [How to provide your passwords](https://torsion.org/borgmatic/how-to/provide-your-passwords/)
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Runtime directory
|
||||
title: 📁 Runtime directory
|
||||
eleventyNavigation:
|
||||
key: 📁 Runtime directory
|
||||
parent: ⚙️ Configuration
|
||||
@@ -39,3 +39,28 @@ system temporary directories.
|
||||
borgmatic created temporary streaming database dumps within the `~/.borgmatic`
|
||||
directory by default. At that time, the path was configurable by the
|
||||
`borgmatic_source_directory` configuration option (now deprecated).
|
||||
|
||||
|
||||
## systemd-tmpfiles
|
||||
|
||||
If borgmatic's runtime directory is in `/tmp`, be aware that some systems may
|
||||
automatically delete `/tmp` files on a periodic basis, e.g. via
|
||||
[systemd-tmpfiles](https://www.freedesktop.org/software/systemd/man/251/systemd-tmpfiles.html).
|
||||
|
||||
One sign that this is happening is borgmatic erroring during cleanup with "No
|
||||
such file or directory" on the runtime directory path, indicating that
|
||||
borgmatic's runtime directory is getting deleted out from under it.
|
||||
|
||||
You can work around this by either excluding borgmatic's runtime directory from
|
||||
automatic systemd-tmpfiles management—or you can change borgmatic's runtime
|
||||
directory to not be in `/tmp` as described above.
|
||||
|
||||
Here's what a systemd-tmpfiles exclude for borgmatic might look like, for
|
||||
instance in an `/etc/tmpfiles.d/borgmatic.conf` file:
|
||||
|
||||
```
|
||||
x /tmp/borgmatic-*
|
||||
```
|
||||
|
||||
That tells systemd-tmpfiles to ignore borgmatic's runtime directory when
|
||||
automatically deleting paths in `/tmp`.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Source code
|
||||
title: 🐍 Source code
|
||||
eleventyNavigation:
|
||||
key: 🐍 Source code
|
||||
parent: Reference guides
|
||||
|
||||
Vendored
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 10 KiB |
+1
-1
@@ -1,6 +1,6 @@
|
||||
[project]
|
||||
name = "borgmatic"
|
||||
version = "2.0.11"
|
||||
version = "2.0.13"
|
||||
authors = [
|
||||
{ name="Dan Helfman", email="witten@torsion.org" },
|
||||
]
|
||||
|
||||
@@ -26,13 +26,16 @@ LockPersonality=true
|
||||
# But you can try setting it to "yes" for improved security if you don't use those features.
|
||||
MemoryDenyWriteExecute=no
|
||||
NoNewPrivileges=yes
|
||||
# Filesystem hooks like ZFS may not work unless PrivateDevices is disabled.
|
||||
# Filesystem hooks like ZFS and LVM may not work unless PrivateDevices is disabled.
|
||||
PrivateDevices=yes
|
||||
PrivateTmp=yes
|
||||
ProtectClock=yes
|
||||
ProtectControlGroups=yes
|
||||
ProtectHostname=yes
|
||||
ProtectKernelLogs=yes
|
||||
# You may need to disable this if you're using the LVM hook. But an alternative
|
||||
# might be to leave this enabled and preload the necessary kernel modules like
|
||||
# "dm_snapshot" at boot, so this service doesn't need to load them on demand.
|
||||
ProtectKernelModules=yes
|
||||
ProtectKernelTunables=yes
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 AF_NETLINK
|
||||
@@ -54,7 +57,9 @@ ProtectSystem=full
|
||||
# BindReadOnlyPaths=-/root/.ssh
|
||||
|
||||
# May interfere with running external programs within borgmatic hooks. This
|
||||
# includes, for instance, programs to snapshot filesystems (e.g. ZFS).
|
||||
# includes, for instance, programs to snapshot filesystems (e.g. ZFS, LVM, and
|
||||
# Btrfs). But rather than commenting this out entirely, one workaround may be to
|
||||
# add "CAP_SYS_ADMIN".
|
||||
CapabilityBoundingSet=CAP_DAC_READ_SEARCH CAP_NET_RAW
|
||||
|
||||
# Lower CPU and I/O priority.
|
||||
|
||||
@@ -4,7 +4,7 @@ Description=Run borgmatic backup
|
||||
[Timer]
|
||||
OnCalendar=daily
|
||||
Persistent=true
|
||||
RandomizedDelaySec=3h
|
||||
RandomizedDelaySec=10m
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
|
||||
@@ -2,10 +2,20 @@
|
||||
|
||||
set -e
|
||||
|
||||
docs_container_id=$(podman create "$IMAGE_NAME")
|
||||
podman cp $docs_container_id:/usr/share/nginx/html - > borgmatic-docs-dump.tar
|
||||
USER_PODMAN_SOCKET_PATH=/run/user/$UID/podman/podman.sock
|
||||
|
||||
if [ -e "$USER_PODMAN_SOCKET_PATH" ]; then
|
||||
export DOCKER_HOST="unix://$USER_PODMAN_SOCKET_PATH"
|
||||
export CONTAINER_SOCKET_PATH="$USER_PODMAN_SOCKET_PATH"
|
||||
fi
|
||||
|
||||
podman build --tag borgmatic-release-docs --file docs/Dockerfile .
|
||||
docs_container_id=$(podman create borgmatic-release-docs)
|
||||
podman cp "$docs_container_id":/usr/share/nginx/html - > borgmatic-docs-dump.tar
|
||||
tar xf borgmatic-docs-dump.tar
|
||||
rm borgmatic-docs-dump.tar
|
||||
mv html borgmatic-docs
|
||||
tar cfz borgmatic-docs.tar.gz borgmatic-docs
|
||||
podman rm --volumes $docs_container_id
|
||||
rm -f dist/borgmatic-docs.tar.gz
|
||||
tar cfz dist/borgmatic-docs.tar.gz borgmatic-docs
|
||||
rm -fr borgmatic-docs
|
||||
podman rm --volumes "$docs_container_id"
|
||||
|
||||
+26
-4
@@ -34,18 +34,40 @@ git push github $version
|
||||
# Build borgmatic and publish to pypi.
|
||||
rm -fr dist
|
||||
uv build
|
||||
twine upload -r pypi --username __token__ dist/borgmatic-*.tar.gz
|
||||
twine upload -r pypi --username __token__ dist/borgmatic-*-py3-none-any.whl
|
||||
tarball_path="dist/borgmatic-$version.tar.gz"
|
||||
wheel_path=$(ls dist/borgmatic-*-py3-none-any.whl)
|
||||
twine upload -r pypi --username __token__ "$tarball_path"
|
||||
twine upload -r pypi --username __token__ "$wheel_path"
|
||||
|
||||
# Build docs and extract HTML.
|
||||
scripts/export-docs-from-image
|
||||
docs_path=dist/borgmatic-docs.tar.gz
|
||||
|
||||
# Set release changelogs on projects.torsion.org and GitHub.
|
||||
release_changelog="$(cat NEWS | sed '/^$/q' | grep -v '^\S')"
|
||||
escaped_release_changelog="$(echo "$release_changelog" | sed -z 's/\n/\\n/g' | sed -z 's/\"/\\"/g')"
|
||||
curl --silent --request POST \
|
||||
release_id=$(curl --silent --request POST \
|
||||
"https://projects.torsion.org/api/v1/repos/borgmatic-collective/borgmatic/releases" \
|
||||
--header "Authorization: token $projects_token" \
|
||||
--header "Accept: application/json" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data "{\"body\": \"$escaped_release_changelog\", \"draft\": false, \"name\": \"borgmatic $version\", \"prerelease\": false, \"tag_name\": \"$version\"}"
|
||||
--data "{\"body\": \"$escaped_release_changelog\", \"draft\": false, \"name\": \"borgmatic $version\", \"prerelease\": false, \"tag_name\": \"$version\"}" \
|
||||
| jq ".id")
|
||||
curl --silent --request POST \
|
||||
"https://projects.torsion.org/api/v1/repos/borgmatic-collective/borgmatic/releases/$release_id/assets?name=$(basename $wheel_path)" \
|
||||
--header "Authorization: token $projects_token" \
|
||||
--header "Accept: application/json" \
|
||||
--form attachment=@"$wheel_path"
|
||||
curl --silent --request POST \
|
||||
"https://projects.torsion.org/api/v1/repos/borgmatic-collective/borgmatic/releases/$release_id/assets?name=$(basename $tarball_path)" \
|
||||
--header "Authorization: token $projects_token" \
|
||||
--header "Accept: application/json" \
|
||||
--form attachment=@"$tarball_path"
|
||||
curl --silent --request POST \
|
||||
"https://projects.torsion.org/api/v1/repos/borgmatic-collective/borgmatic/releases/$release_id/assets?name=$(basename $docs_path)" \
|
||||
--header "Authorization: token $projects_token" \
|
||||
--header "Accept: application/json" \
|
||||
--form attachment=@"$docs_path"
|
||||
|
||||
github-release create --token="$github_token" --owner=witten --repo=borgmatic --tag="$version" --target_commit="main" \
|
||||
--name="borgmatic $version" --body="$release_changelog"
|
||||
|
||||
@@ -24,4 +24,4 @@ requests-oauthlib==2.0.0
|
||||
rpds-py==0.28.0
|
||||
ruamel-yaml>0.15.0
|
||||
typing-extensions==4.15.0
|
||||
urllib3==2.5.0
|
||||
urllib3==2.6.1
|
||||
|
||||
@@ -16,7 +16,6 @@ def parse_arguments(*unparsed_arguments):
|
||||
show_parser.add_argument('subvolume_path')
|
||||
|
||||
snapshot_parser = subvolume_subparser.add_parser('snapshot')
|
||||
snapshot_parser.add_argument('-r', dest='read_only', action='store_true')
|
||||
snapshot_parser.add_argument('subvolume_path')
|
||||
snapshot_parser.add_argument('snapshot_path')
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ def generate_configuration(config_path, repository_path):
|
||||
.replace('ssh://user@backupserver/./sourcehostname.borg', repository_path)
|
||||
.replace('- path: /e2e/mnt/backup', '')
|
||||
.replace('label: local', '')
|
||||
.replace('- /home/user/path with spaces', '')
|
||||
.replace('- /home', f'- {config_path}')
|
||||
.replace('- /etc', '- /e2e/mnt/subvolume/subdir')
|
||||
.replace('- /var/log/syslog*', '')
|
||||
@@ -43,7 +44,10 @@ def test_btrfs_create_and_list():
|
||||
)
|
||||
|
||||
# Run a create action to exercise Btrfs snapshotting and backup.
|
||||
subprocess.check_call(f'borgmatic --config {config_path} create'.split(' '))
|
||||
subprocess.check_call(
|
||||
f'borgmatic -v 2 --config {config_path} create'.split(' '),
|
||||
env=dict(os.environ, BTRFS_TEST_SUBVOLUME_PATH='/e2e/mnt/subvolume'),
|
||||
)
|
||||
|
||||
# List the resulting archive and assert that the snapshotted files are there.
|
||||
output = subprocess.check_output(
|
||||
|
||||
@@ -512,6 +512,12 @@ def test_generate_sample_configuration_with_dry_run_does_not_write_file():
|
||||
def test_generate_sample_configuration_with_split_writes_each_option_to_file():
|
||||
builtins = flexmock(sys.modules['builtins'])
|
||||
builtins.should_receive('open').with_args('schema.yaml', encoding='utf-8').and_return('')
|
||||
builtins.should_receive('open').with_args(
|
||||
'dest/options.json', 'w', encoding='utf-8'
|
||||
).and_return(flexmock())
|
||||
flexmock(module.json).should_receive('dump').with_args(
|
||||
{'option_names': ['foo', 'bar']}, object
|
||||
).once()
|
||||
flexmock(module.ruamel.yaml).should_receive('YAML').and_return(
|
||||
flexmock(load=lambda filename: {})
|
||||
)
|
||||
@@ -520,17 +526,18 @@ def test_generate_sample_configuration_with_split_writes_each_option_to_file():
|
||||
{'foo': 1, 'bar': 2}
|
||||
)
|
||||
flexmock(module.os.path).should_receive('exists').and_return(False)
|
||||
flexmock(module).should_receive('get_configuration_subset')
|
||||
flexmock(module).should_receive('render_configuration')
|
||||
flexmock(module).should_receive('transform_optional_configuration')
|
||||
flexmock(module).should_receive('transform_optional_configuration').and_return(' ')
|
||||
flexmock(module.os).should_receive('makedirs')
|
||||
flexmock(module).should_receive('write_configuration').with_args(
|
||||
'dest/foo.yaml',
|
||||
None,
|
||||
'',
|
||||
overwrite=False,
|
||||
).once()
|
||||
flexmock(module).should_receive('write_configuration').with_args(
|
||||
'dest/bar.yaml',
|
||||
None,
|
||||
'',
|
||||
overwrite=False,
|
||||
).once()
|
||||
|
||||
@@ -549,6 +556,7 @@ def test_generate_sample_configuration_with_split_and_file_destination_errors():
|
||||
)
|
||||
flexmock(module.os.path).should_receive('exists').and_return(True)
|
||||
flexmock(module.os.path).should_receive('isdir').and_return(False)
|
||||
flexmock(module).should_receive('get_configuration_subset').never()
|
||||
flexmock(module).should_receive('render_configuration').never()
|
||||
flexmock(module).should_receive('transform_optional_configuration').never()
|
||||
flexmock(module.os).should_receive('makedirs').never()
|
||||
|
||||
@@ -24,16 +24,15 @@ def test_dump_data_sources_snapshots_each_subvolume_and_updates_patterns():
|
||||
module.Subvolume('/mnt/subvol2', contained_patterns=(Pattern('/mnt/subvol2'),)),
|
||||
),
|
||||
)
|
||||
flexmock(module.os).should_receive('getpid').and_return(1234)
|
||||
flexmock(module).should_receive('snapshot_subvolume').with_args(
|
||||
'btrfs',
|
||||
'/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
).once()
|
||||
flexmock(module).should_receive('snapshot_subvolume').with_args(
|
||||
'btrfs',
|
||||
'/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
).once()
|
||||
|
||||
assert (
|
||||
@@ -50,21 +49,19 @@ def test_dump_data_sources_snapshots_each_subvolume_and_updates_patterns():
|
||||
|
||||
assert patterns == [
|
||||
Pattern(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2/.borgmatic-snapshot-1234',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2/.borgmatic-snapshot',
|
||||
Pattern_type.NO_RECURSE,
|
||||
Pattern_style.FNMATCH,
|
||||
),
|
||||
Pattern(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1/.borgmatic-snapshot-1234',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1/.borgmatic-snapshot',
|
||||
Pattern_type.NO_RECURSE,
|
||||
Pattern_style.FNMATCH,
|
||||
),
|
||||
Pattern('/foo'),
|
||||
Pattern('/mnt/subvol1/.borgmatic-snapshot-1234/./mnt/subvol1'),
|
||||
Pattern('/mnt/subvol1/.borgmatic-snapshot-1234/./mnt/subvol1/.cache', Pattern_type.EXCLUDE),
|
||||
Pattern('/mnt/subvol1/.borgmatic-snapshot-1234/./mnt/subvol1', Pattern_type.INCLUDE),
|
||||
Pattern('/mnt/subvol2/.borgmatic-snapshot-1234/./mnt/subvol2'),
|
||||
Pattern('/mnt/subvol2/.borgmatic-snapshot-1234/./mnt/subvol2', Pattern_type.INCLUDE),
|
||||
Pattern('/mnt/subvol1/.borgmatic-snapshot/./mnt/subvol1'),
|
||||
Pattern('/mnt/subvol1/.borgmatic-snapshot/./mnt/subvol1/.cache', Pattern_type.EXCLUDE),
|
||||
Pattern('/mnt/subvol2/.borgmatic-snapshot/./mnt/subvol2'),
|
||||
]
|
||||
assert config == {
|
||||
'btrfs': {},
|
||||
|
||||
@@ -85,7 +85,5 @@ def test_dump_data_sources_snapshots_and_mounts_and_updates_patterns():
|
||||
Pattern(
|
||||
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume1/subdir/.cache', Pattern_type.EXCLUDE
|
||||
),
|
||||
Pattern('/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume1/subdir', Pattern_type.INCLUDE),
|
||||
Pattern('/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2'),
|
||||
Pattern('/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2', Pattern_type.INCLUDE),
|
||||
]
|
||||
|
||||
@@ -2,7 +2,7 @@ import os
|
||||
|
||||
from flexmock import flexmock
|
||||
|
||||
from borgmatic.borg.pattern import Pattern, Pattern_type
|
||||
from borgmatic.borg.pattern import Pattern
|
||||
from borgmatic.hooks.data_source import zfs as module
|
||||
|
||||
|
||||
@@ -44,5 +44,4 @@ def test_dump_data_sources_snapshots_and_mounts_and_updates_patterns():
|
||||
|
||||
assert patterns == [
|
||||
Pattern(os.path.join(snapshot_mount_path, 'subdir')),
|
||||
Pattern(os.path.join(snapshot_mount_path, 'subdir'), Pattern_type.INCLUDE),
|
||||
]
|
||||
|
||||
@@ -4,6 +4,7 @@ import os
|
||||
import pytest
|
||||
from flexmock import flexmock
|
||||
|
||||
import borgmatic.borg.pattern
|
||||
from borgmatic.actions import create as module
|
||||
|
||||
|
||||
@@ -296,6 +297,90 @@ def test_run_create_with_active_dumps_json_updates_archive_info():
|
||||
) == [expected_create_result]
|
||||
|
||||
|
||||
def mock_call_hooks(
|
||||
function_name, config, hook_type, config_paths, borgmatic_runtime_directory, patterns, dry_run
|
||||
):
|
||||
'''
|
||||
Simulate a dump_data_sources() call that mutates the given patterns.
|
||||
'''
|
||||
mock_dump_process = flexmock()
|
||||
mock_dump_process.should_receive('poll').and_return(None).and_return(0)
|
||||
|
||||
patterns[0] = borgmatic.borg.pattern.Pattern('/mutated/pattern/path')
|
||||
|
||||
return {'dump': mock_dump_process}
|
||||
|
||||
|
||||
def mock_call_hooks_even_if_unconfigured(
|
||||
function_name, config, hook_type, borgmatic_runtime_directory, patterns, dry_run
|
||||
):
|
||||
'''
|
||||
Assert that we're dealing with the original patterns here, not the mutated patterns.
|
||||
'''
|
||||
assert patterns[0].path == 'foo'
|
||||
|
||||
return {}
|
||||
|
||||
|
||||
def test_run_create_with_active_dumps_removes_data_source_dumps_with_original_patterns():
|
||||
flexmock(module.logger).answer = lambda message: None
|
||||
flexmock(module.borgmatic.config.paths).should_receive('Runtime_directory').and_return(
|
||||
flexmock(),
|
||||
)
|
||||
flexmock(module.borgmatic.borg.create).should_receive('create_archive').once()
|
||||
flexmock(module.borgmatic.hooks.dispatch).should_receive('call_hooks').replace_with(
|
||||
mock_call_hooks
|
||||
)
|
||||
flexmock(module.borgmatic.hooks.dispatch).should_receive(
|
||||
'call_hooks_even_if_unconfigured',
|
||||
).replace_with(mock_call_hooks_even_if_unconfigured)
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('collect_patterns').and_return(
|
||||
(borgmatic.borg.pattern.Pattern('foo'), borgmatic.borg.pattern.Pattern('bar'))
|
||||
)
|
||||
flexmock(module.borgmatic.actions.pattern).should_receive('process_patterns').replace_with(
|
||||
lambda patterns, *args, **kwargs: list(patterns)
|
||||
)
|
||||
flexmock(os.path).should_receive('join').and_return('/run/borgmatic/bootstrap')
|
||||
flexmock(module.borgmatic.borg.repo_list).should_receive('get_latest_archive').and_return(
|
||||
{'id': 'id1', 'name': 'archive.checkpoint'},
|
||||
)
|
||||
|
||||
global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
|
||||
|
||||
flexmock(module).should_receive('rename_checkpoint_archive').with_args(
|
||||
repository_path='repo',
|
||||
global_arguments=global_arguments,
|
||||
config={},
|
||||
local_borg_version=None,
|
||||
local_path=None,
|
||||
remote_path=None,
|
||||
).once()
|
||||
create_arguments = flexmock(
|
||||
repository=None,
|
||||
progress=flexmock(),
|
||||
statistics=flexmock(),
|
||||
json=False,
|
||||
comment=None,
|
||||
list_details=flexmock(),
|
||||
)
|
||||
|
||||
list(
|
||||
module.run_create(
|
||||
config_filename='test.yaml',
|
||||
repository={'path': 'repo'},
|
||||
config={},
|
||||
config_paths=['/tmp/test.yaml'],
|
||||
local_borg_version=None,
|
||||
create_arguments=create_arguments,
|
||||
global_arguments=global_arguments,
|
||||
dry_run_label='',
|
||||
local_path=None,
|
||||
remote_path=None,
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def test_rename_checkpoint_archive_renames_archive_using_name():
|
||||
global_arguments = flexmock(monitoring_verbosity=1, dry_run=False)
|
||||
flexmock(module.borgmatic.borg.repo_list).should_receive('get_latest_archive').and_return(
|
||||
|
||||
@@ -402,6 +402,12 @@ def test_device_map_patterns_with_existing_device_id_does_not_overwrite_it():
|
||||
(Pattern('/', device=1),),
|
||||
False,
|
||||
),
|
||||
(
|
||||
(Pattern('/', device=1), Pattern('/other', device=1)),
|
||||
'/root',
|
||||
(Pattern('/', device=1), Pattern('/other', device=1)),
|
||||
False,
|
||||
),
|
||||
(
|
||||
(Pattern('/', device=1), Pattern('/root', device=2)),
|
||||
None,
|
||||
|
||||
@@ -101,7 +101,7 @@ def test_validate_planned_backup_paths_skips_borgmatic_runtime_directory():
|
||||
)
|
||||
flexmock(module.os.path).should_receive('exists').and_return(True)
|
||||
flexmock(module).should_receive('any_parent_directories').replace_with(
|
||||
lambda path, _: path == '/run/borgmatic/bar'
|
||||
lambda path, candidates: any(path.startswith(parent) for parent in candidates)
|
||||
)
|
||||
|
||||
assert module.validate_planned_backup_paths(
|
||||
@@ -134,7 +134,7 @@ def test_validate_planned_backup_paths_with_borgmatic_runtime_directory_missing_
|
||||
)
|
||||
flexmock(module.os.path).should_receive('exists').and_return(True)
|
||||
flexmock(module).should_receive('any_parent_directories').replace_with(
|
||||
lambda path, _: path == '/run/borgmatic/bar'
|
||||
lambda path, candidates: any(path.startswith(parent) for parent in candidates)
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
@@ -155,6 +155,45 @@ def test_validate_planned_backup_paths_with_borgmatic_runtime_directory_missing_
|
||||
)
|
||||
|
||||
|
||||
def test_validate_planned_backup_paths_with_borgmatic_runtime_directory_partially_excluded_from_paths_output_errors():
|
||||
flexmock(module.flags).should_receive('omit_flag').replace_with(
|
||||
lambda arguments, flag: arguments,
|
||||
)
|
||||
flexmock(module.flags).should_receive('omit_flag_and_value').replace_with(
|
||||
lambda arguments, flag: arguments,
|
||||
)
|
||||
flexmock(module.environment).should_receive('make_environment').and_return(None)
|
||||
|
||||
# /run/borgmatic/bar is present, but /run/borgmatic/quux is missing.
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').and_return(
|
||||
'+ /foo\n- /run/borgmatic/bar\n- /baz',
|
||||
)
|
||||
flexmock(module.os.path).should_receive('exists').and_return(True)
|
||||
flexmock(module).should_receive('any_parent_directories').replace_with(
|
||||
lambda path, candidates: any(path.startswith(parent) for parent in candidates)
|
||||
)
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
module.validate_planned_backup_paths(
|
||||
dry_run=False,
|
||||
create_command=('borg', 'create'),
|
||||
config={},
|
||||
patterns=(
|
||||
module.borgmatic.borg.pattern.Pattern('/foo'),
|
||||
module.borgmatic.borg.pattern.Pattern(
|
||||
'/run/borgmatic/bar', module.borgmatic.borg.pattern.Pattern_type.ROOT
|
||||
),
|
||||
module.borgmatic.borg.pattern.Pattern('/baz'),
|
||||
module.borgmatic.borg.pattern.Pattern(
|
||||
'/run/borgmatic/quux', module.borgmatic.borg.pattern.Pattern_type.ROOT
|
||||
),
|
||||
),
|
||||
local_path=None,
|
||||
working_directory=None,
|
||||
borgmatic_runtime_directory='/run/borgmatic',
|
||||
)
|
||||
|
||||
|
||||
def test_validate_planned_backup_paths_with_borgmatic_runtime_directory_missing_from_patterns_does_not_raise():
|
||||
flexmock(module.flags).should_receive('omit_flag').replace_with(
|
||||
lambda arguments, flag: arguments,
|
||||
@@ -168,7 +207,7 @@ def test_validate_planned_backup_paths_with_borgmatic_runtime_directory_missing_
|
||||
)
|
||||
flexmock(module.os.path).should_receive('exists').and_return(True)
|
||||
flexmock(module).should_receive('any_parent_directories').replace_with(
|
||||
lambda path, _: path == '/run/borgmatic/bar'
|
||||
lambda path, candidates: any(path.startswith(parent) for parent in candidates)
|
||||
)
|
||||
|
||||
assert module.validate_planned_backup_paths(
|
||||
|
||||
@@ -19,7 +19,7 @@ def test_make_list_command_includes_log_info():
|
||||
repository_path='repo',
|
||||
config={},
|
||||
local_borg_version='1.2.3',
|
||||
list_arguments=flexmock(archive=None, paths=None, json=False),
|
||||
list_arguments=flexmock(archive=None, paths=None, format=None, json=False),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
@@ -36,7 +36,7 @@ def test_make_list_command_includes_json_but_not_info():
|
||||
repository_path='repo',
|
||||
config={},
|
||||
local_borg_version='1.2.3',
|
||||
list_arguments=flexmock(archive=None, paths=None, json=True),
|
||||
list_arguments=flexmock(archive=None, paths=None, format=None, json=True),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
@@ -53,7 +53,7 @@ def test_make_list_command_includes_log_debug():
|
||||
repository_path='repo',
|
||||
config={},
|
||||
local_borg_version='1.2.3',
|
||||
list_arguments=flexmock(archive=None, paths=None, json=False),
|
||||
list_arguments=flexmock(archive=None, paths=None, format=None, json=False),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
@@ -70,7 +70,7 @@ def test_make_list_command_includes_json_but_not_debug():
|
||||
repository_path='repo',
|
||||
config={},
|
||||
local_borg_version='1.2.3',
|
||||
list_arguments=flexmock(archive=None, paths=None, json=True),
|
||||
list_arguments=flexmock(archive=None, paths=None, format=None, json=True),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
@@ -86,7 +86,7 @@ def test_make_list_command_includes_json():
|
||||
repository_path='repo',
|
||||
config={},
|
||||
local_borg_version='1.2.3',
|
||||
list_arguments=flexmock(archive=None, paths=None, json=True),
|
||||
list_arguments=flexmock(archive=None, paths=None, format=None, json=True),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
@@ -96,7 +96,7 @@ def test_make_list_command_includes_json():
|
||||
def test_make_list_command_includes_log_json():
|
||||
flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(()).and_return(
|
||||
('--log-json',),
|
||||
)
|
||||
).and_return(())
|
||||
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
|
||||
|
||||
@@ -104,7 +104,7 @@ def test_make_list_command_includes_log_json():
|
||||
repository_path='repo',
|
||||
config={'log_json': True},
|
||||
local_borg_version='1.2.3',
|
||||
list_arguments=flexmock(archive=None, paths=None, json=False),
|
||||
list_arguments=flexmock(archive=None, paths=None, format=None, json=False),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
@@ -114,7 +114,7 @@ def test_make_list_command_includes_log_json():
|
||||
def test_make_list_command_includes_lock_wait():
|
||||
flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(()).and_return(
|
||||
('--lock-wait', '5'),
|
||||
)
|
||||
).and_return(())
|
||||
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
|
||||
|
||||
@@ -122,13 +122,31 @@ def test_make_list_command_includes_lock_wait():
|
||||
repository_path='repo',
|
||||
config={'lock_wait': 5},
|
||||
local_borg_version='1.2.3',
|
||||
list_arguments=flexmock(archive=None, paths=None, json=False),
|
||||
list_arguments=flexmock(archive=None, paths=None, format=None, json=False),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
assert command == ('borg', 'list', '--lock-wait', '5', 'repo')
|
||||
|
||||
|
||||
def test_make_list_command_includes_format():
|
||||
flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(()).and_return(
|
||||
()
|
||||
).and_return(('--format', 'stuff'))
|
||||
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
|
||||
|
||||
command = module.make_list_command(
|
||||
repository_path='repo',
|
||||
config={},
|
||||
local_borg_version='1.2.3',
|
||||
list_arguments=flexmock(archive=None, paths=None, format='stuff', json=False),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
assert command == ('borg', 'list', '--format', 'stuff', 'repo')
|
||||
|
||||
|
||||
def test_make_list_command_includes_extra_borg_options():
|
||||
flexmock(module.flags).should_receive('make_flags').and_return(())
|
||||
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
|
||||
@@ -138,7 +156,7 @@ def test_make_list_command_includes_extra_borg_options():
|
||||
repository_path='repo',
|
||||
config={'extra_borg_options': {'list': '--extra "value with space"'}},
|
||||
local_borg_version='1.2.3',
|
||||
list_arguments=flexmock(archive=None, paths=None, json=False),
|
||||
list_arguments=flexmock(archive=None, paths=None, format=None, json=False),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
@@ -156,7 +174,7 @@ def test_make_list_command_includes_archive():
|
||||
repository_path='repo',
|
||||
config={},
|
||||
local_borg_version='1.2.3',
|
||||
list_arguments=flexmock(archive='archive', paths=None, json=False),
|
||||
list_arguments=flexmock(archive='archive', paths=None, format=None, json=False),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
@@ -174,7 +192,7 @@ def test_make_list_command_includes_archive_and_path():
|
||||
repository_path='repo',
|
||||
config={},
|
||||
local_borg_version='1.2.3',
|
||||
list_arguments=flexmock(archive='archive', paths=['var/lib'], json=False),
|
||||
list_arguments=flexmock(archive='archive', paths=['var/lib'], format=None, json=False),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
@@ -190,7 +208,7 @@ def test_make_list_command_includes_local_path():
|
||||
repository_path='repo',
|
||||
config={},
|
||||
local_borg_version='1.2.3',
|
||||
list_arguments=flexmock(archive=None, paths=None, json=False),
|
||||
list_arguments=flexmock(archive=None, paths=None, format=None, json=False),
|
||||
global_arguments=flexmock(),
|
||||
local_path='borg2',
|
||||
)
|
||||
@@ -214,7 +232,7 @@ def test_make_list_command_includes_remote_path():
|
||||
repository_path='repo',
|
||||
config={},
|
||||
local_borg_version='1.2.3',
|
||||
list_arguments=flexmock(archive=None, paths=None, json=False),
|
||||
list_arguments=flexmock(archive=None, paths=None, format=None, json=False),
|
||||
global_arguments=flexmock(),
|
||||
remote_path='borg2',
|
||||
)
|
||||
@@ -233,7 +251,7 @@ def test_make_list_command_includes_umask():
|
||||
repository_path='repo',
|
||||
config={'umask': '077'},
|
||||
local_borg_version='1.2.3',
|
||||
list_arguments=flexmock(archive=None, paths=None, json=False),
|
||||
list_arguments=flexmock(archive=None, paths=None, format=None, json=False),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
@@ -249,7 +267,7 @@ def test_make_list_command_includes_short():
|
||||
repository_path='repo',
|
||||
config={},
|
||||
local_borg_version='1.2.3',
|
||||
list_arguments=flexmock(archive=None, paths=None, json=False, short=True),
|
||||
list_arguments=flexmock(archive=None, paths=None, format=None, json=False, short=True),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
@@ -284,9 +302,9 @@ def test_make_list_command_includes_additional_flags(argument_name):
|
||||
list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
find_paths=None,
|
||||
format=None,
|
||||
**{argument_name: 'value'},
|
||||
),
|
||||
global_arguments=flexmock(),
|
||||
@@ -344,6 +362,7 @@ def test_list_archive_calls_borg_with_flags():
|
||||
list_arguments = argparse.Namespace(
|
||||
archive='archive',
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
find_paths=None,
|
||||
prefix=None,
|
||||
@@ -410,6 +429,7 @@ def test_list_archive_calls_borg_with_local_path():
|
||||
list_arguments = argparse.Namespace(
|
||||
archive='archive',
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
find_paths=None,
|
||||
prefix=None,
|
||||
|
||||
@@ -600,6 +600,7 @@ def test_make_repo_list_command_includes_log_info():
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
@@ -629,6 +630,7 @@ def test_make_repo_list_command_includes_json_but_not_info():
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=True,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
@@ -658,6 +660,7 @@ def test_make_repo_list_command_includes_log_debug():
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
@@ -687,6 +690,7 @@ def test_make_repo_list_command_includes_json_but_not_debug():
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=True,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
@@ -715,6 +719,7 @@ def test_make_repo_list_command_includes_json():
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=True,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
@@ -729,7 +734,7 @@ def test_make_repo_list_command_includes_log_json():
|
||||
flexmock(module.feature).should_receive('available').and_return(False)
|
||||
flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(
|
||||
('--log-json',),
|
||||
).and_return(())
|
||||
).and_return(()).and_return(())
|
||||
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
|
||||
None,
|
||||
None,
|
||||
@@ -745,6 +750,7 @@ def test_make_repo_list_command_includes_log_json():
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
@@ -759,7 +765,7 @@ def test_make_repo_list_command_includes_lock_wait():
|
||||
flexmock(module.feature).should_receive('available').and_return(False)
|
||||
flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(
|
||||
('--lock-wait', '5'),
|
||||
).and_return(())
|
||||
).and_return(()).and_return(())
|
||||
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
|
||||
None,
|
||||
None,
|
||||
@@ -775,6 +781,7 @@ def test_make_repo_list_command_includes_lock_wait():
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
@@ -803,6 +810,7 @@ def test_make_repo_list_command_includes_list_extra_borg_options():
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
@@ -831,6 +839,7 @@ def test_make_repo_list_command_with_feature_available_includes_repo_list_extra_
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
@@ -859,6 +868,7 @@ def test_make_repo_list_command_includes_local_path():
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
@@ -890,6 +900,7 @@ def test_make_repo_list_command_includes_remote_path():
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
@@ -921,6 +932,7 @@ def test_make_repo_list_command_includes_umask():
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
@@ -935,7 +947,7 @@ def test_make_repo_list_command_transforms_prefix_into_match_archives():
|
||||
flexmock(module.feature).should_receive('available').and_return(False)
|
||||
flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(()).and_return(
|
||||
('--match-archives', 'sh:foo*'),
|
||||
)
|
||||
).and_return(())
|
||||
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
|
||||
None,
|
||||
None,
|
||||
@@ -948,7 +960,9 @@ def test_make_repo_list_command_transforms_prefix_into_match_archives():
|
||||
repository_path='repo',
|
||||
config={},
|
||||
local_borg_version='1.2.3',
|
||||
repo_list_arguments=flexmock(archive=None, paths=None, json=False, prefix='foo'),
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None, paths=None, format=None, json=False, prefix='foo'
|
||||
),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
@@ -959,7 +973,7 @@ def test_make_repo_list_command_prefers_prefix_over_archive_name_format():
|
||||
flexmock(module.feature).should_receive('available').and_return(False)
|
||||
flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(()).and_return(
|
||||
('--match-archives', 'sh:foo*'),
|
||||
)
|
||||
).and_return(())
|
||||
flexmock(module.flags).should_receive('make_match_archives_flags').never()
|
||||
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
|
||||
@@ -968,7 +982,9 @@ def test_make_repo_list_command_prefers_prefix_over_archive_name_format():
|
||||
repository_path='repo',
|
||||
config={'archive_name_format': 'bar-{now}'},
|
||||
local_borg_version='1.2.3',
|
||||
repo_list_arguments=flexmock(archive=None, paths=None, json=False, prefix='foo'),
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None, paths=None, format=None, json=False, prefix='foo'
|
||||
),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
@@ -993,6 +1009,7 @@ def test_make_repo_list_command_transforms_archive_name_format_into_match_archiv
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
@@ -1003,6 +1020,38 @@ def test_make_repo_list_command_transforms_archive_name_format_into_match_archiv
|
||||
assert command == ('borg', 'list', '--match-archives', 'sh:bar-*', 'repo')
|
||||
|
||||
|
||||
def test_make_repo_list_command_includes_format_from_command_line():
|
||||
flexmock(module.feature).should_receive('available').and_return(False)
|
||||
flexmock(module.flags).should_receive('make_flags').and_return(()).and_return(()).and_return(
|
||||
('--format', 'stuff')
|
||||
)
|
||||
flexmock(module.flags).should_receive('make_match_archives_flags').with_args(
|
||||
None,
|
||||
None,
|
||||
'1.2.3',
|
||||
).and_return(())
|
||||
flexmock(module.flags).should_receive('make_flags_from_arguments').and_return(())
|
||||
flexmock(module.flags).should_receive('make_repository_flags').and_return(('repo',))
|
||||
|
||||
command = module.make_repo_list_command(
|
||||
repository_path='repo',
|
||||
config={},
|
||||
local_borg_version='1.2.3',
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format='stuff',
|
||||
json=False,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
short=False,
|
||||
),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
|
||||
assert command == ('borg', 'list', '--format', 'stuff', 'repo')
|
||||
|
||||
|
||||
def test_make_repo_list_command_includes_short():
|
||||
flexmock(module.feature).should_receive('available').and_return(False)
|
||||
flexmock(module.flags).should_receive('make_flags').and_return(())
|
||||
@@ -1021,6 +1070,7 @@ def test_make_repo_list_command_includes_short():
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
@@ -1064,11 +1114,11 @@ def test_make_repo_list_command_includes_additional_flags(argument_name):
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
find_paths=None,
|
||||
format=None,
|
||||
**{argument_name: 'value'},
|
||||
),
|
||||
global_arguments=flexmock(),
|
||||
@@ -1100,11 +1150,11 @@ def test_make_repo_list_command_with_match_archives_calls_borg_with_match_archiv
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
prefix=None,
|
||||
match_archives='foo-*',
|
||||
find_paths=None,
|
||||
format=None,
|
||||
),
|
||||
global_arguments=flexmock(),
|
||||
)
|
||||
@@ -1173,6 +1223,7 @@ def test_make_repo_list_command_with_date_based_matching_calls_borg_with_date_ba
|
||||
repo_list_arguments=flexmock(
|
||||
archive=None,
|
||||
paths=None,
|
||||
format=None,
|
||||
json=False,
|
||||
prefix=None,
|
||||
match_archives=None,
|
||||
|
||||
@@ -21,14 +21,6 @@ def test_get_default_config_paths_prefers_xdg_config_home_for_user_config_path()
|
||||
assert '/home/user/.etc/borgmatic/config.yaml' in config_paths
|
||||
|
||||
|
||||
def test_get_default_config_paths_does_not_expand_home_when_false():
|
||||
flexmock(module.os, environ={'HOME': '/home/user'})
|
||||
|
||||
config_paths = module.get_default_config_paths(expand_home=False)
|
||||
|
||||
assert '$HOME/.config/borgmatic/config.yaml' in config_paths
|
||||
|
||||
|
||||
def test_collect_config_filenames_collects_yml_file_endings():
|
||||
config_paths = ('config.yaml', '/etc/borgmatic.d')
|
||||
mock_path = flexmock(module.os.path)
|
||||
|
||||
@@ -151,6 +151,38 @@ def test_load_credential_with_key_file():
|
||||
)
|
||||
|
||||
|
||||
def test_load_credential_with_key_file_and_ask_for_password_false():
|
||||
flexmock(module.os.path).should_receive('expanduser').with_args('database.kdbx').and_return(
|
||||
'database.kdbx',
|
||||
)
|
||||
flexmock(module.os.path).should_receive('exists').and_return(True)
|
||||
flexmock(module.borgmatic.execute).should_receive(
|
||||
'execute_command_and_capture_output',
|
||||
).with_args(
|
||||
(
|
||||
'keepassxc-cli',
|
||||
'show',
|
||||
'--show-protected',
|
||||
'--attributes',
|
||||
'Password',
|
||||
'--no-password',
|
||||
'--key-file',
|
||||
'/path/to/keyfile',
|
||||
'database.kdbx',
|
||||
'mypassword',
|
||||
),
|
||||
).and_return('password').once()
|
||||
|
||||
assert (
|
||||
module.load_credential(
|
||||
hook_config={'key_file': '/path/to/keyfile', 'ask_for_password': False},
|
||||
config={},
|
||||
credential_parameters=('database.kdbx', 'mypassword'),
|
||||
)
|
||||
== 'password'
|
||||
)
|
||||
|
||||
|
||||
def test_load_credential_with_yubikey():
|
||||
flexmock(module.os.path).should_receive('expanduser').with_args('database.kdbx').and_return(
|
||||
'database.kdbx',
|
||||
|
||||
@@ -5,36 +5,37 @@ from borgmatic.borg.pattern import Pattern, Pattern_source, Pattern_style, Patte
|
||||
from borgmatic.hooks.data_source import btrfs as module
|
||||
|
||||
|
||||
def test_path_is_a_subvolume_with_btrfs_success_call_returns_true():
|
||||
def test_path_is_a_subvolume_with_btrfs_inode_returns_true():
|
||||
module.path_is_a_subvolume.cache_clear()
|
||||
flexmock(module.borgmatic.execute).should_receive(
|
||||
'execute_command',
|
||||
).with_args(('btrfs', 'subvolume', 'show', '/mnt0'), output_log_level=None, close_fds=True)
|
||||
|
||||
assert module.path_is_a_subvolume('btrfs', '/mnt0') is True
|
||||
|
||||
|
||||
def test_path_is_a_subvolume_with_btrfs_error_returns_false():
|
||||
module.path_is_a_subvolume.cache_clear()
|
||||
flexmock(module.borgmatic.execute).should_receive(
|
||||
'execute_command',
|
||||
).with_args(
|
||||
('btrfs', 'subvolume', 'show', '/mnt0'), output_log_level=None, close_fds=True
|
||||
).and_raise(
|
||||
module.subprocess.CalledProcessError(1, 'btrfs'),
|
||||
flexmock(module.os).should_receive('stat').and_return(
|
||||
flexmock(st_ino=module.BTRFS_SUBVOLUME_INODE_NUMBER)
|
||||
)
|
||||
|
||||
assert module.path_is_a_subvolume('btrfs', '/mnt0') is False
|
||||
assert module.path_is_a_subvolume('/mnt0') is True
|
||||
|
||||
|
||||
def test_path_is_a_subvolume_with_non_btrfs_inode_returns_false():
|
||||
module.path_is_a_subvolume.cache_clear()
|
||||
flexmock(module.os).should_receive('stat').and_return(flexmock(st_ino=12345))
|
||||
|
||||
assert module.path_is_a_subvolume('/mnt0') is False
|
||||
|
||||
|
||||
def test_path_is_a_subvolume_caches_result_after_first_call():
|
||||
module.path_is_a_subvolume.cache_clear()
|
||||
flexmock(module.borgmatic.execute).should_receive(
|
||||
'execute_command',
|
||||
flexmock(module.os).should_receive('stat').and_return(
|
||||
flexmock(st_ino=module.BTRFS_SUBVOLUME_INODE_NUMBER)
|
||||
).once()
|
||||
|
||||
assert module.path_is_a_subvolume('btrfs', '/mnt0') is True
|
||||
assert module.path_is_a_subvolume('btrfs', '/mnt0') is True
|
||||
assert module.path_is_a_subvolume('/mnt0') is True
|
||||
assert module.path_is_a_subvolume('/mnt0') is True
|
||||
|
||||
|
||||
def test_path_is_a_subvolume_with_missing_path_returns_false():
|
||||
module.path_is_a_subvolume.cache_clear()
|
||||
flexmock(module.os).should_receive('stat').and_raise(FileNotFoundError)
|
||||
|
||||
assert module.path_is_a_subvolume('/mnt0') is False
|
||||
|
||||
|
||||
def test_get_subvolume_property_with_invalid_btrfs_output_errors():
|
||||
@@ -85,76 +86,66 @@ def test_get_subvolume_property_caches_result_after_first_call():
|
||||
|
||||
|
||||
def test_get_containing_subvolume_path_with_subvolume_self_returns_it():
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args(
|
||||
'btrfs', '/foo/bar/baz'
|
||||
).and_return(True)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('btrfs', '/foo/bar').never()
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('btrfs', '/foo').never()
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('btrfs', '/').never()
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/foo/bar/baz').and_return(
|
||||
True
|
||||
)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/foo/bar').never()
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/foo').never()
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/').never()
|
||||
flexmock(module).should_receive('get_subvolume_property').and_return(False)
|
||||
|
||||
assert module.get_containing_subvolume_path('btrfs', '/foo/bar/baz') == '/foo/bar/baz'
|
||||
|
||||
|
||||
def test_get_containing_subvolume_path_with_subvolume_parent_returns_it():
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args(
|
||||
'btrfs', '/foo/bar/baz'
|
||||
).and_return(False)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args(
|
||||
'btrfs', '/foo/bar'
|
||||
).and_return(True)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('btrfs', '/foo').never()
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('btrfs', '/').never()
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/foo/bar/baz').and_return(
|
||||
False
|
||||
)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/foo/bar').and_return(True)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/foo').never()
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/').never()
|
||||
flexmock(module).should_receive('get_subvolume_property').and_return(False)
|
||||
|
||||
assert module.get_containing_subvolume_path('btrfs', '/foo/bar/baz') == '/foo/bar'
|
||||
|
||||
|
||||
def test_get_containing_subvolume_path_with_subvolume_grandparent_returns_it():
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args(
|
||||
'btrfs', '/foo/bar/baz'
|
||||
).and_return(False)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args(
|
||||
'btrfs', '/foo/bar'
|
||||
).and_return(False)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('btrfs', '/foo').and_return(
|
||||
True
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/foo/bar/baz').and_return(
|
||||
False
|
||||
)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('btrfs', '/').never()
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/foo/bar').and_return(False)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/foo').and_return(True)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/').never()
|
||||
flexmock(module).should_receive('get_subvolume_property').and_return(False)
|
||||
|
||||
assert module.get_containing_subvolume_path('btrfs', '/foo/bar/baz') == '/foo'
|
||||
|
||||
|
||||
def test_get_containing_subvolume_path_without_subvolume_ancestor_returns_none():
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args(
|
||||
'btrfs', '/foo/bar/baz'
|
||||
).and_return(False)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args(
|
||||
'btrfs', '/foo/bar'
|
||||
).and_return(False)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('btrfs', '/foo').and_return(
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/foo/bar/baz').and_return(
|
||||
False
|
||||
)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('btrfs', '/').and_return(False)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/foo/bar').and_return(False)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/foo').and_return(False)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/').and_return(False)
|
||||
flexmock(module).should_receive('get_subvolume_property').and_return(False)
|
||||
|
||||
assert module.get_containing_subvolume_path('btrfs', '/foo/bar/baz') is None
|
||||
|
||||
|
||||
def test_get_containing_subvolume_path_with_read_only_subvolume_returns_none():
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args(
|
||||
'btrfs', '/foo/bar/baz'
|
||||
).and_return(True)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/foo/bar/baz').and_return(
|
||||
True
|
||||
)
|
||||
flexmock(module).should_receive('get_subvolume_property').and_return(True)
|
||||
|
||||
assert module.get_containing_subvolume_path('btrfs', '/foo/bar/baz') is None
|
||||
|
||||
|
||||
def test_get_containing_subvolume_path_with_read_only_error_returns_none():
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args(
|
||||
'btrfs', '/foo/bar/baz'
|
||||
).and_return(True)
|
||||
flexmock(module).should_receive('path_is_a_subvolume').with_args('/foo/bar/baz').and_return(
|
||||
True
|
||||
)
|
||||
flexmock(module).should_receive('get_subvolume_property').and_raise(
|
||||
module.subprocess.CalledProcessError(1, 'wtf')
|
||||
)
|
||||
@@ -340,16 +331,14 @@ def test_get_subvolumes_skips_non_config_patterns():
|
||||
@pytest.mark.parametrize(
|
||||
'subvolume_path,expected_snapshot_path',
|
||||
(
|
||||
('/foo/bar', '/foo/bar/.borgmatic-snapshot-1234/foo/bar'),
|
||||
('/', '/.borgmatic-snapshot-1234'),
|
||||
('/foo/bar', '/foo/bar/.borgmatic-snapshot/foo/bar'),
|
||||
('/', '/.borgmatic-snapshot'),
|
||||
),
|
||||
)
|
||||
def test_make_snapshot_path_includes_stripped_subvolume_path(
|
||||
subvolume_path,
|
||||
expected_snapshot_path,
|
||||
):
|
||||
flexmock(module.os).should_receive('getpid').and_return(1234)
|
||||
|
||||
assert module.make_snapshot_path(subvolume_path) == expected_snapshot_path
|
||||
|
||||
|
||||
@@ -359,14 +348,14 @@ def test_make_snapshot_path_includes_stripped_subvolume_path(
|
||||
(
|
||||
'/foo/bar',
|
||||
Pattern('/foo/bar/baz'),
|
||||
Pattern('/foo/bar/.borgmatic-snapshot-1234/./foo/bar/baz'),
|
||||
Pattern('/foo/bar/.borgmatic-snapshot/./foo/bar/baz'),
|
||||
),
|
||||
('/foo/bar', Pattern('/foo/bar'), Pattern('/foo/bar/.borgmatic-snapshot-1234/./foo/bar')),
|
||||
('/foo/bar', Pattern('/foo/bar'), Pattern('/foo/bar/.borgmatic-snapshot/./foo/bar')),
|
||||
(
|
||||
'/foo/bar',
|
||||
Pattern('^/foo/bar', Pattern_type.INCLUDE, Pattern_style.REGULAR_EXPRESSION),
|
||||
Pattern(
|
||||
'^/foo/bar/.borgmatic-snapshot-1234/./foo/bar',
|
||||
'^/foo/bar/.borgmatic-snapshot/./foo/bar',
|
||||
Pattern_type.INCLUDE,
|
||||
Pattern_style.REGULAR_EXPRESSION,
|
||||
),
|
||||
@@ -375,17 +364,17 @@ def test_make_snapshot_path_includes_stripped_subvolume_path(
|
||||
'/foo/bar',
|
||||
Pattern('/foo/bar', Pattern_type.INCLUDE, Pattern_style.REGULAR_EXPRESSION),
|
||||
Pattern(
|
||||
'/foo/bar/.borgmatic-snapshot-1234/./foo/bar',
|
||||
'/foo/bar/.borgmatic-snapshot/./foo/bar',
|
||||
Pattern_type.INCLUDE,
|
||||
Pattern_style.REGULAR_EXPRESSION,
|
||||
),
|
||||
),
|
||||
('/', Pattern('/foo'), Pattern('/.borgmatic-snapshot-1234/./foo')),
|
||||
('/', Pattern('/'), Pattern('/.borgmatic-snapshot-1234/./')),
|
||||
('/', Pattern('/foo'), Pattern('/.borgmatic-snapshot/./foo')),
|
||||
('/', Pattern('/'), Pattern('/.borgmatic-snapshot/./')),
|
||||
(
|
||||
'/foo/bar',
|
||||
Pattern('/foo/bar/./baz'),
|
||||
Pattern('/foo/bar/.borgmatic-snapshot-1234/foo/bar/./baz'),
|
||||
Pattern('/foo/bar/.borgmatic-snapshot/foo/bar/./baz'),
|
||||
),
|
||||
),
|
||||
)
|
||||
@@ -394,8 +383,6 @@ def test_make_borg_snapshot_pattern_includes_slashdot_hack_and_stripped_pattern_
|
||||
pattern,
|
||||
expected_pattern,
|
||||
):
|
||||
flexmock(module.os).should_receive('getpid').and_return(1234)
|
||||
|
||||
assert module.make_borg_snapshot_pattern(subvolume_path, pattern) == expected_pattern
|
||||
|
||||
|
||||
@@ -409,26 +396,26 @@ def test_dump_data_sources_snapshots_each_subvolume_and_replaces_patterns():
|
||||
),
|
||||
)
|
||||
flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
)
|
||||
flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol2').and_return(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
)
|
||||
flexmock(module).should_receive('snapshot_subvolume').with_args(
|
||||
'btrfs',
|
||||
'/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
).once()
|
||||
flexmock(module).should_receive('snapshot_subvolume').with_args(
|
||||
'btrfs',
|
||||
'/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
).once()
|
||||
flexmock(module).should_receive('make_snapshot_exclude_pattern').with_args(
|
||||
'/mnt/subvol1',
|
||||
).and_return(
|
||||
Pattern(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1/.borgmatic-snapshot-1234',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1/.borgmatic-snapshot',
|
||||
Pattern_type.NO_RECURSE,
|
||||
Pattern_style.FNMATCH,
|
||||
),
|
||||
@@ -437,7 +424,7 @@ def test_dump_data_sources_snapshots_each_subvolume_and_replaces_patterns():
|
||||
'/mnt/subvol2',
|
||||
).and_return(
|
||||
Pattern(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2/.borgmatic-snapshot-1234',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2/.borgmatic-snapshot',
|
||||
Pattern_type.NO_RECURSE,
|
||||
Pattern_style.FNMATCH,
|
||||
),
|
||||
@@ -445,36 +432,31 @@ def test_dump_data_sources_snapshots_each_subvolume_and_replaces_patterns():
|
||||
flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
|
||||
'/mnt/subvol1',
|
||||
object,
|
||||
).and_return(Pattern('/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1'))
|
||||
).and_return(Pattern('/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1'))
|
||||
flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
|
||||
'/mnt/subvol2',
|
||||
object,
|
||||
).and_return(Pattern('/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2'))
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
|
||||
'get_last_pattern_index'
|
||||
).and_return(0)
|
||||
).and_return(Pattern('/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2'))
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
|
||||
object,
|
||||
Pattern('/mnt/subvol1'),
|
||||
module.borgmatic.borg.pattern.Pattern(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||
),
|
||||
0,
|
||||
).once()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
|
||||
object,
|
||||
Pattern('/mnt/subvol2'),
|
||||
module.borgmatic.borg.pattern.Pattern(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||
),
|
||||
0,
|
||||
).once()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').with_args(
|
||||
object,
|
||||
module.borgmatic.borg.pattern.Pattern(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1/.borgmatic-snapshot-1234',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1/.borgmatic-snapshot',
|
||||
Pattern_type.NO_RECURSE,
|
||||
Pattern_style.FNMATCH,
|
||||
),
|
||||
@@ -482,7 +464,7 @@ def test_dump_data_sources_snapshots_each_subvolume_and_replaces_patterns():
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').with_args(
|
||||
object,
|
||||
module.borgmatic.borg.pattern.Pattern(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2/.borgmatic-snapshot-1234',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2/.borgmatic-snapshot',
|
||||
Pattern_type.NO_RECURSE,
|
||||
Pattern_style.FNMATCH,
|
||||
),
|
||||
@@ -512,18 +494,18 @@ def test_dump_data_sources_uses_custom_btrfs_command_in_commands():
|
||||
(module.Subvolume('/mnt/subvol1', contained_patterns=(Pattern('/mnt/subvol1'),)),),
|
||||
)
|
||||
flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
)
|
||||
flexmock(module).should_receive('snapshot_subvolume').with_args(
|
||||
'/usr/local/bin/btrfs',
|
||||
'/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
).once()
|
||||
flexmock(module).should_receive('make_snapshot_exclude_pattern').with_args(
|
||||
'/mnt/subvol1',
|
||||
).and_return(
|
||||
Pattern(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1/.borgmatic-snapshot-1234',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1/.borgmatic-snapshot',
|
||||
Pattern_type.NO_RECURSE,
|
||||
Pattern_style.FNMATCH,
|
||||
),
|
||||
@@ -531,23 +513,19 @@ def test_dump_data_sources_uses_custom_btrfs_command_in_commands():
|
||||
flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
|
||||
'/mnt/subvol1',
|
||||
object,
|
||||
).and_return(Pattern('/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1'))
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
|
||||
'get_last_pattern_index'
|
||||
).and_return(0)
|
||||
).and_return(Pattern('/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1'))
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
|
||||
object,
|
||||
Pattern('/mnt/subvol1'),
|
||||
module.borgmatic.borg.pattern.Pattern(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||
),
|
||||
0,
|
||||
).once()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').with_args(
|
||||
object,
|
||||
module.borgmatic.borg.pattern.Pattern(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1/.borgmatic-snapshot-1234',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1/.borgmatic-snapshot',
|
||||
Pattern_type.NO_RECURSE,
|
||||
Pattern_style.FNMATCH,
|
||||
),
|
||||
@@ -583,18 +561,18 @@ def test_dump_data_sources_with_findmnt_command_warns():
|
||||
(module.Subvolume('/mnt/subvol1', contained_patterns=(Pattern('/mnt/subvol1'),)),),
|
||||
).once()
|
||||
flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
)
|
||||
flexmock(module).should_receive('snapshot_subvolume').with_args(
|
||||
'btrfs',
|
||||
'/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
).once()
|
||||
flexmock(module).should_receive('make_snapshot_exclude_pattern').with_args(
|
||||
'/mnt/subvol1',
|
||||
).and_return(
|
||||
Pattern(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1/.borgmatic-snapshot-1234',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1/.borgmatic-snapshot',
|
||||
Pattern_type.NO_RECURSE,
|
||||
Pattern_style.FNMATCH,
|
||||
),
|
||||
@@ -602,23 +580,19 @@ def test_dump_data_sources_with_findmnt_command_warns():
|
||||
flexmock(module).should_receive('make_borg_snapshot_pattern').with_args(
|
||||
'/mnt/subvol1',
|
||||
object,
|
||||
).and_return(Pattern('/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1'))
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
|
||||
'get_last_pattern_index'
|
||||
).and_return(0)
|
||||
).and_return(Pattern('/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1'))
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
|
||||
object,
|
||||
Pattern('/mnt/subvol1'),
|
||||
module.borgmatic.borg.pattern.Pattern(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||
),
|
||||
0,
|
||||
).once()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').with_args(
|
||||
object,
|
||||
module.borgmatic.borg.pattern.Pattern(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1/.borgmatic-snapshot-1234',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1/.borgmatic-snapshot',
|
||||
Pattern_type.NO_RECURSE,
|
||||
Pattern_style.FNMATCH,
|
||||
),
|
||||
@@ -650,13 +624,10 @@ def test_dump_data_sources_with_dry_run_skips_snapshot_and_patterns_update():
|
||||
(module.Subvolume('/mnt/subvol1', contained_patterns=(Pattern('/mnt/subvol1'),)),),
|
||||
)
|
||||
flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
)
|
||||
flexmock(module).should_receive('snapshot_subvolume').never()
|
||||
flexmock(module).should_receive('make_snapshot_exclude_pattern').never()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
|
||||
'get_last_pattern_index'
|
||||
).never()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').never()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').never()
|
||||
|
||||
@@ -682,9 +653,6 @@ def test_dump_data_sources_without_matching_subvolumes_skips_snapshot_and_patter
|
||||
flexmock(module).should_receive('make_snapshot_path').never()
|
||||
flexmock(module).should_receive('snapshot_subvolume').never()
|
||||
flexmock(module).should_receive('make_snapshot_exclude_pattern').never()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
|
||||
'get_last_pattern_index'
|
||||
).never()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').never()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('inject_pattern').never()
|
||||
|
||||
@@ -712,91 +680,59 @@ def test_remove_data_source_dumps_deletes_snapshots():
|
||||
),
|
||||
)
|
||||
flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/./mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/./mnt/subvol1',
|
||||
)
|
||||
flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol2').and_return(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/./mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/./mnt/subvol2',
|
||||
)
|
||||
flexmock(module.borgmatic.config.paths).should_receive(
|
||||
'replace_temporary_subdirectory_with_glob',
|
||||
).with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
|
||||
).and_return('/mnt/subvol1/.borgmatic-*/mnt/subvol1')
|
||||
flexmock(module.borgmatic.config.paths).should_receive(
|
||||
'replace_temporary_subdirectory_with_glob',
|
||||
).with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
|
||||
).and_return('/mnt/subvol2/.borgmatic-*/mnt/subvol2')
|
||||
flexmock(module.glob).should_receive('glob').with_args(
|
||||
'/mnt/subvol1/.borgmatic-*/mnt/subvol1',
|
||||
).and_return(
|
||||
(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot-5678/mnt/subvol1',
|
||||
),
|
||||
('/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',),
|
||||
)
|
||||
flexmock(module.glob).should_receive('glob').with_args(
|
||||
'/mnt/subvol2/.borgmatic-*/mnt/subvol2',
|
||||
).and_return(
|
||||
(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot-5678/mnt/subvol2',
|
||||
),
|
||||
('/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',),
|
||||
)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
).and_return(True)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-5678/mnt/subvol1',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
).and_return(True)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
).and_return(True)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-5678/mnt/subvol2',
|
||||
).and_return(False)
|
||||
flexmock(module).should_receive('delete_snapshot').with_args(
|
||||
'btrfs',
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
).once()
|
||||
flexmock(module).should_receive('delete_snapshot').with_args(
|
||||
'btrfs',
|
||||
'/mnt/subvol1/.borgmatic-snapshot-5678/mnt/subvol1',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
).once()
|
||||
flexmock(module).should_receive('delete_snapshot').with_args(
|
||||
'btrfs',
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
).once()
|
||||
flexmock(module).should_receive('delete_snapshot').with_args(
|
||||
'btrfs',
|
||||
'/mnt/subvol2/.borgmatic-snapshot-5678/mnt/subvol2',
|
||||
).never()
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234',
|
||||
'/mnt/subvol1/.borgmatic-snapshot',
|
||||
).and_return(True)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-5678',
|
||||
).and_return(True)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234',
|
||||
).and_return(True)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-5678',
|
||||
'/mnt/subvol2/.borgmatic-snapshot',
|
||||
).and_return(True)
|
||||
flexmock(module.shutil).should_receive('rmtree').with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234',
|
||||
'/mnt/subvol1/.borgmatic-snapshot',
|
||||
).once()
|
||||
flexmock(module.shutil).should_receive('rmtree').with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-5678',
|
||||
'/mnt/subvol2/.borgmatic-snapshot',
|
||||
).once()
|
||||
flexmock(module.shutil).should_receive('rmtree').with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234',
|
||||
).once()
|
||||
flexmock(module.shutil).should_receive('rmtree').with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-5678',
|
||||
).never()
|
||||
|
||||
module.remove_data_source_dumps(
|
||||
hook_config=config['btrfs'],
|
||||
@@ -874,50 +810,50 @@ def test_remove_data_source_dumps_with_dry_run_skips_deletes():
|
||||
),
|
||||
)
|
||||
flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/./mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/./mnt/subvol1',
|
||||
)
|
||||
flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol2').and_return(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/./mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/./mnt/subvol2',
|
||||
)
|
||||
flexmock(module.borgmatic.config.paths).should_receive(
|
||||
'replace_temporary_subdirectory_with_glob',
|
||||
).with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
|
||||
).and_return('/mnt/subvol1/.borgmatic-*/mnt/subvol1')
|
||||
flexmock(module.borgmatic.config.paths).should_receive(
|
||||
'replace_temporary_subdirectory_with_glob',
|
||||
).with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
|
||||
).and_return('/mnt/subvol2/.borgmatic-*/mnt/subvol2')
|
||||
flexmock(module.glob).should_receive('glob').with_args(
|
||||
'/mnt/subvol1/.borgmatic-*/mnt/subvol1',
|
||||
).and_return(
|
||||
(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot-5678/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
),
|
||||
)
|
||||
flexmock(module.glob).should_receive('glob').with_args(
|
||||
'/mnt/subvol2/.borgmatic-*/mnt/subvol2',
|
||||
).and_return(
|
||||
(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot-5678/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
),
|
||||
)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
).and_return(True)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-5678/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
).and_return(True)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
).and_return(True)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-5678/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
).and_return(False)
|
||||
flexmock(module).should_receive('delete_snapshot').never()
|
||||
flexmock(module.shutil).should_receive('rmtree').never()
|
||||
@@ -959,21 +895,21 @@ def test_remove_data_source_without_snapshots_skips_deletes():
|
||||
),
|
||||
)
|
||||
flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/./mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/./mnt/subvol1',
|
||||
)
|
||||
flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol2').and_return(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/./mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/./mnt/subvol2',
|
||||
)
|
||||
flexmock(module.borgmatic.config.paths).should_receive(
|
||||
'replace_temporary_subdirectory_with_glob',
|
||||
).with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
|
||||
).and_return('/mnt/subvol1/.borgmatic-*/mnt/subvol1')
|
||||
flexmock(module.borgmatic.config.paths).should_receive(
|
||||
'replace_temporary_subdirectory_with_glob',
|
||||
).with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
|
||||
).and_return('/mnt/subvol2/.borgmatic-*/mnt/subvol2')
|
||||
flexmock(module.glob).should_receive('glob').and_return(())
|
||||
@@ -999,50 +935,50 @@ def test_remove_data_source_dumps_with_delete_snapshot_file_not_found_error_bail
|
||||
),
|
||||
)
|
||||
flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/./mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/./mnt/subvol1',
|
||||
)
|
||||
flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol2').and_return(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/./mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/./mnt/subvol2',
|
||||
)
|
||||
flexmock(module.borgmatic.config.paths).should_receive(
|
||||
'replace_temporary_subdirectory_with_glob',
|
||||
).with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
|
||||
).and_return('/mnt/subvol1/.borgmatic-*/mnt/subvol1')
|
||||
flexmock(module.borgmatic.config.paths).should_receive(
|
||||
'replace_temporary_subdirectory_with_glob',
|
||||
).with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
|
||||
).and_return('/mnt/subvol2/.borgmatic-*/mnt/subvol2')
|
||||
flexmock(module.glob).should_receive('glob').with_args(
|
||||
'/mnt/subvol1/.borgmatic-*/mnt/subvol1',
|
||||
).and_return(
|
||||
(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot-5678/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
),
|
||||
)
|
||||
flexmock(module.glob).should_receive('glob').with_args(
|
||||
'/mnt/subvol2/.borgmatic-*/mnt/subvol2',
|
||||
).and_return(
|
||||
(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot-5678/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
),
|
||||
)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
).and_return(True)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-5678/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
).and_return(True)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
).and_return(True)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-5678/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
).and_return(False)
|
||||
flexmock(module).should_receive('delete_snapshot').and_raise(FileNotFoundError)
|
||||
flexmock(module.shutil).should_receive('rmtree').never()
|
||||
@@ -1065,50 +1001,50 @@ def test_remove_data_source_dumps_with_delete_snapshot_called_process_error_bail
|
||||
),
|
||||
)
|
||||
flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol1').and_return(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/./mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/./mnt/subvol1',
|
||||
)
|
||||
flexmock(module).should_receive('make_snapshot_path').with_args('/mnt/subvol2').and_return(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/./mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/./mnt/subvol2',
|
||||
)
|
||||
flexmock(module.borgmatic.config.paths).should_receive(
|
||||
'replace_temporary_subdirectory_with_glob',
|
||||
).with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
|
||||
).and_return('/mnt/subvol1/.borgmatic-*/mnt/subvol1')
|
||||
flexmock(module.borgmatic.config.paths).should_receive(
|
||||
'replace_temporary_subdirectory_with_glob',
|
||||
).with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
|
||||
).and_return('/mnt/subvol2/.borgmatic-*/mnt/subvol2')
|
||||
flexmock(module.glob).should_receive('glob').with_args(
|
||||
'/mnt/subvol1/.borgmatic-*/mnt/subvol1',
|
||||
).and_return(
|
||||
(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot-5678/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
),
|
||||
)
|
||||
flexmock(module.glob).should_receive('glob').with_args(
|
||||
'/mnt/subvol2/.borgmatic-*/mnt/subvol2',
|
||||
).and_return(
|
||||
(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot-5678/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
),
|
||||
)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-1234/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
).and_return(True)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol1/.borgmatic-snapshot-5678/mnt/subvol1',
|
||||
'/mnt/subvol1/.borgmatic-snapshot/mnt/subvol1',
|
||||
).and_return(True)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-1234/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
).and_return(True)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/mnt/subvol2/.borgmatic-snapshot-5678/mnt/subvol2',
|
||||
'/mnt/subvol2/.borgmatic-snapshot/mnt/subvol2',
|
||||
).and_return(False)
|
||||
flexmock(module).should_receive('delete_snapshot').and_raise(
|
||||
module.subprocess.CalledProcessError(1, 'command', 'error'),
|
||||
@@ -1131,40 +1067,29 @@ def test_remove_data_source_dumps_with_root_subvolume_skips_duplicate_removal():
|
||||
)
|
||||
|
||||
flexmock(module).should_receive('make_snapshot_path').with_args('/').and_return(
|
||||
'/.borgmatic-snapshot-1234',
|
||||
'/.borgmatic-snapshot',
|
||||
)
|
||||
|
||||
flexmock(module.borgmatic.config.paths).should_receive(
|
||||
'replace_temporary_subdirectory_with_glob',
|
||||
).with_args(
|
||||
'/.borgmatic-snapshot-1234',
|
||||
'/.borgmatic-snapshot',
|
||||
temporary_directory_prefix=module.BORGMATIC_SNAPSHOT_PREFIX,
|
||||
).and_return('/.borgmatic-*')
|
||||
|
||||
flexmock(module.glob).should_receive('glob').with_args('/.borgmatic-*').and_return(
|
||||
('/.borgmatic-snapshot-1234', '/.borgmatic-snapshot-5678'),
|
||||
('/.borgmatic-snapshot', '/.borgmatic-snapshot'),
|
||||
)
|
||||
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/.borgmatic-snapshot-1234'
|
||||
).and_return(
|
||||
True,
|
||||
).and_return(False)
|
||||
flexmock(module.os.path).should_receive('isdir').with_args(
|
||||
'/.borgmatic-snapshot-5678'
|
||||
).and_return(
|
||||
True,
|
||||
flexmock(module.os.path).should_receive('isdir').with_args('/.borgmatic-snapshot').and_return(
|
||||
True
|
||||
).and_return(False)
|
||||
|
||||
flexmock(module).should_receive('delete_snapshot').with_args(
|
||||
'btrfs', '/.borgmatic-snapshot-1234'
|
||||
).once()
|
||||
flexmock(module).should_receive('delete_snapshot').with_args(
|
||||
'btrfs', '/.borgmatic-snapshot-5678'
|
||||
'btrfs', '/.borgmatic-snapshot'
|
||||
).once()
|
||||
|
||||
flexmock(module.os.path).should_receive('isdir').with_args('').and_return(False)
|
||||
|
||||
flexmock(module.shutil).should_receive('rmtree').never()
|
||||
|
||||
module.remove_data_source_dumps(
|
||||
|
||||
@@ -203,50 +203,23 @@ def test_inject_pattern_with_root_pattern_prepends_it_along_with_corresponding_i
|
||||
]
|
||||
|
||||
|
||||
def test_get_last_pattern_index_with_ordered_subset_patterns_finds_last_one():
|
||||
def test_inject_pattern_with_root_pattern_and_override_excludes_false_omits_include_pattern():
|
||||
patterns = [
|
||||
module.borgmatic.borg.pattern.Pattern('/foo'),
|
||||
module.borgmatic.borg.pattern.Pattern('/bar'),
|
||||
module.borgmatic.borg.pattern.Pattern('/baz'),
|
||||
module.borgmatic.borg.pattern.Pattern('/quux'),
|
||||
]
|
||||
patterns_subset = [
|
||||
module.borgmatic.borg.pattern.Pattern('/bar'),
|
||||
module.borgmatic.borg.pattern.Pattern('/baz'),
|
||||
module.borgmatic.borg.pattern.Pattern('/etc'),
|
||||
module.borgmatic.borg.pattern.Pattern('/var'),
|
||||
]
|
||||
|
||||
assert module.get_last_pattern_index(patterns, patterns_subset) == 2
|
||||
module.inject_pattern(
|
||||
patterns,
|
||||
module.borgmatic.borg.pattern.Pattern('/foo/bar'),
|
||||
override_excludes=False,
|
||||
)
|
||||
|
||||
|
||||
def test_get_last_pattern_index_with_unordered_subset_patterns_finds_last_one():
|
||||
patterns = [
|
||||
module.borgmatic.borg.pattern.Pattern('/foo'),
|
||||
module.borgmatic.borg.pattern.Pattern('/bar'),
|
||||
module.borgmatic.borg.pattern.Pattern('/baz'),
|
||||
module.borgmatic.borg.pattern.Pattern('/quux'),
|
||||
assert patterns == [
|
||||
module.borgmatic.borg.pattern.Pattern('/foo/bar'),
|
||||
module.borgmatic.borg.pattern.Pattern('/etc'),
|
||||
module.borgmatic.borg.pattern.Pattern('/var'),
|
||||
]
|
||||
patterns_subset = [
|
||||
module.borgmatic.borg.pattern.Pattern('/baz'),
|
||||
module.borgmatic.borg.pattern.Pattern('/bar'),
|
||||
]
|
||||
|
||||
assert module.get_last_pattern_index(patterns, patterns_subset) == 2
|
||||
|
||||
|
||||
def test_get_last_pattern_index_with_unknown_subset_patterns_skips_it():
|
||||
patterns = [
|
||||
module.borgmatic.borg.pattern.Pattern('/foo'),
|
||||
module.borgmatic.borg.pattern.Pattern('/bar'),
|
||||
module.borgmatic.borg.pattern.Pattern('/baz'),
|
||||
module.borgmatic.borg.pattern.Pattern('/quux'),
|
||||
]
|
||||
patterns_subset = [
|
||||
module.borgmatic.borg.pattern.Pattern('/baz'),
|
||||
module.borgmatic.borg.pattern.Pattern('/unknown'),
|
||||
module.borgmatic.borg.pattern.Pattern('/bar'),
|
||||
]
|
||||
|
||||
assert module.get_last_pattern_index(patterns, patterns_subset) == 2
|
||||
|
||||
|
||||
def test_replace_pattern_swaps_out_pattern_in_place():
|
||||
@@ -263,7 +236,6 @@ def test_replace_pattern_swaps_out_pattern_in_place():
|
||||
'/foo/bar',
|
||||
type=module.borgmatic.borg.pattern.Pattern_type.EXCLUDE,
|
||||
),
|
||||
0,
|
||||
)
|
||||
|
||||
assert patterns == [
|
||||
@@ -283,39 +255,13 @@ def test_replace_pattern_with_unknown_pattern_falls_back_to_injecting():
|
||||
module.borgmatic.borg.pattern.Pattern('/lib'),
|
||||
]
|
||||
flexmock(module).should_receive('inject_pattern').with_args(
|
||||
patterns, module.borgmatic.borg.pattern.Pattern('/foo/bar')
|
||||
patterns,
|
||||
module.borgmatic.borg.pattern.Pattern('/foo/bar'),
|
||||
override_excludes=False,
|
||||
).once()
|
||||
|
||||
module.replace_pattern(
|
||||
patterns,
|
||||
module.borgmatic.borg.pattern.Pattern('/unknown'),
|
||||
module.borgmatic.borg.pattern.Pattern('/foo/bar'),
|
||||
0,
|
||||
)
|
||||
|
||||
|
||||
def test_replace_pattern_with_root_pattern_swaps_it_in_along_with_corresponding_include_pattern():
|
||||
patterns = [
|
||||
module.borgmatic.borg.pattern.Pattern('/etc'),
|
||||
module.borgmatic.borg.pattern.Pattern('/var'),
|
||||
module.borgmatic.borg.pattern.Pattern('/lib'),
|
||||
module.borgmatic.borg.pattern.Pattern('/run'),
|
||||
]
|
||||
|
||||
module.replace_pattern(
|
||||
patterns,
|
||||
module.borgmatic.borg.pattern.Pattern('/var'),
|
||||
module.borgmatic.borg.pattern.Pattern('/foo/bar'),
|
||||
2,
|
||||
)
|
||||
|
||||
assert patterns == [
|
||||
module.borgmatic.borg.pattern.Pattern('/etc'),
|
||||
module.borgmatic.borg.pattern.Pattern('/foo/bar'),
|
||||
module.borgmatic.borg.pattern.Pattern('/lib'),
|
||||
module.borgmatic.borg.pattern.Pattern(
|
||||
'/foo/bar',
|
||||
type=module.borgmatic.borg.pattern.Pattern_type.INCLUDE,
|
||||
),
|
||||
module.borgmatic.borg.pattern.Pattern('/run'),
|
||||
]
|
||||
|
||||
@@ -360,9 +360,6 @@ def test_dump_data_sources_snapshots_and_mounts_and_replaces_patterns():
|
||||
logical_volumes[1],
|
||||
'/run/borgmatic',
|
||||
).and_return(Pattern('/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2'))
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
|
||||
'get_last_pattern_index'
|
||||
).and_return(0)
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
|
||||
object,
|
||||
Pattern('/mnt/lvolume1/subdir'),
|
||||
@@ -370,7 +367,6 @@ def test_dump_data_sources_snapshots_and_mounts_and_replaces_patterns():
|
||||
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume1/subdir',
|
||||
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||
),
|
||||
0,
|
||||
).once()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
|
||||
object,
|
||||
@@ -379,7 +375,6 @@ def test_dump_data_sources_snapshots_and_mounts_and_replaces_patterns():
|
||||
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2',
|
||||
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||
),
|
||||
0,
|
||||
).once()
|
||||
|
||||
assert (
|
||||
@@ -401,9 +396,6 @@ def test_dump_data_sources_with_no_logical_volumes_skips_snapshots():
|
||||
flexmock(module).should_receive('get_logical_volumes').and_return(())
|
||||
flexmock(module).should_receive('snapshot_logical_volume').never()
|
||||
flexmock(module).should_receive('mount_snapshot').never()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
|
||||
'get_last_pattern_index'
|
||||
).and_return(0)
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').never()
|
||||
|
||||
assert (
|
||||
@@ -485,9 +477,6 @@ def test_dump_data_sources_uses_snapshot_size_for_snapshot():
|
||||
logical_volumes[1],
|
||||
'/run/borgmatic',
|
||||
).and_return(Pattern('/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2'))
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
|
||||
'get_last_pattern_index'
|
||||
).and_return(0)
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
|
||||
object,
|
||||
Pattern('/mnt/lvolume1/subdir'),
|
||||
@@ -495,7 +484,6 @@ def test_dump_data_sources_uses_snapshot_size_for_snapshot():
|
||||
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume1/subdir',
|
||||
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||
),
|
||||
0,
|
||||
).once()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
|
||||
object,
|
||||
@@ -504,7 +492,6 @@ def test_dump_data_sources_uses_snapshot_size_for_snapshot():
|
||||
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2',
|
||||
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||
),
|
||||
0,
|
||||
).once()
|
||||
|
||||
assert (
|
||||
@@ -593,9 +580,6 @@ def test_dump_data_sources_uses_custom_commands():
|
||||
logical_volumes[1],
|
||||
'/run/borgmatic',
|
||||
).and_return(Pattern('/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2'))
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
|
||||
'get_last_pattern_index'
|
||||
).and_return(0)
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
|
||||
object,
|
||||
Pattern('/mnt/lvolume1/subdir'),
|
||||
@@ -603,7 +587,6 @@ def test_dump_data_sources_uses_custom_commands():
|
||||
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume1/subdir',
|
||||
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||
),
|
||||
0,
|
||||
).once()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
|
||||
object,
|
||||
@@ -612,7 +595,6 @@ def test_dump_data_sources_uses_custom_commands():
|
||||
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2',
|
||||
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||
),
|
||||
0,
|
||||
).once()
|
||||
|
||||
assert (
|
||||
@@ -651,9 +633,6 @@ def test_dump_data_sources_with_dry_run_skips_snapshots_and_does_not_touch_patte
|
||||
flexmock(module).should_receive('snapshot_logical_volume').never()
|
||||
flexmock(module).should_receive('get_snapshots').never()
|
||||
flexmock(module).should_receive('mount_snapshot').never()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
|
||||
'get_last_pattern_index'
|
||||
).never()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').never()
|
||||
|
||||
assert (
|
||||
@@ -735,9 +714,6 @@ def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained
|
||||
logical_volumes[1],
|
||||
'/run/borgmatic',
|
||||
).and_return(Pattern('/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2'))
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
|
||||
'get_last_pattern_index'
|
||||
).and_return(0)
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
|
||||
object,
|
||||
Pattern('/mnt/lvolume1/subdir'),
|
||||
@@ -745,7 +721,6 @@ def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained
|
||||
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume1/subdir',
|
||||
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||
),
|
||||
0,
|
||||
).once()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').with_args(
|
||||
object,
|
||||
@@ -754,7 +729,6 @@ def test_dump_data_sources_ignores_mismatch_between_given_patterns_and_contained
|
||||
'/run/borgmatic/lvm_snapshots/b33f/./mnt/lvolume2',
|
||||
source=module.borgmatic.borg.pattern.Pattern_source.HOOK,
|
||||
),
|
||||
0,
|
||||
).once()
|
||||
|
||||
assert (
|
||||
@@ -811,9 +785,6 @@ def test_dump_data_sources_with_missing_snapshot_errors():
|
||||
snapshot_name='lvolume2_borgmatic-1234',
|
||||
).never()
|
||||
flexmock(module).should_receive('mount_snapshot').never()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive(
|
||||
'get_last_pattern_index'
|
||||
).never()
|
||||
flexmock(module.borgmatic.hooks.data_source.config).should_receive('replace_pattern').never()
|
||||
|
||||
with pytest.raises(ValueError):
|
||||
|
||||
@@ -181,11 +181,15 @@ def test_database_names_to_dump_queries_mariadb_for_database_names():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
|
||||
'/path/to/working/dir'
|
||||
)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
(
|
||||
'mariadb',
|
||||
@@ -196,6 +200,7 @@ def test_database_names_to_dump_queries_mariadb_for_database_names():
|
||||
'show schemas',
|
||||
),
|
||||
environment=environment,
|
||||
working_directory='/path/to/working/dir',
|
||||
).and_return('foo\nbar\nmysql\n').once()
|
||||
|
||||
names = module.database_names_to_dump(
|
||||
@@ -216,11 +221,13 @@ def test_database_names_to_dump_with_database_name_all_and_skip_names_filters_ou
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
(
|
||||
'mariadb',
|
||||
@@ -231,6 +238,7 @@ def test_database_names_to_dump_with_database_name_all_and_skip_names_filters_ou
|
||||
'show schemas',
|
||||
),
|
||||
environment=environment,
|
||||
working_directory=None,
|
||||
).and_return('foo\nbar\nbaz\nmysql\n').once()
|
||||
|
||||
names = module.database_names_to_dump(
|
||||
@@ -245,13 +253,55 @@ def test_database_names_to_dump_with_database_name_all_and_skip_names_filters_ou
|
||||
assert names == ('baz',)
|
||||
|
||||
|
||||
def test_database_names_to_dump_runs_mariadb_with_socket_path():
|
||||
environment = flexmock()
|
||||
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
(
|
||||
'mariadb',
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--socket',
|
||||
'/socket',
|
||||
'--skip-column-names',
|
||||
'--batch',
|
||||
'--execute',
|
||||
'show schemas',
|
||||
),
|
||||
environment=environment,
|
||||
working_directory=None,
|
||||
).and_return('foo\nbar\nmysql\n').once()
|
||||
|
||||
names = module.database_names_to_dump(
|
||||
{'name': 'all', 'socket_path': '/socket'},
|
||||
{},
|
||||
'root',
|
||||
'trustsome1',
|
||||
environment,
|
||||
dry_run=False,
|
||||
)
|
||||
|
||||
assert names == ('foo', 'bar')
|
||||
|
||||
|
||||
def test_database_names_to_dump_with_environment_password_transport_skips_defaults_file_and_passes_user_flag():
|
||||
environment = flexmock()
|
||||
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').never()
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
(
|
||||
'mariadb',
|
||||
@@ -263,6 +313,7 @@ def test_database_names_to_dump_with_environment_password_transport_skips_defaul
|
||||
'show schemas',
|
||||
),
|
||||
environment=environment,
|
||||
working_directory=None,
|
||||
).and_return('foo\nbar\nmysql\n').once()
|
||||
|
||||
names = module.database_names_to_dump(
|
||||
@@ -283,11 +334,13 @@ def test_database_names_to_dump_runs_mariadb_with_tls():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
(
|
||||
'mariadb',
|
||||
@@ -299,6 +352,7 @@ def test_database_names_to_dump_runs_mariadb_with_tls():
|
||||
'show schemas',
|
||||
),
|
||||
environment=environment,
|
||||
working_directory=None,
|
||||
).and_return('foo\nbar\nmysql\n').once()
|
||||
|
||||
names = module.database_names_to_dump(
|
||||
@@ -319,11 +373,13 @@ def test_database_names_to_dump_runs_mariadb_without_tls():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
(
|
||||
'mariadb',
|
||||
@@ -335,6 +391,7 @@ def test_database_names_to_dump_runs_mariadb_without_tls():
|
||||
'show schemas',
|
||||
),
|
||||
environment=environment,
|
||||
working_directory=None,
|
||||
).and_return('foo\nbar\nmysql\n').once()
|
||||
|
||||
names = module.database_names_to_dump(
|
||||
@@ -694,11 +751,13 @@ def test_database_names_to_dump_runs_mariadb_with_list_options():
|
||||
('--skip-ssl',),
|
||||
'mariadb.cnf',
|
||||
)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
'mariadb.cnf',
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
(
|
||||
'mariadb',
|
||||
@@ -710,6 +769,7 @@ def test_database_names_to_dump_runs_mariadb_with_list_options():
|
||||
'show schemas',
|
||||
),
|
||||
environment=None,
|
||||
working_directory=None,
|
||||
).and_return('foo\nbar').once()
|
||||
|
||||
assert module.database_names_to_dump(database, {}, 'root', 'trustsome1', None, '') == (
|
||||
@@ -728,11 +788,13 @@ def test_database_names_to_dump_runs_non_default_mariadb_with_list_options():
|
||||
('--skip-ssl',),
|
||||
'mariadb.cnf',
|
||||
)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
'mariadb.cnf',
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
environment=None,
|
||||
full_command=(
|
||||
@@ -744,6 +806,7 @@ def test_database_names_to_dump_runs_non_default_mariadb_with_list_options():
|
||||
'--execute',
|
||||
'show schemas',
|
||||
),
|
||||
working_directory=None,
|
||||
).and_return('foo\nbar').once()
|
||||
|
||||
assert module.database_names_to_dump(database, {}, 'root', 'trustsome1', None, '') == (
|
||||
@@ -760,18 +823,21 @@ def test_execute_dump_command_runs_mariadb_dump():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
'mariadb-dump',
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--add-drop-database',
|
||||
'--single-transaction',
|
||||
'--databases',
|
||||
'foo',
|
||||
'--result-file',
|
||||
@@ -779,6 +845,7 @@ def test_execute_dump_command_runs_mariadb_dump():
|
||||
),
|
||||
environment=None,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -805,13 +872,16 @@ def test_execute_dump_command_with_environment_password_transport_skips_defaults
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').never()
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
'mariadb-dump',
|
||||
'--add-drop-database',
|
||||
'--single-transaction',
|
||||
'--user',
|
||||
'root',
|
||||
'--databases',
|
||||
@@ -821,6 +891,7 @@ def test_execute_dump_command_with_environment_password_transport_skips_defaults
|
||||
),
|
||||
environment=None,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -847,17 +918,20 @@ def test_execute_dump_command_runs_mariadb_dump_without_add_drop_database():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
'mariadb-dump',
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--single-transaction',
|
||||
'--databases',
|
||||
'foo',
|
||||
'--result-file',
|
||||
@@ -865,6 +939,7 @@ def test_execute_dump_command_runs_mariadb_dump_without_add_drop_database():
|
||||
),
|
||||
environment=None,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -891,18 +966,23 @@ def test_execute_dump_command_runs_mariadb_dump_with_hostname_and_port():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(
|
||||
'database.example.org'
|
||||
)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
'mariadb-dump',
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--add-drop-database',
|
||||
'--single-transaction',
|
||||
'--host',
|
||||
'database.example.org',
|
||||
'--port',
|
||||
@@ -916,6 +996,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_hostname_and_port():
|
||||
),
|
||||
environment=None,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -942,18 +1023,21 @@ def test_execute_dump_command_runs_mariadb_dump_with_tls():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
'mariadb-dump',
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--add-drop-database',
|
||||
'--single-transaction',
|
||||
'--ssl',
|
||||
'--databases',
|
||||
'foo',
|
||||
@@ -962,6 +1046,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_tls():
|
||||
),
|
||||
environment=None,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -988,18 +1073,21 @@ def test_execute_dump_command_runs_mariadb_dump_without_tls():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
'mariadb-dump',
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--add-drop-database',
|
||||
'--single-transaction',
|
||||
'--skip-ssl',
|
||||
'--databases',
|
||||
'foo',
|
||||
@@ -1008,6 +1096,7 @@ def test_execute_dump_command_runs_mariadb_dump_without_tls():
|
||||
),
|
||||
environment=None,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -1034,18 +1123,21 @@ def test_execute_dump_command_runs_mariadb_dump_with_username_and_password():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
'mariadb-dump',
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--add-drop-database',
|
||||
'--single-transaction',
|
||||
'--databases',
|
||||
'foo',
|
||||
'--result-file',
|
||||
@@ -1053,6 +1145,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_username_and_password():
|
||||
),
|
||||
environment={},
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -1079,12 +1172,14 @@ def test_execute_dump_command_runs_mariadb_dump_with_options():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return(('--stuff=such',), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
@@ -1092,6 +1187,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_options():
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--stuff=such',
|
||||
'--add-drop-database',
|
||||
'--single-transaction',
|
||||
'--databases',
|
||||
'foo',
|
||||
'--result-file',
|
||||
@@ -1099,6 +1195,7 @@ def test_execute_dump_command_runs_mariadb_dump_with_options():
|
||||
),
|
||||
environment=None,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -1125,12 +1222,14 @@ def test_execute_dump_command_runs_non_default_mariadb_dump_with_options():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return(('--stuff=such',), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
@@ -1138,6 +1237,7 @@ def test_execute_dump_command_runs_non_default_mariadb_dump_with_options():
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--stuff=such',
|
||||
'--add-drop-database',
|
||||
'--single-transaction',
|
||||
'--databases',
|
||||
'foo',
|
||||
'--result-file',
|
||||
@@ -1145,6 +1245,7 @@ def test_execute_dump_command_runs_non_default_mariadb_dump_with_options():
|
||||
),
|
||||
environment=None,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -1171,6 +1272,7 @@ def test_execute_dump_command_with_duplicate_dump_skips_mariadb_dump():
|
||||
flexmock(module.dump).should_receive('make_data_source_dump_filename').and_return('dump')
|
||||
flexmock(module.os.path).should_receive('exists').and_return(True)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
@@ -1202,6 +1304,7 @@ def test_execute_dump_command_with_dry_run_skips_mariadb_dump():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
@@ -1235,18 +1338,21 @@ def test_restore_data_source_dump_runs_mariadb_to_restore():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).and_return(())
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
('mariadb', '--batch'),
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1273,18 +1379,21 @@ def test_restore_data_source_dump_runs_mariadb_with_options():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return(('--harder',), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).and_return(())
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
('mariadb', '--harder', '--batch'),
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1313,18 +1422,21 @@ def test_restore_data_source_dump_runs_non_default_mariadb_with_options():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return(('--harder',), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).and_return(())
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
('custom_mariadb', '--harder', '--batch'),
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1351,12 +1463,16 @@ def test_restore_data_source_dump_runs_mariadb_with_hostname_and_port():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').replace_with(
|
||||
lambda option, data_source, connection_params=None, restore=False: data_source.get(option)
|
||||
)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).and_return(())
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
(
|
||||
'mariadb',
|
||||
@@ -1372,6 +1488,55 @@ def test_restore_data_source_dump_runs_mariadb_with_hostname_and_port():
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
hook_config,
|
||||
{},
|
||||
data_source=hook_config[0],
|
||||
dry_run=False,
|
||||
extract_process=extract_process,
|
||||
connection_params={
|
||||
'hostname': None,
|
||||
'port': None,
|
||||
'username': None,
|
||||
'password': None,
|
||||
},
|
||||
borgmatic_runtime_directory='/run/borgmatic',
|
||||
)
|
||||
|
||||
|
||||
def test_restore_data_source_dump_runs_mariadb_with_socket_path():
|
||||
hook_config = [{'name': 'foo', 'socket_path': '/socket'}]
|
||||
extract_process = flexmock(stdout=flexmock())
|
||||
|
||||
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').replace_with(
|
||||
lambda option, data_source, connection_params=None, restore=False: data_source.get(option)
|
||||
)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).and_return(())
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
(
|
||||
'mariadb',
|
||||
'--batch',
|
||||
'--socket',
|
||||
'/socket',
|
||||
),
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1398,12 +1563,16 @@ def test_restore_data_source_dump_runs_mariadb_with_tls():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').replace_with(
|
||||
lambda option, data_source, connection_params=None, restore=False: data_source.get(option)
|
||||
)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).and_return(())
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
(
|
||||
'mariadb',
|
||||
@@ -1414,6 +1583,7 @@ def test_restore_data_source_dump_runs_mariadb_with_tls():
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1440,12 +1610,16 @@ def test_restore_data_source_dump_runs_mariadb_without_tls():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').replace_with(
|
||||
lambda option, data_source, connection_params=None, restore=False: data_source.get(option)
|
||||
)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).and_return(())
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
(
|
||||
'mariadb',
|
||||
@@ -1456,6 +1630,7 @@ def test_restore_data_source_dump_runs_mariadb_without_tls():
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1482,18 +1657,23 @@ def test_restore_data_source_dump_runs_mariadb_with_username_and_password():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').replace_with(
|
||||
lambda option, data_source, connection_params=None, restore=False: data_source.get(option)
|
||||
)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'root',
|
||||
'trustsome1',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
('mariadb', '--defaults-extra-file=/dev/fd/99', '--batch'),
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1527,16 +1707,21 @@ def test_restore_data_source_with_environment_password_transport_skips_defaults_
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').replace_with(
|
||||
lambda option, data_source, connection_params=None, restore=False: data_source.get(option)
|
||||
)
|
||||
flexmock(module).should_receive('make_defaults_file_options').never()
|
||||
flexmock(module.os).should_receive('environ').and_return(
|
||||
{'USER': 'root', 'MYSQL_PWD': 'trustsome1'},
|
||||
)
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
('mariadb', '--batch', '--user', 'root'),
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root', 'MYSQL_PWD': 'trustsome1'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1573,12 +1758,18 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').replace_with(
|
||||
lambda option, data_source, connection_params=None, restore=False: (
|
||||
connection_params or {}
|
||||
).get(option)
|
||||
)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'cliusername',
|
||||
'clipassword',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
(
|
||||
'mariadb',
|
||||
@@ -1595,6 +1786,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1635,12 +1827,18 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').replace_with(
|
||||
lambda option, data_source, connection_params=None, restore=False: data_source.get(
|
||||
f'restore_{option}'
|
||||
)
|
||||
)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
'restoreuser',
|
||||
'restorepass',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
(
|
||||
'mariadb',
|
||||
@@ -1658,6 +1856,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1683,6 +1882,7 @@ def test_restore_data_source_dump_with_dry_run_skips_restore():
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module).should_receive('parse_extra_options').and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module).should_receive('make_defaults_file_options').with_args(
|
||||
None,
|
||||
None,
|
||||
|
||||
@@ -31,12 +31,16 @@ def test_dump_data_sources_runs_mongodump_for_each_database():
|
||||
'databases/localhost/foo',
|
||||
).and_return('databases/localhost/bar')
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(
|
||||
'/path/to/working/dir'
|
||||
)
|
||||
|
||||
for name, process in zip(('foo', 'bar'), processes):
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
('mongodump', '--db', name, '--archive', '>', f'databases/localhost/{name}'),
|
||||
shell=True,
|
||||
run_to_completion=False,
|
||||
working_directory='/path/to/working/dir',
|
||||
).and_return(process).once()
|
||||
|
||||
flexmock(module.dump).should_receive('write_data_source_dumps_metadata').with_args(
|
||||
@@ -100,6 +104,7 @@ def test_dump_data_sources_runs_mongodump_with_hostname_and_port():
|
||||
'databases/database.example.org/foo',
|
||||
)
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
@@ -116,6 +121,7 @@ def test_dump_data_sources_runs_mongodump_with_hostname_and_port():
|
||||
),
|
||||
shell=True,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
flexmock(module.dump).should_receive('write_data_source_dumps_metadata').with_args(
|
||||
'/run/borgmatic',
|
||||
@@ -165,6 +171,7 @@ def test_dump_data_sources_runs_mongodump_with_username_and_password():
|
||||
'/dev/fd/99',
|
||||
)
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
@@ -183,6 +190,7 @@ def test_dump_data_sources_runs_mongodump_with_username_and_password():
|
||||
),
|
||||
shell=True,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
flexmock(module.dump).should_receive('write_data_source_dumps_metadata').with_args(
|
||||
'/run/borgmatic',
|
||||
@@ -217,10 +225,12 @@ def test_dump_data_sources_runs_mongodump_with_directory_format():
|
||||
)
|
||||
flexmock(module.dump).should_receive('create_parent_directory_for_dump')
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump').never()
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
('mongodump', '--out', 'databases/localhost/foo', '--db', 'foo'),
|
||||
shell=True,
|
||||
working_directory=None,
|
||||
).and_return(flexmock()).once()
|
||||
flexmock(module.dump).should_receive('write_data_source_dumps_metadata').with_args(
|
||||
'/run/borgmatic',
|
||||
@@ -258,6 +268,7 @@ def test_dump_data_sources_runs_mongodump_with_options():
|
||||
'databases/localhost/foo',
|
||||
)
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
@@ -271,6 +282,7 @@ def test_dump_data_sources_runs_mongodump_with_options():
|
||||
),
|
||||
shell=True,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
flexmock(module.dump).should_receive('write_data_source_dumps_metadata').with_args(
|
||||
'/run/borgmatic',
|
||||
@@ -305,11 +317,13 @@ def test_dump_data_sources_runs_mongodumpall_for_all_databases():
|
||||
'databases/localhost/all',
|
||||
)
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
('mongodump', '--archive', '>', 'databases/localhost/all'),
|
||||
shell=True,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
flexmock(module.dump).should_receive('write_data_source_dumps_metadata').with_args(
|
||||
'/run/borgmatic',
|
||||
@@ -373,11 +387,13 @@ def test_restore_data_source_dump_runs_mongorestore():
|
||||
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
['mongorestore', '--archive', '--drop'],
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -407,6 +423,7 @@ def test_restore_data_source_dump_runs_mongorestore_with_hostname_and_port():
|
||||
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
[
|
||||
'mongorestore',
|
||||
@@ -420,6 +437,7 @@ def test_restore_data_source_dump_runs_mongorestore_with_hostname_and_port():
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -458,6 +476,7 @@ def test_restore_data_source_dump_runs_mongorestore_with_username_and_password()
|
||||
flexmock(module).should_receive('make_password_config_file').with_args('trustsome1').and_return(
|
||||
'/dev/fd/99',
|
||||
)
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
[
|
||||
'mongorestore',
|
||||
@@ -473,6 +492,7 @@ def test_restore_data_source_dump_runs_mongorestore_with_username_and_password()
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -515,6 +535,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
|
||||
flexmock(module).should_receive('make_password_config_file').with_args(
|
||||
'clipassword',
|
||||
).and_return('/dev/fd/99')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
[
|
||||
'mongorestore',
|
||||
@@ -534,6 +555,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -576,6 +598,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
|
||||
flexmock(module).should_receive('make_password_config_file').with_args(
|
||||
'restorepass',
|
||||
).and_return('/dev/fd/99')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
[
|
||||
'mongorestore',
|
||||
@@ -595,6 +618,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -622,11 +646,13 @@ def test_restore_data_source_dump_runs_mongorestore_with_options():
|
||||
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
['mongorestore', '--archive', '--drop', '--harder'],
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -654,6 +680,7 @@ def test_restore_databases_dump_runs_mongorestore_with_schemas():
|
||||
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
[
|
||||
'mongorestore',
|
||||
@@ -667,6 +694,7 @@ def test_restore_databases_dump_runs_mongorestore_with_schemas():
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -694,11 +722,13 @@ def test_restore_data_source_dump_runs_psql_for_all_database_dump():
|
||||
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
['mongorestore', '--archive'],
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -751,11 +781,13 @@ def test_restore_data_source_dump_without_extract_process_restores_from_disk():
|
||||
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
['mongorestore', '--dir', '/dump/path', '--drop'],
|
||||
processes=[],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=None,
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -785,6 +817,7 @@ def test_dump_data_sources_uses_custom_mongodump_command():
|
||||
'databases/localhost/foo',
|
||||
)
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
@@ -797,6 +830,7 @@ def test_dump_data_sources_uses_custom_mongodump_command():
|
||||
),
|
||||
shell=True,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
flexmock(module.dump).should_receive('write_data_source_dumps_metadata').with_args(
|
||||
'/run/borgmatic',
|
||||
@@ -855,6 +889,7 @@ def test_restore_data_source_dump_uses_custom_mongorestore_command():
|
||||
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
[
|
||||
'custom_mongorestore', # Should use custom command instead of default
|
||||
@@ -865,6 +900,7 @@ def test_restore_data_source_dump_uses_custom_mongorestore_command():
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
|
||||
@@ -65,9 +65,11 @@ def test_database_names_to_dump_queries_mysql_for_database_names():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
(
|
||||
'mysql',
|
||||
@@ -78,6 +80,7 @@ def test_database_names_to_dump_queries_mysql_for_database_names():
|
||||
'show schemas',
|
||||
),
|
||||
environment=environment,
|
||||
working_directory=None,
|
||||
).and_return('foo\nbar\nmysql\n').once()
|
||||
|
||||
names = module.database_names_to_dump(
|
||||
@@ -100,6 +103,7 @@ def test_database_names_to_dump_with_database_name_all_and_skip_names_filters_ou
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options'
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options'
|
||||
).with_args(
|
||||
@@ -107,6 +111,7 @@ def test_database_names_to_dump_with_database_name_all_and_skip_names_filters_ou
|
||||
'trustsome1',
|
||||
None,
|
||||
).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
(
|
||||
'mysql',
|
||||
@@ -117,6 +122,7 @@ def test_database_names_to_dump_with_database_name_all_and_skip_names_filters_ou
|
||||
'show schemas',
|
||||
),
|
||||
environment=environment,
|
||||
working_directory=None,
|
||||
).and_return('foo\nbar\nbaz\nmysql\n').once()
|
||||
|
||||
names = module.database_names_to_dump(
|
||||
@@ -131,6 +137,46 @@ def test_database_names_to_dump_with_database_name_all_and_skip_names_filters_ou
|
||||
assert names == ('baz',)
|
||||
|
||||
|
||||
def test_database_names_to_dump_runs_mysql_with_socket_path():
|
||||
environment = flexmock()
|
||||
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
(
|
||||
'mysql',
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--socket',
|
||||
'/socket',
|
||||
'--skip-column-names',
|
||||
'--batch',
|
||||
'--execute',
|
||||
'show schemas',
|
||||
),
|
||||
environment=environment,
|
||||
working_directory=None,
|
||||
).and_return('foo\nbar\nmysql\n').once()
|
||||
|
||||
names = module.database_names_to_dump(
|
||||
{'name': 'all', 'socket_path': '/socket'},
|
||||
{},
|
||||
'root',
|
||||
'trustsome1',
|
||||
environment,
|
||||
dry_run=False,
|
||||
)
|
||||
|
||||
assert names == ('foo', 'bar')
|
||||
|
||||
|
||||
def test_database_names_to_dump_with_environment_password_transport_skips_defaults_file_and_passes_user_flag():
|
||||
environment = flexmock()
|
||||
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
|
||||
@@ -139,9 +185,11 @@ def test_database_names_to_dump_with_environment_password_transport_skips_defaul
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).never()
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
(
|
||||
'mysql',
|
||||
@@ -153,6 +201,7 @@ def test_database_names_to_dump_with_environment_password_transport_skips_defaul
|
||||
'show schemas',
|
||||
),
|
||||
environment=environment,
|
||||
working_directory=None,
|
||||
).and_return('foo\nbar\nmysql\n').once()
|
||||
|
||||
names = module.database_names_to_dump(
|
||||
@@ -175,9 +224,11 @@ def test_database_names_to_dump_runs_mysql_with_tls():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
(
|
||||
'mysql',
|
||||
@@ -189,6 +240,7 @@ def test_database_names_to_dump_runs_mysql_with_tls():
|
||||
'show schemas',
|
||||
),
|
||||
environment=environment,
|
||||
working_directory=None,
|
||||
).and_return('foo\nbar\nmysql\n').once()
|
||||
|
||||
names = module.database_names_to_dump(
|
||||
@@ -211,9 +263,11 @@ def test_database_names_to_dump_runs_mysql_without_tls():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
(
|
||||
'mysql',
|
||||
@@ -225,6 +279,7 @@ def test_database_names_to_dump_runs_mysql_without_tls():
|
||||
'show schemas',
|
||||
),
|
||||
environment=environment,
|
||||
working_directory=None,
|
||||
).and_return('foo\nbar\nmysql\n').once()
|
||||
|
||||
names = module.database_names_to_dump(
|
||||
@@ -581,9 +636,11 @@ def test_database_names_to_dump_runs_mysql_with_list_options():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return(('--skip-ssl',), 'my.cnf')
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', 'my.cnf').and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
(
|
||||
'mysql',
|
||||
@@ -595,6 +652,7 @@ def test_database_names_to_dump_runs_mysql_with_list_options():
|
||||
'show schemas',
|
||||
),
|
||||
environment=None,
|
||||
working_directory=None,
|
||||
).and_return('foo\nbar').once()
|
||||
|
||||
assert module.database_names_to_dump(database, {}, 'root', 'trustsome1', None, '') == (
|
||||
@@ -612,9 +670,11 @@ def test_database_names_to_dump_runs_non_default_mysql_with_list_options():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return(('--skip-ssl',), 'my.cnf')
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', 'my.cnf').and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_and_capture_output').with_args(
|
||||
environment=None,
|
||||
full_command=(
|
||||
@@ -626,6 +686,7 @@ def test_database_names_to_dump_runs_non_default_mysql_with_list_options():
|
||||
'--execute',
|
||||
'show schemas',
|
||||
),
|
||||
working_directory=None,
|
||||
).and_return('foo\nbar').once()
|
||||
|
||||
assert module.database_names_to_dump(database, {}, 'root', 'trustsome1', None, '') == (
|
||||
@@ -644,16 +705,19 @@ def test_execute_dump_command_runs_mysqldump():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
'mysqldump',
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--add-drop-database',
|
||||
'--single-transaction',
|
||||
'--databases',
|
||||
'foo',
|
||||
'--result-file',
|
||||
@@ -661,6 +725,7 @@ def test_execute_dump_command_runs_mysqldump():
|
||||
),
|
||||
environment=None,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -689,15 +754,18 @@ def test_execute_dump_command_with_environment_password_transport_skips_defaults
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).never()
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
'mysqldump',
|
||||
'--add-drop-database',
|
||||
'--single-transaction',
|
||||
'--user',
|
||||
'root',
|
||||
'--databases',
|
||||
@@ -707,6 +775,7 @@ def test_execute_dump_command_with_environment_password_transport_skips_defaults
|
||||
),
|
||||
environment=None,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -735,15 +804,18 @@ def test_execute_dump_command_runs_mysqldump_without_add_drop_database():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
'mysqldump',
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--single-transaction',
|
||||
'--databases',
|
||||
'foo',
|
||||
'--result-file',
|
||||
@@ -751,6 +823,7 @@ def test_execute_dump_command_runs_mysqldump_without_add_drop_database():
|
||||
),
|
||||
environment=None,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -779,16 +852,21 @@ def test_execute_dump_command_runs_mysqldump_with_hostname_and_port():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(
|
||||
'database.example.org'
|
||||
)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
'mysqldump',
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--add-drop-database',
|
||||
'--single-transaction',
|
||||
'--host',
|
||||
'database.example.org',
|
||||
'--port',
|
||||
@@ -802,6 +880,7 @@ def test_execute_dump_command_runs_mysqldump_with_hostname_and_port():
|
||||
),
|
||||
environment=None,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -830,16 +909,19 @@ def test_execute_dump_command_runs_mysqldump_with_tls():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
'mysqldump',
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--add-drop-database',
|
||||
'--single-transaction',
|
||||
'--ssl',
|
||||
'--databases',
|
||||
'foo',
|
||||
@@ -848,6 +930,7 @@ def test_execute_dump_command_runs_mysqldump_with_tls():
|
||||
),
|
||||
environment=None,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -876,16 +959,19 @@ def test_execute_dump_command_runs_mysqldump_without_tls():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
'mysqldump',
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--add-drop-database',
|
||||
'--single-transaction',
|
||||
'--skip-ssl',
|
||||
'--databases',
|
||||
'foo',
|
||||
@@ -894,6 +980,7 @@ def test_execute_dump_command_runs_mysqldump_without_tls():
|
||||
),
|
||||
environment=None,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -922,16 +1009,19 @@ def test_execute_dump_command_runs_mysqldump_with_username_and_password():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
'mysqldump',
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--add-drop-database',
|
||||
'--single-transaction',
|
||||
'--databases',
|
||||
'foo',
|
||||
'--result-file',
|
||||
@@ -939,6 +1029,7 @@ def test_execute_dump_command_runs_mysqldump_with_username_and_password():
|
||||
),
|
||||
environment={},
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -967,10 +1058,12 @@ def test_execute_dump_command_runs_mysqldump_with_options():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return(('--stuff=such',), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
@@ -978,6 +1071,7 @@ def test_execute_dump_command_runs_mysqldump_with_options():
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--stuff=such',
|
||||
'--add-drop-database',
|
||||
'--single-transaction',
|
||||
'--databases',
|
||||
'foo',
|
||||
'--result-file',
|
||||
@@ -985,6 +1079,7 @@ def test_execute_dump_command_runs_mysqldump_with_options():
|
||||
),
|
||||
environment=None,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -1013,16 +1108,19 @@ def test_execute_dump_command_runs_non_default_mysqldump():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.dump).should_receive('create_named_pipe_for_dump')
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
|
||||
flexmock(module).should_receive('execute_command').with_args(
|
||||
(
|
||||
'custom_mysqldump', # Custom MySQL dump command
|
||||
'--defaults-extra-file=/dev/fd/99',
|
||||
'--add-drop-database',
|
||||
'--single-transaction',
|
||||
'--databases',
|
||||
'foo',
|
||||
'--result-file',
|
||||
@@ -1030,6 +1128,7 @@ def test_execute_dump_command_runs_non_default_mysqldump():
|
||||
),
|
||||
environment=None,
|
||||
run_to_completion=False,
|
||||
working_directory=None,
|
||||
).and_return(process).once()
|
||||
|
||||
assert (
|
||||
@@ -1057,6 +1156,7 @@ def test_execute_dump_command_with_duplicate_dump_skips_mysqldump():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
@@ -1088,6 +1188,7 @@ def test_execute_dump_command_with_dry_run_skips_mysqldump():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
@@ -1121,16 +1222,19 @@ def test_restore_data_source_dump_runs_mysql_to_restore():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args(None, None, None).and_return(())
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
('mysql', '--batch'),
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1159,16 +1263,19 @@ def test_restore_data_source_dump_runs_mysql_with_options():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return(('--harder',), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args(None, None, None).and_return(())
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
('mysql', '--harder', '--batch'),
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1197,16 +1304,19 @@ def test_restore_data_source_dump_runs_non_default_mysql_with_options():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return(('--harder',), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args(None, None, None).and_return(())
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
('custom_mysql', '--harder', '--batch'),
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1235,10 +1345,14 @@ def test_restore_data_source_dump_runs_mysql_with_hostname_and_port():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').replace_with(
|
||||
lambda option, data_source, connection_params=None, restore=False: data_source.get(option)
|
||||
)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args(None, None, None).and_return(())
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
(
|
||||
'mysql',
|
||||
@@ -1254,6 +1368,59 @@ def test_restore_data_source_dump_runs_mysql_with_hostname_and_port():
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
hook_config,
|
||||
{},
|
||||
data_source=hook_config[0],
|
||||
dry_run=False,
|
||||
extract_process=extract_process,
|
||||
connection_params={
|
||||
'hostname': None,
|
||||
'port': None,
|
||||
'username': None,
|
||||
'password': None,
|
||||
},
|
||||
borgmatic_runtime_directory='/run/borgmatic',
|
||||
)
|
||||
|
||||
|
||||
def test_restore_data_source_dump_runs_mysql_with_socket_path():
|
||||
hook_config = [{'name': 'foo', 'socket_path': '/socket'}]
|
||||
extract_process = flexmock(stdout=flexmock())
|
||||
|
||||
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').replace_with(
|
||||
lambda option, data_source, connection_params=None, restore=False: data_source.get(option)
|
||||
)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options'
|
||||
).with_args(
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
).and_return(())
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
(
|
||||
'mysql',
|
||||
'--batch',
|
||||
'--socket',
|
||||
'/socket',
|
||||
),
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1282,10 +1449,14 @@ def test_restore_data_source_dump_runs_mysql_with_tls():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').replace_with(
|
||||
lambda option, data_source, connection_params=None, restore=False: data_source.get(option)
|
||||
)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args(None, None, None).and_return(())
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
(
|
||||
'mysql',
|
||||
@@ -1296,6 +1467,7 @@ def test_restore_data_source_dump_runs_mysql_with_tls():
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1321,6 +1493,9 @@ def test_restore_data_source_dump_runs_mysql_without_tls():
|
||||
flexmock(module.borgmatic.hooks.credential.parse).should_receive(
|
||||
'resolve_credential',
|
||||
).replace_with(lambda value, config: value)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').replace_with(
|
||||
lambda option, data_source, connection_params=None, restore=False: data_source.get(option)
|
||||
)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
@@ -1328,6 +1503,7 @@ def test_restore_data_source_dump_runs_mysql_without_tls():
|
||||
'make_defaults_file_options',
|
||||
).with_args(None, None, None).and_return(())
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
(
|
||||
'mysql',
|
||||
@@ -1338,6 +1514,7 @@ def test_restore_data_source_dump_runs_mysql_without_tls():
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1366,16 +1543,21 @@ def test_restore_data_source_dump_runs_mysql_with_username_and_password():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').replace_with(
|
||||
lambda option, data_source, connection_params=None, restore=False: data_source.get(option)
|
||||
)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('root', 'trustsome1', None).and_return(('--defaults-extra-file=/dev/fd/99',))
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
('mysql', '--defaults-extra-file=/dev/fd/99', '--batch'),
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1411,18 +1593,23 @@ def test_restore_data_source_with_environment_password_transport_skips_defaults_
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').replace_with(
|
||||
lambda option, data_source, connection_params=None, restore=False: data_source.get(option)
|
||||
)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).never()
|
||||
flexmock(module.os).should_receive('environ').and_return(
|
||||
{'USER': 'root', 'MYSQL_PWD': 'trustsome1'},
|
||||
)
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
('mysql', '--batch', '--user', 'root'),
|
||||
processes=[extract_process],
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root', 'MYSQL_PWD': 'trustsome1'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1461,12 +1648,18 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').replace_with(
|
||||
lambda option, data_source, connection_params=None, restore=False: (
|
||||
connection_params or {}
|
||||
).get(option)
|
||||
)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('cliusername', 'clipassword', None).and_return(
|
||||
('--defaults-extra-file=/dev/fd/99',),
|
||||
)
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
(
|
||||
'mysql',
|
||||
@@ -1483,6 +1676,7 @@ def test_restore_data_source_dump_with_connection_params_uses_connection_params_
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1525,12 +1719,18 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').replace_with(
|
||||
lambda option, data_source, connection_params=None, restore=False: data_source.get(
|
||||
f'restore_{option}'
|
||||
)
|
||||
)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args('restoreuser', 'restorepass', None).and_return(
|
||||
('--defaults-extra-file=/dev/fd/99',),
|
||||
)
|
||||
flexmock(module.os).should_receive('environ').and_return({'USER': 'root'})
|
||||
flexmock(module.borgmatic.config.paths).should_receive('get_working_directory').and_return(None)
|
||||
flexmock(module).should_receive('execute_command_with_processes').with_args(
|
||||
(
|
||||
'mysql',
|
||||
@@ -1548,6 +1748,7 @@ def test_restore_data_source_dump_without_connection_params_uses_restore_params_
|
||||
output_log_level=logging.DEBUG,
|
||||
input_file=extract_process.stdout,
|
||||
environment={'USER': 'root'},
|
||||
working_directory=None,
|
||||
).once()
|
||||
|
||||
module.restore_data_source_dump(
|
||||
@@ -1575,6 +1776,7 @@ def test_restore_data_source_dump_with_dry_run_skips_restore():
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'parse_extra_options',
|
||||
).and_return((), None)
|
||||
flexmock(module.database_config).should_receive('resolve_database_option').and_return(None)
|
||||
flexmock(module.borgmatic.hooks.data_source.mariadb).should_receive(
|
||||
'make_defaults_file_options',
|
||||
).with_args(None, None, None).and_return(())
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user