Merge.
This commit is contained in:
commit
a5aa9355f5
8 changed files with 78 additions and 20 deletions
1
AUTHORS
1
AUTHORS
|
@ -5,3 +5,4 @@ Henning Schroeder: Copy editing
|
||||||
Michele Lazzeri: Custom archive names
|
Michele Lazzeri: Custom archive names
|
||||||
Robin `ypid` Schneider: Support additional options of Borg
|
Robin `ypid` Schneider: Support additional options of Borg
|
||||||
Scott Squires: Custom archive names
|
Scott Squires: Custom archive names
|
||||||
|
Johannes Feichtner: Support for user hooks
|
||||||
|
|
|
@ -66,6 +66,12 @@ To install borgmatic, run the following command to download and install it:
|
||||||
Note that your pip binary may have a different name than "pip3". Make sure
|
Note that your pip binary may have a different name than "pip3". Make sure
|
||||||
you're using Python 3, as borgmatic does not support Python 2.
|
you're using Python 3, as borgmatic does not support Python 2.
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
|
If you would like to run borgmatic within Docker, please take a look at
|
||||||
|
[b3vis/borgmatic](https://hub.docker.com/r/b3vis/borgmatic/) for more
|
||||||
|
information.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
After you install borgmatic, generate a sample configuration file:
|
After you install borgmatic, generate a sample configuration file:
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
|
@ -5,6 +6,7 @@ from subprocess import CalledProcessError
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from borgmatic.borg import check, create, prune
|
from borgmatic.borg import check, create, prune
|
||||||
|
from borgmatic.commands import hook
|
||||||
from borgmatic.config import collect, convert, validate
|
from borgmatic.config import collect, convert, validate
|
||||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS, verbosity_to_log_level
|
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS, verbosity_to_log_level
|
||||||
|
|
||||||
|
@ -92,13 +94,15 @@ def main(): # pragma: no cover
|
||||||
for config_filename in config_filenames:
|
for config_filename in config_filenames:
|
||||||
logger.info('{}: Parsing configuration file'.format(config_filename))
|
logger.info('{}: Parsing configuration file'.format(config_filename))
|
||||||
config = validate.parse_configuration(config_filename, validate.schema_filename())
|
config = validate.parse_configuration(config_filename, validate.schema_filename())
|
||||||
(location, storage, retention, consistency) = (
|
(location, storage, retention, consistency, hooks) = (
|
||||||
config.get(section_name, {})
|
config.get(section_name, {})
|
||||||
for section_name in ('location', 'storage', 'retention', 'consistency')
|
for section_name in ('location', 'storage', 'retention', 'consistency', 'hooks')
|
||||||
)
|
)
|
||||||
remote_path = location.get('remote_path')
|
remote_path = location.get('remote_path')
|
||||||
|
|
||||||
|
try:
|
||||||
create.initialize(storage)
|
create.initialize(storage)
|
||||||
|
hook.execute_hook(hooks.get('before_backup'))
|
||||||
|
|
||||||
for repository in location['repositories']:
|
for repository in location['repositories']:
|
||||||
if args.prune:
|
if args.prune:
|
||||||
|
@ -115,6 +119,11 @@ def main(): # pragma: no cover
|
||||||
if args.check:
|
if args.check:
|
||||||
logger.info('{}: Running consistency checks'.format(repository))
|
logger.info('{}: Running consistency checks'.format(repository))
|
||||||
check.check_archives(args.verbosity, repository, consistency, remote_path=remote_path)
|
check.check_archives(args.verbosity, repository, consistency, remote_path=remote_path)
|
||||||
|
|
||||||
|
hook.execute_hook(hooks.get('after_backup'))
|
||||||
|
except (OSError, CalledProcessError):
|
||||||
|
hook.execute_hook(hooks.get('on_error'))
|
||||||
|
raise
|
||||||
except (ValueError, OSError, CalledProcessError) as error:
|
except (ValueError, OSError, CalledProcessError) as error:
|
||||||
print(error, file=sys.stderr)
|
print(error, file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
7
borgmatic/commands/hook.py
Normal file
7
borgmatic/commands/hook.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
def execute_hook(commands):
|
||||||
|
if commands:
|
||||||
|
for cmd in commands:
|
||||||
|
subprocess.check_call(cmd, shell=True)
|
|
@ -24,7 +24,7 @@ def _schema_to_sample_configuration(schema, level=0):
|
||||||
for each section based on the schema "desc" description.
|
for each section based on the schema "desc" description.
|
||||||
'''
|
'''
|
||||||
example = schema.get('example')
|
example = schema.get('example')
|
||||||
if example:
|
if example is not None:
|
||||||
return example
|
return example
|
||||||
|
|
||||||
config = yaml.comments.CommentedMap([
|
config = yaml.comments.CommentedMap([
|
||||||
|
|
|
@ -157,3 +157,28 @@ map:
|
||||||
desc: Restrict the number of checked archives to the last n. Applies only to the
|
desc: Restrict the number of checked archives to the last n. Applies only to the
|
||||||
"archives" check.
|
"archives" check.
|
||||||
example: 3
|
example: 3
|
||||||
|
hooks:
|
||||||
|
desc: |
|
||||||
|
Shell commands or scripts to execute before and after a backup or if an error has occurred.
|
||||||
|
IMPORTANT: All provided commands and scripts are executed with user permissions of borgmatic.
|
||||||
|
Do not forget to set secure permissions on this file as well as on any script listed (chmod 0700) to
|
||||||
|
prevent potential shell injection or privilege escalation.
|
||||||
|
map:
|
||||||
|
before_backup:
|
||||||
|
seq:
|
||||||
|
- type: scalar
|
||||||
|
desc: List of one or more shell commands or scripts to execute before creating a backup.
|
||||||
|
example:
|
||||||
|
- echo "`date` - Starting a backup job."
|
||||||
|
after_backup:
|
||||||
|
seq:
|
||||||
|
- type: scalar
|
||||||
|
desc: List of one or more shell commands or scripts to execute after creating a backup.
|
||||||
|
example:
|
||||||
|
- echo "`date` - Backup created."
|
||||||
|
on_error:
|
||||||
|
seq:
|
||||||
|
- type: scalar
|
||||||
|
desc: List of one or more shell commands or scripts to execute in case an exception has occurred.
|
||||||
|
example:
|
||||||
|
- echo "`date` - Error while creating a backup."
|
||||||
|
|
|
@ -50,7 +50,7 @@ def parse_configuration(config_filename, schema_filename):
|
||||||
# simply remove all examples before passing the schema to pykwalify.
|
# simply remove all examples before passing the schema to pykwalify.
|
||||||
for section_name, section_schema in schema['map'].items():
|
for section_name, section_schema in schema['map'].items():
|
||||||
for field_name, field_schema in section_schema['map'].items():
|
for field_name, field_schema in section_schema['map'].items():
|
||||||
field_schema.pop('example')
|
field_schema.pop('example', None)
|
||||||
|
|
||||||
validator = pykwalify.core.Core(source_data=config, schema_data=schema)
|
validator = pykwalify.core.Core(source_data=config, schema_data=schema)
|
||||||
parsed_result = validator.validate(raise_exception=False)
|
parsed_result = validator.validate(raise_exception=False)
|
||||||
|
|
10
borgmatic/tests/unit/borg/test_hook.py
Normal file
10
borgmatic/tests/unit/borg/test_hook.py
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
from flexmock import flexmock
|
||||||
|
|
||||||
|
from borgmatic.commands import hook as module
|
||||||
|
|
||||||
|
|
||||||
|
def test_execute_hook_invokes_each_command():
|
||||||
|
subprocess = flexmock(module.subprocess)
|
||||||
|
subprocess.should_receive('check_call').with_args(':', shell=True).once()
|
||||||
|
|
||||||
|
module.execute_hook([':'])
|
Loading…
Reference in a new issue