mirror of
https://github.com/borgmatic-collective/borgmatic.git
synced 2026-07-22 10:13:00 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3821636b77 | ||
|
|
596f6f9dac | ||
|
|
7ecdaea83a | ||
|
|
98cb2644db | ||
|
|
31db6faa19 | ||
|
|
872d8b695a | ||
|
|
6db3e1dda5 | ||
|
|
9aaf78b9dd | ||
|
|
5d8ac158ce | ||
|
|
d32a53d58f | ||
|
|
a836ec944f | ||
|
|
e7b128e735 | ||
|
|
ff3cb1d80f | ||
|
|
c5ff08ee25 | ||
|
|
856db29180 | ||
|
|
20e09b4ea8 | ||
|
|
1dd0682661 | ||
|
|
7252b8d614 | ||
|
|
e5870a169b | ||
|
|
94795a3560 | ||
|
|
7705debab0 | ||
|
|
f87df0527f | ||
|
|
e4512a40e0 | ||
|
|
1d4a9510b8 | ||
|
|
2648f07e7a | ||
|
|
459bf1fcf6 | ||
|
|
3930e63320 | ||
|
|
acecb1e397 | ||
|
|
9b48eb5a61 | ||
|
|
7d40a448cb | ||
|
|
da7aed3814 | ||
|
|
c7f4200417 | ||
|
|
5e2a5494af | ||
|
|
7b77fd2510 |
@@ -1,11 +1,12 @@
|
||||
Dan Helfman <witten@torsion.org>: Main developer
|
||||
|
||||
Alexander Görtz: Python 3 compatibility
|
||||
Florian Lindner: Logging rewrite
|
||||
Henning Schroeder: Copy editing
|
||||
Johannes Feichtner: Support for user hooks
|
||||
Michele Lazzeri: Custom archive names
|
||||
Nick Whyte: Support prefix filtering for archive consistency checks
|
||||
newtonne: Read encryption password from external file
|
||||
Robin `ypid` Schneider: Support additional options of Borg
|
||||
Scott Squires: Custom archive names
|
||||
Thomas LÉVEIL: Support for a keep_minutely prune option. Support for the --json option
|
||||
Nick Whyte: Support prefix filtering for archive consistency checks
|
||||
|
||||
@@ -1,3 +1,22 @@
|
||||
1.2.4
|
||||
* Fix for archive checking traceback due to parameter mismatch.
|
||||
|
||||
1.2.3
|
||||
* #64, #90, #92: Rewrite of logging system. Now verbosity flags passed to Borg are derived from
|
||||
borgmatic's log level. Note that the output of borgmatic might slightly change.
|
||||
* Part of #80: Support for Borg create --read-special via "read_special" option in borgmatic's
|
||||
location configuration.
|
||||
* #87: Support for Borg create --checkpoint-interval via "checkpoint_interval" option in
|
||||
borgmatic's storage configuration.
|
||||
* #88: Fix declared pykwalify compatibility version range in setup.py to prevent use of ancient
|
||||
versions of pykwalify with large version numbers.
|
||||
* #89: Pass --show-rc option to Borg when at highest verbosity level.
|
||||
* #94: Support for Borg --json option via borgmatic command-line to --create archives.
|
||||
|
||||
1.2.2
|
||||
* #85: Fix compatibility issue between pykwalify and ruamel.yaml 0.15.52, which manifested in
|
||||
borgmatic as a pykwalify RuleError.
|
||||
|
||||
1.2.1
|
||||
* Skip before/after backup hooks when only doing --prune, --check, --list, and/or --info.
|
||||
* #71: Support for XDG_CONFIG_HOME environment variable for specifying alternate user ~/.config/
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
<img src="https://projects.torsion.org/witten/borgmatic/raw/branch/master/static/borgmatic.png" width="150px" style="float: right; padding-left: 1em;">
|
||||
|
||||
---
|
||||
title: borgmatic
|
||||
permalink: borgmatic/index.html
|
||||
---
|
||||
## Overview
|
||||
|
||||
borgmatic (formerly atticmatic) is a simple Python wrapper script for the
|
||||
[Borg](https://borgbackup.readthedocs.org/en/stable/) backup software that
|
||||
initiates a backup, prunes any old backups according to a retention policy,
|
||||
and validates backups for consistency. The script supports specifying your
|
||||
settings in a declarative configuration file rather than having to put them
|
||||
all on the command-line, and handles common errors.
|
||||
<img src="https://projects.torsion.org/witten/borgmatic/raw/branch/master/static/borgmatic.png" width="150px" style="float: right; padding-left: 1em;">
|
||||
|
||||
borgmatic is a simple Python wrapper script for the
|
||||
[Borg](https://www.borgbackup.org/) backup software that initiates a backup,
|
||||
prunes any old backups according to a retention policy, and validates backups
|
||||
for consistency. The script supports specifying your settings in a declarative
|
||||
configuration file rather than having to put them all on the command-line, and
|
||||
handles common errors.
|
||||
|
||||
Here's an example config file:
|
||||
|
||||
@@ -157,7 +161,7 @@ borgmatic runs `on_error` hooks if an error occurs.
|
||||
An important security note about hooks: borgmatic executes all hook commands
|
||||
with the user permissions of borgmatic itself. So to prevent potential shell
|
||||
injection or privilege escalation, do not forget to set secure permissions
|
||||
(chmod 0700) on borgmatic configuration files and scripts invoked by hooks.
|
||||
(`chmod 0700`) on borgmatic configuration files and scripts invoked by hooks.
|
||||
|
||||
See the sample generated configuration file mentioned above for specifics
|
||||
about hook configuration syntax.
|
||||
@@ -291,6 +295,20 @@ any number of them. This supports use cases like running consistency checks
|
||||
from a different cron job with a different frequency, or running pruning with
|
||||
a different verbosity level.
|
||||
|
||||
Additionally, borgmatic provides convenient flags for Borg's
|
||||
[list](https://borgbackup.readthedocs.io/en/stable/usage/list.html) and
|
||||
[info](https://borgbackup.readthedocs.io/en/stable/usage/info.html)
|
||||
functionality:
|
||||
|
||||
|
||||
```bash
|
||||
borgmatic --list
|
||||
borgmatic --info
|
||||
```
|
||||
|
||||
You can include an optional `--json` flag with `--create`, `--list`, or
|
||||
`--info` to get the output formatted as JSON.
|
||||
|
||||
|
||||
## Autopilot
|
||||
|
||||
@@ -369,7 +387,7 @@ the following deviations from it:
|
||||
|
||||
To get set up to hack on borgmatic, first clone master via HTTPS or SSH:
|
||||
|
||||
```
|
||||
```bash
|
||||
git clone https://projects.torsion.org/witten/borgmatic.git
|
||||
```
|
||||
|
||||
|
||||
+11
-11
@@ -3,7 +3,6 @@ import os
|
||||
import subprocess
|
||||
|
||||
from borgmatic.borg import extract
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
|
||||
|
||||
DEFAULT_CHECKS = ('repository', 'archives')
|
||||
@@ -74,12 +73,10 @@ def _make_check_flags(checks, check_last=None, prefix=None):
|
||||
) + last_flags + prefix_flags
|
||||
|
||||
|
||||
def check_archives(verbosity, repository, storage_config, consistency_config, local_path='borg',
|
||||
remote_path=None):
|
||||
def check_archives(repository, storage_config, consistency_config, local_path='borg', remote_path=None):
|
||||
'''
|
||||
Given a verbosity flag, a local or remote repository path, a storage config dict, a consistency
|
||||
config dict, and a local/remote commands to run, check the contained Borg archives for
|
||||
consistency.
|
||||
Given a local or remote repository path, a storage config dict, a consistency config dict,
|
||||
and a local/remote commands to run, check the contained Borg archives for consistency.
|
||||
|
||||
If there are no consistency checks to run, skip running them.
|
||||
'''
|
||||
@@ -91,10 +88,13 @@ def check_archives(verbosity, repository, storage_config, consistency_config, lo
|
||||
remote_path_flags = ('--remote-path', remote_path) if remote_path else ()
|
||||
lock_wait = storage_config.get('lock_wait', None)
|
||||
lock_wait_flags = ('--lock-wait', str(lock_wait)) if lock_wait else ()
|
||||
verbosity_flags = {
|
||||
VERBOSITY_SOME: ('--info',),
|
||||
VERBOSITY_LOTS: ('--debug',),
|
||||
}.get(verbosity, ())
|
||||
|
||||
verbosity_flags = ()
|
||||
if logger.isEnabledFor(logging.INFO):
|
||||
verbosity_flags = ('--info',)
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
verbosity_flags = ('--debug', '--show-rc')
|
||||
|
||||
prefix = consistency_config.get('prefix')
|
||||
|
||||
full_command = (
|
||||
@@ -109,4 +109,4 @@ def check_archives(verbosity, repository, storage_config, consistency_config, lo
|
||||
subprocess.check_call(full_command, stdout=stdout, stderr=subprocess.STDOUT)
|
||||
|
||||
if 'extract' in checks:
|
||||
extract.extract_last_archive_dry_run(verbosity, repository, lock_wait, local_path, remote_path)
|
||||
extract.extract_last_archive_dry_run(repository, lock_wait, local_path, remote_path)
|
||||
|
||||
@@ -5,8 +5,6 @@ import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -105,8 +103,7 @@ def _make_exclude_flags(location_config, exclude_filename=None):
|
||||
|
||||
|
||||
def create_archive(
|
||||
verbosity, dry_run, repository, location_config, storage_config, local_path='borg', remote_path=None,
|
||||
):
|
||||
dry_run, repository, location_config, storage_config, local_path='borg', remote_path=None, json=False):
|
||||
'''
|
||||
Given vebosity/dry-run flags, a local or remote repository path, a location config dict, and a
|
||||
storage config dict, create a Borg archive.
|
||||
@@ -115,6 +112,7 @@ def create_archive(
|
||||
|
||||
pattern_file = _write_pattern_file(location_config.get('patterns'))
|
||||
exclude_file = _write_pattern_file(_expand_directories(location_config.get('exclude_patterns')))
|
||||
checkpoint_interval = storage_config.get('checkpoint_interval', None)
|
||||
compression = storage_config.get('compression', None)
|
||||
remote_rate_limit = storage_config.get('remote_rate_limit', None)
|
||||
umask = storage_config.get('umask', None)
|
||||
@@ -140,19 +138,22 @@ def create_archive(
|
||||
location_config,
|
||||
exclude_file.name if exclude_file else None,
|
||||
)
|
||||
+ (('--checkpoint-interval', str(checkpoint_interval)) if checkpoint_interval else ())
|
||||
+ (('--compression', compression) if compression else ())
|
||||
+ (('--remote-ratelimit', str(remote_rate_limit)) if remote_rate_limit else ())
|
||||
+ (('--one-file-system',) if location_config.get('one_file_system') else ())
|
||||
+ (('--read-special',) if location_config.get('read_special') else ())
|
||||
+ (('--nobsdflags',) if location_config.get('bsd_flags') is False else ())
|
||||
+ (('--files-cache', files_cache) if files_cache else ())
|
||||
+ (('--remote-path', remote_path) if remote_path else ())
|
||||
+ (('--umask', str(umask)) if umask else ())
|
||||
+ (('--lock-wait', str(lock_wait)) if lock_wait else ())
|
||||
+ {
|
||||
VERBOSITY_SOME: ('--info',) if dry_run else ('--info', '--stats',),
|
||||
VERBOSITY_LOTS: ('--debug', '--list',) if dry_run else ('--debug', '--list', '--stats',),
|
||||
}.get(verbosity, ())
|
||||
+ (('--list', '--filter', 'AME',) if logger.isEnabledFor(logging.INFO) else ())
|
||||
+ (( '--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
||||
+ (('--stats',) if not dry_run and logger.isEnabledFor(logging.INFO) else ())
|
||||
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
||||
+ (('--dry-run',) if dry_run else ())
|
||||
+ (('--json',) if json else ())
|
||||
)
|
||||
|
||||
logger.debug(' '.join(full_command))
|
||||
|
||||
@@ -2,23 +2,23 @@ import logging
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def extract_last_archive_dry_run(verbosity, repository, lock_wait=None, local_path='borg', remote_path=None):
|
||||
def extract_last_archive_dry_run(repository, lock_wait=None, local_path='borg', remote_path=None):
|
||||
'''
|
||||
Perform an extraction dry-run of just the most recent archive. If there are no archives, skip
|
||||
the dry-run.
|
||||
'''
|
||||
remote_path_flags = ('--remote-path', remote_path) if remote_path else ()
|
||||
lock_wait_flags = ('--lock-wait', str(lock_wait)) if lock_wait else ()
|
||||
verbosity_flags = {
|
||||
VERBOSITY_SOME: ('--info',),
|
||||
VERBOSITY_LOTS: ('--debug',),
|
||||
}.get(verbosity, ())
|
||||
verbosity_flags = ()
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
verbosity_flags = ('--debug', '--show-rc')
|
||||
elif logger.isEnabledFor(logging.INFO):
|
||||
verbosity_flags = ('--info',)
|
||||
|
||||
|
||||
full_list_command = (
|
||||
local_path, 'list',
|
||||
@@ -32,7 +32,7 @@ def extract_last_archive_dry_run(verbosity, repository, lock_wait=None, local_pa
|
||||
if not last_archive_name:
|
||||
return
|
||||
|
||||
list_flag = ('--list',) if verbosity == VERBOSITY_LOTS else ()
|
||||
list_flag = ('--list',) if logger.isEnabledFor(logging.DEBUG) else ()
|
||||
full_extract_command = (
|
||||
local_path, 'extract',
|
||||
'--dry-run',
|
||||
|
||||
+4
-10
@@ -1,17 +1,13 @@
|
||||
import logging
|
||||
import subprocess
|
||||
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def display_archives_info(
|
||||
verbosity, repository, storage_config, local_path='borg', remote_path=None, json=False
|
||||
):
|
||||
def display_archives_info(repository, storage_config, local_path='borg', remote_path=None, json=False):
|
||||
'''
|
||||
Given a verbosity flag, a local or remote repository path, and a storage config dict,
|
||||
Given a local or remote repository path, and a storage config dict,
|
||||
display summary information for Borg archives in the repository.
|
||||
'''
|
||||
lock_wait = storage_config.get('lock_wait', None)
|
||||
@@ -20,11 +16,9 @@ def display_archives_info(
|
||||
(local_path, 'info', repository)
|
||||
+ (('--remote-path', remote_path) if remote_path else ())
|
||||
+ (('--lock-wait', str(lock_wait)) if lock_wait else ())
|
||||
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
||||
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
||||
+ (('--json',) if json else ())
|
||||
+ {
|
||||
VERBOSITY_SOME: ('--info',),
|
||||
VERBOSITY_LOTS: ('--debug',),
|
||||
}.get(verbosity, ())
|
||||
)
|
||||
|
||||
logger.debug(' '.join(full_command))
|
||||
|
||||
+4
-11
@@ -1,17 +1,13 @@
|
||||
import logging
|
||||
import subprocess
|
||||
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def list_archives(
|
||||
verbosity, repository, storage_config, local_path='borg', remote_path=None, json=False
|
||||
):
|
||||
def list_archives(repository, storage_config, local_path='borg', remote_path=None, json=False):
|
||||
'''
|
||||
Given a verbosity flag, a local or remote repository path, and a storage config dict,
|
||||
Given a local or remote repository path, and a storage config dict,
|
||||
list Borg archives in the repository.
|
||||
'''
|
||||
lock_wait = storage_config.get('lock_wait', None)
|
||||
@@ -20,13 +16,10 @@ def list_archives(
|
||||
(local_path, 'list', repository)
|
||||
+ (('--remote-path', remote_path) if remote_path else ())
|
||||
+ (('--lock-wait', str(lock_wait)) if lock_wait else ())
|
||||
+ (('--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
||||
+ (('--debug', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
||||
+ (('--json',) if json else ())
|
||||
+ {
|
||||
VERBOSITY_SOME: ('--info',),
|
||||
VERBOSITY_LOTS: ('--debug',),
|
||||
}.get(verbosity, ())
|
||||
)
|
||||
|
||||
logger.debug(' '.join(full_command))
|
||||
|
||||
output = subprocess.check_output(full_command)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import logging
|
||||
import subprocess
|
||||
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -32,10 +31,9 @@ def _make_prune_flags(retention_config):
|
||||
)
|
||||
|
||||
|
||||
def prune_archives(verbosity, dry_run, repository, storage_config, retention_config,
|
||||
local_path='borg', remote_path=None):
|
||||
def prune_archives(dry_run, repository, storage_config, retention_config, local_path='borg', remote_path=None):
|
||||
'''
|
||||
Given verbosity/dry-run flags, a local or remote repository path, a storage config dict, and a
|
||||
Given dry-run flag, a local or remote repository path, a storage config dict, and a
|
||||
retention config dict, prune Borg archives according to the retention policy specified in that
|
||||
configuration.
|
||||
'''
|
||||
@@ -54,10 +52,9 @@ def prune_archives(verbosity, dry_run, repository, storage_config, retention_con
|
||||
+ (('--remote-path', remote_path) if remote_path else ())
|
||||
+ (('--umask', str(umask)) if umask else ())
|
||||
+ (('--lock-wait', str(lock_wait)) if lock_wait else ())
|
||||
+ {
|
||||
VERBOSITY_SOME: ('--info', '--stats',),
|
||||
VERBOSITY_LOTS: ('--debug', '--stats', '--list'),
|
||||
}.get(verbosity, ())
|
||||
+ (('--stats',) if not dry_run and logger.isEnabledFor(logging.INFO) else ())
|
||||
+ (( '--info',) if logger.getEffectiveLevel() == logging.INFO else ())
|
||||
+ (('--debug', '--list', '--show-rc') if logger.isEnabledFor(logging.DEBUG) else ())
|
||||
+ (('--dry-run',) if dry_run else ())
|
||||
)
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ from borgmatic.borg import check as borg_check, create as borg_create, prune as
|
||||
from borgmatic.commands import hook
|
||||
from borgmatic.config import collect, convert, validate
|
||||
from borgmatic.signals import configure_signals
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS, verbosity_to_log_level
|
||||
from borgmatic.verbosity import verbosity_to_log_level
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -81,7 +81,7 @@ def parse_arguments(*arguments):
|
||||
dest='json',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='Output results from the --list option as json',
|
||||
help='Output results from the --create, --list, or --info options as json',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-n', '--dry-run',
|
||||
@@ -92,13 +92,15 @@ def parse_arguments(*arguments):
|
||||
parser.add_argument(
|
||||
'-v', '--verbosity',
|
||||
type=int,
|
||||
choices=range(0,3),
|
||||
default=0,
|
||||
help='Display verbose progress (1 for some, 2 for lots)',
|
||||
)
|
||||
|
||||
args = parser.parse_args(arguments)
|
||||
|
||||
if args.json and not (args.list or args.info):
|
||||
raise ValueError('The --json option can only be used with the --list or --info options')
|
||||
if args.json and not (args.create or args.list or args.info):
|
||||
raise ValueError('The --json option can only be used with the --create, --list, or --info options')
|
||||
|
||||
if args.json and args.list and args.info:
|
||||
raise ValueError(
|
||||
@@ -165,7 +167,6 @@ def _run_commands_on_repository(
|
||||
if args.prune:
|
||||
logger.info('{}: Pruning archives{}'.format(repository, dry_run_label))
|
||||
borg_prune.prune_archives(
|
||||
args.verbosity,
|
||||
args.dry_run,
|
||||
repository,
|
||||
storage,
|
||||
@@ -176,7 +177,6 @@ def _run_commands_on_repository(
|
||||
if args.create:
|
||||
logger.info('{}: Creating archive{}'.format(repository, dry_run_label))
|
||||
borg_create.create_archive(
|
||||
args.verbosity,
|
||||
args.dry_run,
|
||||
repository,
|
||||
location,
|
||||
@@ -187,17 +187,15 @@ def _run_commands_on_repository(
|
||||
if args.check:
|
||||
logger.info('{}: Running consistency checks'.format(repository))
|
||||
borg_check.check_archives(
|
||||
args.verbosity,
|
||||
repository,
|
||||
storage,
|
||||
consistency,
|
||||
local_path=local_path,
|
||||
remote_path=remote_path,
|
||||
remote_path=remote_path
|
||||
)
|
||||
if args.list:
|
||||
logger.info('{}: Listing archives'.format(repository))
|
||||
output = borg_list.list_archives(
|
||||
args.verbosity,
|
||||
repository,
|
||||
storage,
|
||||
local_path=local_path,
|
||||
@@ -211,7 +209,6 @@ def _run_commands_on_repository(
|
||||
if args.info:
|
||||
logger.info('{}: Displaying summary info for archives'.format(repository))
|
||||
output = borg_info.display_archives_info(
|
||||
args.verbosity,
|
||||
repository,
|
||||
storage,
|
||||
local_path=local_path,
|
||||
|
||||
@@ -22,6 +22,13 @@ map:
|
||||
type: bool
|
||||
desc: Stay in same file system (do not cross mount points).
|
||||
example: true
|
||||
read_special:
|
||||
type: bool
|
||||
desc: |
|
||||
Use Borg's --read-special flag to allow backup of block and other special
|
||||
devices. Use with caution, as it will lead to problems if used when
|
||||
backing up special devices such as /dev/zero.
|
||||
example: false
|
||||
bsd_flags:
|
||||
type: bool
|
||||
desc: Record bsdflags (e.g. NODUMP, IMMUTABLE) in archive. Defaults to true.
|
||||
@@ -124,6 +131,13 @@ map:
|
||||
punctuation, so it parses correctly. And backslash any quote or backslash
|
||||
literals as well.
|
||||
example: "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
|
||||
checkpoint_interval:
|
||||
type: int
|
||||
desc: |
|
||||
Number of seconds between each checkpoint during a long-running backup. See
|
||||
https://borgbackup.readthedocs.io/en/stable/faq.html#if-a-backup-stops-mid-way-does-the-already-backed-up-data-stay-there
|
||||
for details. Defaults to checkpoints every 1800 seconds (30 minutes).
|
||||
example: 1800
|
||||
compression:
|
||||
type: scalar
|
||||
desc: |
|
||||
|
||||
@@ -74,8 +74,8 @@ def parse_configuration(config_filename, schema_filename):
|
||||
logging.getLogger('pykwalify').setLevel(logging.ERROR)
|
||||
|
||||
try:
|
||||
config = yaml.round_trip_load(open(config_filename))
|
||||
schema = yaml.round_trip_load(open(schema_filename))
|
||||
config = yaml.safe_load(open(config_filename))
|
||||
schema = yaml.safe_load(open(schema_filename))
|
||||
except yaml.error.YAMLError as error:
|
||||
raise Validation_error(config_filename, (str(error),))
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ def test_parse_arguments_with_no_arguments_uses_defaults():
|
||||
|
||||
assert parser.config_paths == config_paths
|
||||
assert parser.excludes_filename == None
|
||||
assert parser.verbosity is None
|
||||
assert parser.verbosity is 0
|
||||
assert parser.json is False
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ def test_parse_arguments_with_path_arguments_overrides_defaults():
|
||||
|
||||
assert parser.config_paths == ['myconfig']
|
||||
assert parser.excludes_filename == 'myexcludes'
|
||||
assert parser.verbosity is None
|
||||
assert parser.verbosity is 0
|
||||
|
||||
|
||||
def test_parse_arguments_with_multiple_config_paths_parses_as_list():
|
||||
@@ -34,7 +34,7 @@ def test_parse_arguments_with_multiple_config_paths_parses_as_list():
|
||||
parser = module.parse_arguments('--config', 'myconfig', 'otherconfig')
|
||||
|
||||
assert parser.config_paths == ['myconfig', 'otherconfig']
|
||||
assert parser.verbosity is None
|
||||
assert parser.verbosity is 0
|
||||
|
||||
|
||||
def test_parse_arguments_with_verbosity_flag_overrides_default():
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
from subprocess import STDOUT
|
||||
import sys
|
||||
import logging, sys
|
||||
|
||||
from flexmock import flexmock
|
||||
import pytest
|
||||
|
||||
from borgmatic.borg import check as module
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
|
||||
from borgmatic.tests.unit.test_verbosity import insert_logging_mock
|
||||
|
||||
def insert_subprocess_mock(check_call_command, **kwargs):
|
||||
subprocess = flexmock(module.subprocess)
|
||||
@@ -125,7 +124,6 @@ def test_check_archives_calls_borg_with_parameters(checks):
|
||||
flexmock(module.os).should_receive('devnull')
|
||||
|
||||
module.check_archives(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
consistency_config=consistency_config,
|
||||
@@ -142,43 +140,42 @@ def test_check_archives_with_extract_check_calls_extract_only():
|
||||
insert_subprocess_never()
|
||||
|
||||
module.check_archives(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
consistency_config=consistency_config,
|
||||
)
|
||||
|
||||
|
||||
def test_check_archives_with_verbosity_some_calls_borg_with_info_parameter():
|
||||
def test_check_archives_with_log_info_calls_borg_with_info_parameter():
|
||||
checks = ('repository',)
|
||||
consistency_config = {'check_last': None}
|
||||
flexmock(module).should_receive('_parse_checks').and_return(checks)
|
||||
flexmock(module).should_receive('_make_check_flags').and_return(())
|
||||
insert_logging_mock(logging.INFO)
|
||||
insert_subprocess_mock(
|
||||
('borg', 'check', 'repo', '--info'),
|
||||
stdout=None, stderr=STDOUT,
|
||||
)
|
||||
|
||||
module.check_archives(
|
||||
verbosity=VERBOSITY_SOME,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
consistency_config=consistency_config,
|
||||
)
|
||||
|
||||
|
||||
def test_check_archives_with_verbosity_lots_calls_borg_with_debug_parameter():
|
||||
def test_check_archives_with_log_debug_calls_borg_with_debug_parameter():
|
||||
checks = ('repository',)
|
||||
consistency_config = {'check_last': None}
|
||||
flexmock(module).should_receive('_parse_checks').and_return(checks)
|
||||
flexmock(module).should_receive('_make_check_flags').and_return(())
|
||||
insert_logging_mock(logging.DEBUG)
|
||||
insert_subprocess_mock(
|
||||
('borg', 'check', 'repo', '--debug'),
|
||||
('borg', 'check', 'repo', '--debug', '--show-rc'),
|
||||
stdout=None, stderr=STDOUT,
|
||||
)
|
||||
|
||||
module.check_archives(
|
||||
verbosity=VERBOSITY_LOTS,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
consistency_config=consistency_config,
|
||||
@@ -191,7 +188,6 @@ def test_check_archives_without_any_checks_bails():
|
||||
insert_subprocess_never()
|
||||
|
||||
module.check_archives(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
consistency_config=consistency_config,
|
||||
@@ -213,7 +209,6 @@ def test_check_archives_with_local_path_calls_borg_via_local_path():
|
||||
flexmock(module.os).should_receive('devnull')
|
||||
|
||||
module.check_archives(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
consistency_config=consistency_config,
|
||||
@@ -236,7 +231,6 @@ def test_check_archives_with_remote_path_calls_borg_with_remote_path_parameters(
|
||||
flexmock(module.os).should_receive('devnull')
|
||||
|
||||
module.check_archives(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
consistency_config=consistency_config,
|
||||
@@ -259,7 +253,6 @@ def test_check_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
|
||||
flexmock(module.os).should_receive('devnull')
|
||||
|
||||
module.check_archives(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config={'lock_wait': 5},
|
||||
consistency_config=consistency_config,
|
||||
@@ -283,7 +276,6 @@ def test_check_archives_with_retention_prefix():
|
||||
flexmock(module.os).should_receive('devnull')
|
||||
|
||||
module.check_archives(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
consistency_config=consistency_config,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import os
|
||||
import logging, os
|
||||
|
||||
from flexmock import flexmock
|
||||
|
||||
from borgmatic.borg import create as module
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
from borgmatic.tests.unit.test_verbosity import insert_logging_mock
|
||||
|
||||
|
||||
def test_initialize_environment_with_passcommand_should_set_environment():
|
||||
@@ -216,7 +216,6 @@ def test_create_archive_calls_borg_with_parameters():
|
||||
insert_subprocess_mock(CREATE_COMMAND)
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -237,7 +236,6 @@ def test_create_archive_with_patterns_calls_borg_with_patterns():
|
||||
insert_subprocess_mock(CREATE_COMMAND + pattern_flags)
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -258,7 +256,6 @@ def test_create_archive_with_exclude_patterns_calls_borg_with_excludes():
|
||||
insert_subprocess_mock(CREATE_COMMAND + exclude_flags)
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -270,16 +267,16 @@ def test_create_archive_with_exclude_patterns_calls_borg_with_excludes():
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_verbosity_some_calls_borg_with_info_parameter():
|
||||
def test_create_archive_with_log_info_calls_borg_with_info_parameter():
|
||||
flexmock(module).should_receive('_expand_directories').and_return(('foo', 'bar')).and_return(())
|
||||
flexmock(module).should_receive('_write_pattern_file').and_return(None)
|
||||
flexmock(module).should_receive('_make_pattern_flags').and_return(())
|
||||
flexmock(module).should_receive('_make_pattern_flags').and_return(())
|
||||
flexmock(module).should_receive('_make_exclude_flags').and_return(())
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--info', '--stats',))
|
||||
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--list', '--filter', 'AME', '--info', '--stats',))
|
||||
insert_logging_mock(logging.INFO)
|
||||
|
||||
module.create_archive(
|
||||
verbosity=VERBOSITY_SOME,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -291,15 +288,15 @@ def test_create_archive_with_verbosity_some_calls_borg_with_info_parameter():
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_verbosity_lots_calls_borg_with_debug_parameter():
|
||||
def test_create_archive_with_log_debug_calls_borg_with_debug_parameter():
|
||||
flexmock(module).should_receive('_expand_directories').and_return(('foo', 'bar')).and_return(())
|
||||
flexmock(module).should_receive('_write_pattern_file').and_return(None)
|
||||
flexmock(module).should_receive('_make_pattern_flags').and_return(())
|
||||
flexmock(module).should_receive('_make_exclude_flags').and_return(())
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--debug', '--list', '--stats'))
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--list', '--filter', 'AME','--stats', '--debug', '--show-rc'))
|
||||
insert_logging_mock(logging.DEBUG)
|
||||
|
||||
module.create_archive(
|
||||
verbosity=VERBOSITY_LOTS,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -320,7 +317,6 @@ def test_create_archive_with_dry_run_calls_borg_with_dry_run_parameter():
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--dry-run',))
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=True,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -332,16 +328,18 @@ def test_create_archive_with_dry_run_calls_borg_with_dry_run_parameter():
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_dry_run_and_verbosity_some_calls_borg_without_stats_parameter():
|
||||
def test_create_archive_with_dry_run_and_log_info_calls_borg_without_stats_parameter():
|
||||
""" --dry-run and --stats are mutually exclusive, see:
|
||||
https://borgbackup.readthedocs.io/en/stable/usage/create.html#description """
|
||||
flexmock(module).should_receive('_expand_directories').and_return(('foo', 'bar')).and_return(())
|
||||
flexmock(module).should_receive('_write_pattern_file').and_return(None)
|
||||
flexmock(module).should_receive('_make_pattern_flags').and_return(())
|
||||
flexmock(module).should_receive('_make_pattern_flags').and_return(())
|
||||
flexmock(module).should_receive('_make_exclude_flags').and_return(())
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--info', '--dry-run'))
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--list', '--filter', 'AME', '--info', '--dry-run'))
|
||||
insert_logging_mock(logging.INFO)
|
||||
|
||||
module.create_archive(
|
||||
verbosity=VERBOSITY_SOME,
|
||||
dry_run=True,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -353,16 +351,18 @@ def test_create_archive_with_dry_run_and_verbosity_some_calls_borg_without_stats
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_dry_run_and_verbosity_lots_calls_borg_without_stats_parameter():
|
||||
def test_create_archive_with_dry_run_and_log_debug_calls_borg_without_stats_parameter():
|
||||
""" --dry-run and --stats are mutually exclusive, see:
|
||||
https://borgbackup.readthedocs.io/en/stable/usage/create.html#description """
|
||||
flexmock(module).should_receive('_expand_directories').and_return(('foo', 'bar')).and_return(())
|
||||
flexmock(module).should_receive('_write_pattern_file').and_return(None)
|
||||
flexmock(module).should_receive('_make_pattern_flags').and_return(())
|
||||
flexmock(module).should_receive('_make_pattern_flags').and_return(())
|
||||
flexmock(module).should_receive('_make_exclude_flags').and_return(())
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--debug', '--list', '--dry-run'))
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--list', '--filter', 'AME', '--debug', '--show-rc', '--dry-run'))
|
||||
insert_logging_mock(logging.DEBUG)
|
||||
|
||||
module.create_archive(
|
||||
verbosity=VERBOSITY_LOTS,
|
||||
dry_run=True,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -374,6 +374,25 @@ def test_create_archive_with_dry_run_and_verbosity_lots_calls_borg_without_stats
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_checkpoint_interval_calls_borg_with_checkpoint_interval_parameters():
|
||||
flexmock(module).should_receive('_expand_directories').and_return(('foo', 'bar')).and_return(())
|
||||
flexmock(module).should_receive('_write_pattern_file').and_return(None)
|
||||
flexmock(module).should_receive('_make_pattern_flags').and_return(())
|
||||
flexmock(module).should_receive('_make_exclude_flags').and_return(())
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--checkpoint-interval', '600'))
|
||||
|
||||
module.create_archive(
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
'source_directories': ['foo', 'bar'],
|
||||
'repositories': ['repo'],
|
||||
'exclude_patterns': None,
|
||||
},
|
||||
storage_config={'checkpoint_interval': 600},
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_compression_calls_borg_with_compression_parameters():
|
||||
flexmock(module).should_receive('_expand_directories').and_return(('foo', 'bar')).and_return(())
|
||||
flexmock(module).should_receive('_write_pattern_file').and_return(None)
|
||||
@@ -382,7 +401,6 @@ def test_create_archive_with_compression_calls_borg_with_compression_parameters(
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--compression', 'rle'))
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -402,7 +420,6 @@ def test_create_archive_with_remote_rate_limit_calls_borg_with_remote_ratelimit_
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--remote-ratelimit', '100'))
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -422,7 +439,6 @@ def test_create_archive_with_one_file_system_calls_borg_with_one_file_system_par
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--one-file-system',))
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -435,6 +451,26 @@ def test_create_archive_with_one_file_system_calls_borg_with_one_file_system_par
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_read_special_calls_borg_with_read_special_parameter():
|
||||
flexmock(module).should_receive('_expand_directories').and_return(('foo', 'bar')).and_return(())
|
||||
flexmock(module).should_receive('_write_pattern_file').and_return(None)
|
||||
flexmock(module).should_receive('_make_pattern_flags').and_return(())
|
||||
flexmock(module).should_receive('_make_exclude_flags').and_return(())
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--read-special',))
|
||||
|
||||
module.create_archive(
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
'source_directories': ['foo', 'bar'],
|
||||
'repositories': ['repo'],
|
||||
'read_special': True,
|
||||
'exclude_patterns': None,
|
||||
},
|
||||
storage_config={},
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_bsd_flags_true_calls_borg_without_nobsdflags_parameter():
|
||||
flexmock(module).should_receive('_expand_directories').and_return(('foo', 'bar')).and_return(())
|
||||
flexmock(module).should_receive('_write_pattern_file').and_return(None)
|
||||
@@ -443,7 +479,6 @@ def test_create_archive_with_bsd_flags_true_calls_borg_without_nobsdflags_parame
|
||||
insert_subprocess_mock(CREATE_COMMAND)
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -464,7 +499,6 @@ def test_create_archive_with_bsd_flags_false_calls_borg_with_nobsdflags_paramete
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--nobsdflags',))
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -485,7 +519,6 @@ def test_create_archive_with_files_cache_calls_borg_with_files_cache_parameters(
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--files-cache', 'ctime,size'))
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -506,7 +539,6 @@ def test_create_archive_with_local_path_calls_borg_via_local_path():
|
||||
insert_subprocess_mock(('borg1',) + CREATE_COMMAND[1:])
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -527,7 +559,6 @@ def test_create_archive_with_remote_path_calls_borg_with_remote_path_parameters(
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--remote-path', 'borg1'))
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -548,7 +579,6 @@ def test_create_archive_with_umask_calls_borg_with_umask_parameters():
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--umask', '740'))
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -568,7 +598,6 @@ def test_create_archive_with_lock_wait_calls_borg_with_lock_wait_parameters():
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--lock-wait', '5'))
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -580,6 +609,26 @@ def test_create_archive_with_lock_wait_calls_borg_with_lock_wait_parameters():
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_json_calls_borg_with_json_parameter():
|
||||
flexmock(module).should_receive('_expand_directories').and_return(('foo', 'bar')).and_return(())
|
||||
flexmock(module).should_receive('_write_pattern_file').and_return(None)
|
||||
flexmock(module).should_receive('_make_pattern_flags').and_return(())
|
||||
flexmock(module).should_receive('_make_exclude_flags').and_return(())
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--json',))
|
||||
|
||||
module.create_archive(
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
'source_directories': ['foo', 'bar'],
|
||||
'repositories': ['repo'],
|
||||
'exclude_patterns': None,
|
||||
},
|
||||
storage_config={},
|
||||
json=True
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_source_directories_glob_expands():
|
||||
flexmock(module).should_receive('_expand_directories').and_return(('foo', 'food')).and_return(())
|
||||
flexmock(module).should_receive('_write_pattern_file').and_return(None)
|
||||
@@ -589,7 +638,6 @@ def test_create_archive_with_source_directories_glob_expands():
|
||||
flexmock(module.glob).should_receive('glob').with_args('foo*').and_return(['foo', 'food'])
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -610,7 +658,6 @@ def test_create_archive_with_non_matching_source_directories_glob_passes_through
|
||||
flexmock(module.glob).should_receive('glob').with_args('foo*').and_return([])
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -630,7 +677,6 @@ def test_create_archive_with_glob_calls_borg_with_expanded_directories():
|
||||
insert_subprocess_mock(('borg', 'create', 'repo::{}'.format(DEFAULT_ARCHIVE_NAME), 'foo', 'food'))
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -650,7 +696,6 @@ def test_create_archive_with_archive_name_format_calls_borg_with_archive_name():
|
||||
insert_subprocess_mock(('borg', 'create', 'repo::ARCHIVE_NAME', 'foo', 'bar'))
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
@@ -672,7 +717,6 @@ def test_create_archive_with_archive_name_format_accepts_borg_placeholders():
|
||||
insert_subprocess_mock(('borg', 'create', 'repo::Documents_{hostname}-{now}', 'foo', 'bar'))
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
location_config={
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import sys
|
||||
import logging, sys
|
||||
|
||||
from flexmock import flexmock
|
||||
|
||||
from borgmatic.borg import extract as module
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
from borgmatic.tests.unit.test_verbosity import insert_logging_mock
|
||||
|
||||
|
||||
def insert_subprocess_mock(check_call_command, **kwargs):
|
||||
@@ -32,7 +33,6 @@ def test_extract_last_archive_dry_run_should_call_borg_with_last_archive():
|
||||
)
|
||||
|
||||
module.extract_last_archive_dry_run(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
lock_wait=None,
|
||||
)
|
||||
@@ -47,13 +47,12 @@ def test_extract_last_archive_dry_run_without_any_archives_should_bail():
|
||||
insert_subprocess_never()
|
||||
|
||||
module.extract_last_archive_dry_run(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
lock_wait=None,
|
||||
)
|
||||
|
||||
|
||||
def test_extract_last_archive_dry_run_with_verbosity_some_should_call_borg_with_info_parameter():
|
||||
def test_extract_last_archive_dry_run_with_log_info_should_call_borg_with_info_parameter():
|
||||
flexmock(sys.stdout).encoding = 'utf-8'
|
||||
insert_subprocess_check_output_mock(
|
||||
('borg', 'list', '--short', 'repo', '--info'),
|
||||
@@ -62,26 +61,26 @@ def test_extract_last_archive_dry_run_with_verbosity_some_should_call_borg_with_
|
||||
insert_subprocess_mock(
|
||||
('borg', 'extract', '--dry-run', 'repo::archive2', '--info'),
|
||||
)
|
||||
insert_logging_mock(logging.INFO)
|
||||
|
||||
module.extract_last_archive_dry_run(
|
||||
verbosity=VERBOSITY_SOME,
|
||||
repository='repo',
|
||||
lock_wait=None,
|
||||
)
|
||||
|
||||
|
||||
def test_extract_last_archive_dry_run_with_verbosity_lots_should_call_borg_with_debug_parameter():
|
||||
def test_extract_last_archive_dry_run_with_log_debug_should_call_borg_with_debug_parameter():
|
||||
flexmock(sys.stdout).encoding = 'utf-8'
|
||||
insert_subprocess_check_output_mock(
|
||||
('borg', 'list', '--short', 'repo', '--debug'),
|
||||
('borg', 'list', '--short', 'repo', '--debug', '--show-rc'),
|
||||
result='archive1\narchive2\n'.encode('utf-8'),
|
||||
)
|
||||
insert_subprocess_mock(
|
||||
('borg', 'extract', '--dry-run', 'repo::archive2', '--debug', '--list'),
|
||||
('borg', 'extract', '--dry-run', 'repo::archive2', '--debug', '--show-rc', '--list'),
|
||||
)
|
||||
insert_logging_mock(logging.DEBUG)
|
||||
|
||||
module.extract_last_archive_dry_run(
|
||||
verbosity=VERBOSITY_LOTS,
|
||||
repository='repo',
|
||||
lock_wait=None,
|
||||
)
|
||||
@@ -98,7 +97,6 @@ def test_extract_last_archive_dry_run_should_call_borg_via_local_path():
|
||||
)
|
||||
|
||||
module.extract_last_archive_dry_run(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
lock_wait=None,
|
||||
local_path='borg1',
|
||||
@@ -116,7 +114,6 @@ def test_extract_last_archive_dry_run_should_call_borg_with_remote_path_paramete
|
||||
)
|
||||
|
||||
module.extract_last_archive_dry_run(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
lock_wait=None,
|
||||
remote_path='borg1',
|
||||
@@ -134,7 +131,6 @@ def test_extract_last_archive_dry_run_should_call_borg_with_lock_wait_parameters
|
||||
)
|
||||
|
||||
module.extract_last_archive_dry_run(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
lock_wait=5,
|
||||
)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
from flexmock import flexmock
|
||||
|
||||
from borgmatic.borg import info as module
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
from borgmatic.tests.unit.test_verbosity import insert_logging_mock
|
||||
|
||||
|
||||
def insert_subprocess_mock(check_call_command, **kwargs):
|
||||
@@ -18,29 +19,27 @@ def test_display_archives_info_calls_borg_with_parameters():
|
||||
insert_subprocess_mock(INFO_COMMAND)
|
||||
|
||||
module.display_archives_info(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
)
|
||||
|
||||
|
||||
def test_display_archives_info_with_verbosity_some_calls_borg_with_info_parameter():
|
||||
def test_display_archives_info_with_log_info_calls_borg_with_info_parameter():
|
||||
insert_subprocess_mock(INFO_COMMAND + ('--info',))
|
||||
|
||||
insert_logging_mock(logging.INFO)
|
||||
module.display_archives_info(
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
verbosity=VERBOSITY_SOME,
|
||||
)
|
||||
|
||||
|
||||
def test_display_archives_info_with_verbosity_lots_calls_borg_with_debug_parameter():
|
||||
insert_subprocess_mock(INFO_COMMAND + ('--debug',))
|
||||
def test_display_archives_info_with_log_debug_calls_borg_with_debug_parameter():
|
||||
insert_subprocess_mock(INFO_COMMAND + ('--debug', '--show-rc'))
|
||||
insert_logging_mock(logging.DEBUG)
|
||||
|
||||
module.display_archives_info(
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
verbosity=VERBOSITY_LOTS,
|
||||
)
|
||||
|
||||
|
||||
@@ -48,7 +47,6 @@ def test_display_archives_info_with_json_calls_borg_with_json_parameter():
|
||||
insert_subprocess_mock(INFO_COMMAND + ('--json',))
|
||||
|
||||
module.display_archives_info(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
json=True,
|
||||
@@ -59,7 +57,6 @@ def test_display_archives_info_with_local_path_calls_borg_via_local_path():
|
||||
insert_subprocess_mock(('borg1',) + INFO_COMMAND[1:])
|
||||
|
||||
module.display_archives_info(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
local_path='borg1',
|
||||
@@ -70,7 +67,6 @@ def test_display_archives_info_with_remote_path_calls_borg_with_remote_path_para
|
||||
insert_subprocess_mock(INFO_COMMAND + ('--remote-path', 'borg1'))
|
||||
|
||||
module.display_archives_info(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
remote_path='borg1',
|
||||
@@ -82,7 +78,6 @@ def test_display_archives_info_with_lock_wait_calls_borg_with_lock_wait_paramete
|
||||
insert_subprocess_mock(INFO_COMMAND + ('--lock-wait', '5'))
|
||||
|
||||
module.display_archives_info(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config=storage_config,
|
||||
)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
from flexmock import flexmock
|
||||
|
||||
from borgmatic.borg import list as module
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
from borgmatic.tests.unit.test_verbosity import insert_logging_mock
|
||||
|
||||
|
||||
def insert_subprocess_mock(check_call_command, **kwargs):
|
||||
@@ -18,29 +19,28 @@ def test_list_archives_calls_borg_with_parameters():
|
||||
insert_subprocess_mock(LIST_COMMAND)
|
||||
|
||||
module.list_archives(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
)
|
||||
|
||||
|
||||
def test_list_archives_with_verbosity_some_calls_borg_with_info_parameter():
|
||||
def test_list_archives_with_log_info_calls_borg_with_info_parameter():
|
||||
insert_subprocess_mock(LIST_COMMAND + ('--info',))
|
||||
insert_logging_mock(logging.INFO)
|
||||
|
||||
module.list_archives(
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
verbosity=VERBOSITY_SOME,
|
||||
)
|
||||
|
||||
|
||||
def test_list_archives_with_verbosity_lots_calls_borg_with_debug_parameter():
|
||||
insert_subprocess_mock(LIST_COMMAND + ('--debug',))
|
||||
def test_list_archives_with_log_debug_calls_borg_with_debug_parameter():
|
||||
insert_subprocess_mock(LIST_COMMAND + ('--debug', '--show-rc'))
|
||||
insert_logging_mock(logging.DEBUG)
|
||||
|
||||
module.list_archives(
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
verbosity=VERBOSITY_LOTS,
|
||||
)
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@ def test_list_archives_with_json_calls_borg_with_json_parameter():
|
||||
insert_subprocess_mock(LIST_COMMAND + ('--json',))
|
||||
|
||||
module.list_archives(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
json=True,
|
||||
@@ -59,7 +58,6 @@ def test_list_archives_with_local_path_calls_borg_via_local_path():
|
||||
insert_subprocess_mock(('borg1',) + LIST_COMMAND[1:])
|
||||
|
||||
module.list_archives(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
local_path='borg1',
|
||||
@@ -70,7 +68,6 @@ def test_list_archives_with_remote_path_calls_borg_with_remote_path_parameters()
|
||||
insert_subprocess_mock(LIST_COMMAND + ('--remote-path', 'borg1'))
|
||||
|
||||
module.list_archives(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
remote_path='borg1',
|
||||
@@ -82,7 +79,6 @@ def test_list_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
|
||||
insert_subprocess_mock(LIST_COMMAND + ('--lock-wait', '5'))
|
||||
|
||||
module.list_archives(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
storage_config=storage_config,
|
||||
)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import logging
|
||||
from collections import OrderedDict
|
||||
|
||||
from flexmock import flexmock
|
||||
|
||||
from borgmatic.borg import prune as module
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
from borgmatic.tests.unit.test_verbosity import insert_logging_mock
|
||||
|
||||
|
||||
def insert_subprocess_mock(check_call_command, **kwargs):
|
||||
@@ -11,6 +12,7 @@ def insert_subprocess_mock(check_call_command, **kwargs):
|
||||
subprocess.should_receive('check_call').with_args(check_call_command, **kwargs).once()
|
||||
|
||||
|
||||
|
||||
BASE_PRUNE_FLAGS = (
|
||||
('--keep-daily', '1'),
|
||||
('--keep-weekly', '2'),
|
||||
@@ -63,7 +65,6 @@ def test_prune_archives_calls_borg_with_parameters():
|
||||
insert_subprocess_mock(PRUNE_COMMAND)
|
||||
|
||||
module.prune_archives(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
@@ -71,33 +72,33 @@ def test_prune_archives_calls_borg_with_parameters():
|
||||
)
|
||||
|
||||
|
||||
def test_prune_archives_with_verbosity_some_calls_borg_with_info_parameter():
|
||||
def test_prune_archives_with_log_info_calls_borg_with_info_parameter():
|
||||
retention_config = flexmock()
|
||||
flexmock(module).should_receive('_make_prune_flags').with_args(retention_config).and_return(
|
||||
BASE_PRUNE_FLAGS,
|
||||
)
|
||||
insert_subprocess_mock(PRUNE_COMMAND + ('--info', '--stats',))
|
||||
insert_subprocess_mock(PRUNE_COMMAND + ('--stats', '--info',))
|
||||
insert_logging_mock(logging.INFO)
|
||||
|
||||
module.prune_archives(
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
verbosity=VERBOSITY_SOME,
|
||||
dry_run=False,
|
||||
retention_config=retention_config,
|
||||
)
|
||||
|
||||
|
||||
def test_prune_archives_with_verbosity_lots_calls_borg_with_debug_parameter():
|
||||
def test_prune_archives_with_log_debug_calls_borg_with_debug_parameter():
|
||||
retention_config = flexmock()
|
||||
flexmock(module).should_receive('_make_prune_flags').with_args(retention_config).and_return(
|
||||
BASE_PRUNE_FLAGS,
|
||||
)
|
||||
insert_subprocess_mock(PRUNE_COMMAND + ('--debug', '--stats', '--list'))
|
||||
insert_subprocess_mock(PRUNE_COMMAND + ('--stats', '--debug', '--list', '--show-rc'))
|
||||
insert_logging_mock(logging.DEBUG)
|
||||
|
||||
module.prune_archives(
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
verbosity=VERBOSITY_LOTS,
|
||||
dry_run=False,
|
||||
retention_config=retention_config,
|
||||
)
|
||||
@@ -113,7 +114,6 @@ def test_prune_archives_with_dry_run_calls_borg_with_dry_run_parameter():
|
||||
module.prune_archives(
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
verbosity=None,
|
||||
dry_run=True,
|
||||
retention_config=retention_config,
|
||||
)
|
||||
@@ -127,7 +127,6 @@ def test_prune_archives_with_local_path_calls_borg_via_local_path():
|
||||
insert_subprocess_mock(('borg1',) + PRUNE_COMMAND[1:])
|
||||
|
||||
module.prune_archives(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
@@ -144,7 +143,6 @@ def test_prune_archives_with_remote_path_calls_borg_with_remote_path_parameters(
|
||||
insert_subprocess_mock(PRUNE_COMMAND + ('--remote-path', 'borg1'))
|
||||
|
||||
module.prune_archives(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
storage_config={},
|
||||
@@ -162,7 +160,6 @@ def test_prune_archives_with_umask_calls_borg_with_umask_parameters():
|
||||
insert_subprocess_mock(PRUNE_COMMAND + ('--umask', '077'))
|
||||
|
||||
module.prune_archives(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
storage_config=storage_config,
|
||||
@@ -179,7 +176,6 @@ def test_prune_archives_with_lock_wait_calls_borg_with_lock_wait_parameters():
|
||||
insert_subprocess_mock(PRUNE_COMMAND + ('--lock-wait', '5'))
|
||||
|
||||
module.prune_archives(
|
||||
verbosity=None,
|
||||
dry_run=False,
|
||||
repository='repo',
|
||||
storage_config=storage_config,
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
import logging
|
||||
|
||||
from flexmock import flexmock
|
||||
|
||||
from borgmatic import verbosity as module
|
||||
|
||||
def insert_logging_mock(log_level):
|
||||
""" Mocks the isEnabledFor from python logging. """
|
||||
logging = flexmock(module.logging.Logger)
|
||||
logging.should_receive('isEnabledFor').replace_with(lambda lvl : lvl >= log_level)
|
||||
logging.should_receive('getEffectiveLevel').replace_with(lambda: log_level)
|
||||
|
||||
|
||||
def test_verbosity_to_log_level_maps_known_verbosity_to_log_level():
|
||||
assert module.verbosity_to_log_level(module.VERBOSITY_SOME) == logging.INFO
|
||||
assert module.verbosity_to_log_level(module.VERBOSITY_LOTS) == logging.DEBUG
|
||||
|
||||
|
||||
def test_verbosity_to_log_level_maps_unknown_verbosity_to_warning_level():
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import logging
|
||||
|
||||
|
||||
VERBOSITY_WARNING = 0
|
||||
VERBOSITY_SOME = 1
|
||||
VERBOSITY_LOTS = 2
|
||||
|
||||
@@ -10,6 +11,7 @@ def verbosity_to_log_level(verbosity):
|
||||
Given a borgmatic verbosity value, return the corresponding Python log level.
|
||||
'''
|
||||
return {
|
||||
VERBOSITY_WARNING: logging.WARNING,
|
||||
VERBOSITY_SOME: logging.INFO,
|
||||
VERBOSITY_LOTS: logging.DEBUG,
|
||||
}.get(verbosity, logging.WARNING)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# You can drop this file into /etc/cron.d/ to run borgmatic nightly.
|
||||
|
||||
0 3 * * * PATH=$PATH:/usr/bin /usr/bin/borgmatic
|
||||
@@ -22,22 +22,30 @@ for sub_command in prune create check list info; do
|
||||
sort borgmatic_borg_flags > borgmatic_borg_flags.sorted
|
||||
mv borgmatic_borg_flags.sorted borgmatic_borg_flags
|
||||
|
||||
for line in $(borg $sub_command --help | awk -v RS= '/^usage:/') ; do
|
||||
for word in $(borg $sub_command --help | grep '^ -') ; do
|
||||
# Exclude a bunch of flags that borgmatic actually supports, but don't get exercised by the
|
||||
# generated sample config, and also flags that don't make sense to support.
|
||||
echo "$line" | grep -- -- | sed -r 's/(\[|\])//g' \
|
||||
| grep -v '^-h$' \
|
||||
echo "$word" | grep ^-- | sed -e 's/,$//' \
|
||||
| grep -v '^--archives-only$' \
|
||||
| grep -v '^--repository-only$' \
|
||||
| grep -v '^--stats$' \
|
||||
| grep -v '^--list$' \
|
||||
| grep -v '^--critical$' \
|
||||
| grep -v '^--error$' \
|
||||
| grep -v '^--warning$' \
|
||||
| grep -v '^--info$' \
|
||||
| grep -v '^--debug$' \
|
||||
| grep -v '^--dry-run$' \
|
||||
| grep -v '^--error$' \
|
||||
| grep -v '^--help$' \
|
||||
| grep -v '^--info$' \
|
||||
| grep -v '^--list$' \
|
||||
| grep -v '^--nobsdflags$' \
|
||||
| grep -v '^--pattern$' \
|
||||
| grep -v '^--read-special$' \
|
||||
| grep -v '^--repository-only$' \
|
||||
| grep -v '^--show-rc$' \
|
||||
| grep -v '^--stats$' \
|
||||
| grep -v '^--verbose$' \
|
||||
| grep -v '^--warning$' \
|
||||
| grep -v '^-h$' \
|
||||
>> all_borg_flags
|
||||
done
|
||||
|
||||
sort all_borg_flags > all_borg_flags.sorted
|
||||
mv all_borg_flags.sorted all_borg_flags
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
|
||||
VERSION = '1.2.1'
|
||||
VERSION = '1.2.4'
|
||||
|
||||
|
||||
setup(
|
||||
@@ -32,7 +32,7 @@ setup(
|
||||
'atticmatic',
|
||||
],
|
||||
install_requires=(
|
||||
'pykwalify>=1.6.0',
|
||||
'pykwalify>=1.6.0,<14.06',
|
||||
'ruamel.yaml>0.15.0,<0.16.0',
|
||||
'setuptools',
|
||||
),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
flexmock==0.10.2
|
||||
pykwalify==1.6.1
|
||||
pytest==3.6.3
|
||||
pytest==3.7.4
|
||||
pytest-cov==2.5.1
|
||||
ruamel.yaml>0.15.0,<0.16.0
|
||||
|
||||
Reference in New Issue
Block a user