mirror of
https://github.com/borgmatic-collective/borgmatic.git
synced 2026-07-22 10:13:00 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4c09cbf1a4 | ||
|
|
fc077af4ce | ||
|
|
ca4312bb85 | ||
|
|
fc3b1fccba | ||
|
|
f83346b9b3 | ||
|
|
63c7241aec | ||
|
|
fd77dc579e | ||
|
|
f017ed648f | ||
|
|
27a6745743 | ||
|
|
95be0c8e46 | ||
|
|
17a774ba7e |
@@ -1,9 +0,0 @@
|
||||
syntax: glob
|
||||
*.egg-info
|
||||
*.pyc
|
||||
*.swp
|
||||
.cache
|
||||
.coverage
|
||||
.tox
|
||||
build
|
||||
dist
|
||||
@@ -1,40 +0,0 @@
|
||||
467d3a3ce9185e84ee51ca9156499162efd94f9a 0.0.2
|
||||
7730ae34665c0dedf46deab90b32780abf6dbaff 0.0.3
|
||||
4bb2e81fc77038be4499b7ea6797ab7d109460e0 0.0.4
|
||||
b31d51b633701554e84f996cc0c73bad2990780b 0.0.5
|
||||
b31d51b633701554e84f996cc0c73bad2990780b 0.0.5
|
||||
aa8a807f4ba28f0652764ed14713ffea2fd6922d 0.0.5
|
||||
aa8a807f4ba28f0652764ed14713ffea2fd6922d 0.0.5
|
||||
569aef47a9b25c55b13753f94706f5d330219995 0.0.5
|
||||
569aef47a9b25c55b13753f94706f5d330219995 0.0.5
|
||||
a03495a8e8b471da63b5e2ae79d3ff9065839c2a 0.0.5
|
||||
7ea93ca83f426ec0a608a68580c72c0775b81f86 0.0.6
|
||||
cf4c7065f0711deda1cba878398bc05390e2c3f9 0.0.7
|
||||
38d72677343f0a5d6845f4ac50d6778397083d45 0.1.0
|
||||
ac5dfa01e9d14d09845f5e94c2c679e21c5eb2f9 0.1.1
|
||||
ac5dfa01e9d14d09845f5e94c2c679e21c5eb2f9 0.1.1
|
||||
7b6c87dca7ea312b2257ac1b46857b3f8c56b39c 0.1.1
|
||||
83067f995dd391e38544a7722dc3b254b59c5521 0.1.2
|
||||
acc7fb61566fe8028c179f43ecc735c851220b06 0.1.3
|
||||
6dda59c12de88f060eb7244e6d330173985a9639 0.1.4
|
||||
6dda59c12de88f060eb7244e6d330173985a9639 0.1.4
|
||||
e58246fc92bb22c2b2fd8b86a1227de69d2d0315 0.1.4
|
||||
0afff209b902698c2266986129d6dc9f5f913101 0.1.5
|
||||
4c63f3d90ec2bf6af1714a3acec84654a7c9edf3 0.1.6
|
||||
5a458ebef804be14e30d7375e3e9fbc26aedb80d 0.1.7
|
||||
977f19c2f6a515be6c5ef69cf17b0e0989532209 github/yaml_config_files
|
||||
0000000000000000000000000000000000000000 github/yaml_config_files
|
||||
28434dd0440cc8da44c2f3e9bd7e9402a59c3b40 github/master
|
||||
dbc96d3f83bd5570b6826537616d4160b3374836 0.1.8
|
||||
0e1fbee9358de4f062fa9539e1355db83db70caa 1.0.0
|
||||
de2d7721cdec93a52d20222a9ddd579ed93c1017 1.0.1
|
||||
9603d13910b32d57a887765cab694ac5d0acc1f4 1.0.2
|
||||
32c6341dda9fad77a3982641bce8a3a45821842e 1.0.3
|
||||
5a003056a8ff4709c5bd4d6d33354199423f8a1d 1.1.0
|
||||
7d3d11eff6c0773883c48f221431f157bc7995eb 1.1.1
|
||||
f052a77a8ad5a0fea7fa86a902e0e401252f7d80 1.1.2
|
||||
3f838f661546e04529b453aa443529b432afc243 1.1.3
|
||||
3d605962d891731a0f372b903b556ac7a8c8359f 1.1.4
|
||||
64ca13bfe050f656b44ed2eb1c3db045bfddd133 1.1.5
|
||||
4daa944c122c572b9b56bfcac3f4e2869181c630 1.1.6
|
||||
ec7949a14a2051616f7cdcb8e05555f02e024ae8 1.1.7
|
||||
@@ -1,3 +1,11 @@
|
||||
1.1.10
|
||||
* Pass several Unix signals through to child processes like Borg. This means that Borg now properly
|
||||
shuts down if borgmatic is terminated (e.g. due to a system suspend).
|
||||
* #29: Support for using tilde in repository paths to reference home directory.
|
||||
* #42: Support for Borg --files-cache option for setting the files cache operation mode.
|
||||
* #44: Support for Borg --remote-ratelimit for limiting upload rate.
|
||||
* Log invoked Borg commands when at highest verbosity level.
|
||||
|
||||
1.1.9
|
||||
* #16, #38: Support for user-defined hooks before/after backup, or on error.
|
||||
* #33: Improve clarity of logging spew at high verbosity levels.
|
||||
|
||||
@@ -113,6 +113,10 @@ When you set up multiple configuration files like this, borgmatic will run
|
||||
each one in turn from a single borgmatic invocation. This includes, by
|
||||
default, the traditional /etc/borgmatic/config.yaml as well.
|
||||
|
||||
And if you need even more customizability, you can specify alternate
|
||||
configuration paths on the command-line with borgmatic's `--config` option.
|
||||
See `borgmatic --help` for more information.
|
||||
|
||||
|
||||
## Upgrading
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
@@ -8,6 +9,9 @@ from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
DEFAULT_CHECKS = ('repository', 'archives')
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _parse_checks(consistency_config):
|
||||
'''
|
||||
Given a consistency config with a "checks" list, transform it to a tuple of named checks to run.
|
||||
@@ -79,6 +83,7 @@ def check_archives(verbosity, repository, consistency_config, remote_path=None):
|
||||
# The check command spews to stdout/stderr even without the verbose flag. Suppress it.
|
||||
stdout = None if verbosity_flags else open(os.devnull, 'w')
|
||||
|
||||
logger.debug(' '.join(full_command))
|
||||
subprocess.check_call(full_command, stdout=stdout, stderr=subprocess.STDOUT)
|
||||
|
||||
if 'extract' in checks:
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import glob
|
||||
import itertools
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
@@ -7,6 +8,9 @@ import tempfile
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def initialize(storage_config):
|
||||
passphrase = storage_config.get('encryption_passphrase')
|
||||
|
||||
@@ -81,9 +85,13 @@ def create_archive(
|
||||
)
|
||||
compression = storage_config.get('compression', None)
|
||||
compression_flags = ('--compression', compression) if compression else ()
|
||||
remote_rate_limit = storage_config.get('remote_rate_limit', None)
|
||||
remote_rate_limit_flags = ('--remote-ratelimit', str(remote_rate_limit)) if remote_rate_limit else ()
|
||||
umask = storage_config.get('umask', None)
|
||||
umask_flags = ('--umask', str(umask)) if umask else ()
|
||||
one_file_system_flags = ('--one-file-system',) if location_config.get('one_file_system') else ()
|
||||
files_cache = location_config.get('files_cache')
|
||||
files_cache_flags = ('--files-cache', files_cache) if files_cache else ()
|
||||
remote_path = location_config.get('remote_path')
|
||||
remote_path_flags = ('--remote-path', remote_path) if remote_path else ()
|
||||
verbosity_flags = {
|
||||
@@ -99,7 +107,9 @@ def create_archive(
|
||||
repository=repository,
|
||||
archive_name_format=archive_name_format,
|
||||
),
|
||||
) + sources + exclude_flags + compression_flags + one_file_system_flags + \
|
||||
remote_path_flags + umask_flags + verbosity_flags
|
||||
) + sources + exclude_flags + compression_flags + remote_rate_limit_flags + \
|
||||
one_file_system_flags + files_cache_flags + remote_path_flags + umask_flags + \
|
||||
verbosity_flags
|
||||
|
||||
logger.debug(' '.join(full_command))
|
||||
subprocess.check_call(full_command)
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
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, remote_path=None):
|
||||
'''
|
||||
Perform an extraction dry-run of just the most recent archive. If there are no archives, skip
|
||||
@@ -37,4 +41,5 @@ def extract_last_archive_dry_run(verbosity, repository, remote_path=None):
|
||||
),
|
||||
) + remote_path_flags + verbosity_flags + list_flag
|
||||
|
||||
logger.debug(' '.join(full_extract_command))
|
||||
subprocess.check_call(full_extract_command)
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import logging
|
||||
import subprocess
|
||||
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _make_prune_flags(retention_config):
|
||||
'''
|
||||
Given a retention config dict mapping from option name to value, tranform it into an iterable of
|
||||
@@ -48,4 +52,5 @@ def prune_archives(verbosity, repository, retention_config, remote_path=None):
|
||||
for element in pair
|
||||
) + remote_path_flags + verbosity_flags
|
||||
|
||||
logger.debug(' '.join(full_command))
|
||||
subprocess.check_call(full_command)
|
||||
|
||||
@@ -8,6 +8,7 @@ import sys
|
||||
from borgmatic.borg import check, create, prune
|
||||
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
|
||||
|
||||
|
||||
@@ -96,7 +97,8 @@ def run_configuration(config_filename, args): # pragma: no cover
|
||||
create.initialize(storage)
|
||||
hook.execute_hook(hooks.get('before_backup'), config_filename, 'pre-backup')
|
||||
|
||||
for repository in location['repositories']:
|
||||
for unexpanded_repository in location['repositories']:
|
||||
repository = os.path.expanduser(unexpanded_repository)
|
||||
if args.prune:
|
||||
logger.info('{}: Pruning archives'.format(repository))
|
||||
prune.prune_archives(args.verbosity, repository, retention, remote_path=remote_path)
|
||||
@@ -120,6 +122,7 @@ def run_configuration(config_filename, args): # pragma: no cover
|
||||
|
||||
def main(): # pragma: no cover
|
||||
try:
|
||||
configure_signals()
|
||||
args = parse_arguments(*sys.argv[1:])
|
||||
logging.basicConfig(level=verbosity_to_log_level(args.verbosity), format='%(message)s')
|
||||
|
||||
|
||||
@@ -22,6 +22,13 @@ map:
|
||||
type: bool
|
||||
desc: Stay in same file system (do not cross mount points).
|
||||
example: true
|
||||
files_cache:
|
||||
type: scalar
|
||||
desc: |
|
||||
Mode in which to operate the files cache. See
|
||||
https://borgbackup.readthedocs.io/en/stable/usage/create.html#description for
|
||||
details.
|
||||
example: ctime,size,inode
|
||||
remote_path:
|
||||
type: scalar
|
||||
desc: Alternate Borg remote executable. Defaults to "borg".
|
||||
@@ -31,8 +38,8 @@ map:
|
||||
seq:
|
||||
- type: scalar
|
||||
desc: |
|
||||
Paths to local or remote repositories (required). Multiple repositories are
|
||||
backed up to in sequence.
|
||||
Paths to local or remote repositories (required). Tildes are expanded. Multiple
|
||||
repositories are backed up to in sequence.
|
||||
example:
|
||||
- user@backupserver:sourcehostname.borg
|
||||
exclude_patterns:
|
||||
@@ -85,6 +92,10 @@ map:
|
||||
https://borgbackup.readthedocs.org/en/stable/usage.html#borg-create for details.
|
||||
Defaults to no compression.
|
||||
example: lz4
|
||||
remote_rate_limit:
|
||||
type: int
|
||||
desc: Remote network upload rate limit in kiBytes/second.
|
||||
example: 100
|
||||
umask:
|
||||
type: scalar
|
||||
desc: Umask to be used for borg create.
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import os
|
||||
import signal
|
||||
|
||||
|
||||
def _handle_signal(signal_number, frame): # pragma: no cover
|
||||
'''
|
||||
Send the signal to all processes in borgmatic's process group, which includes child process.
|
||||
'''
|
||||
os.killpg(os.getpgrp(), signal_number)
|
||||
|
||||
|
||||
def configure_signals(): # pragma: no cover
|
||||
'''
|
||||
Configure borgmatic's signal handlers to pass relevant signals through to any child processes
|
||||
like Borg. Note that SIGINT gets passed through even without these changes.
|
||||
'''
|
||||
for signal_number in (signal.SIGHUP, signal.SIGTERM, signal.SIGUSR1, signal.SIGUSR2):
|
||||
signal.signal(signal_number, _handle_signal)
|
||||
@@ -231,6 +231,24 @@ def test_create_archive_with_compression_calls_borg_with_compression_parameters(
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_remote_rate_limit_calls_borg_with_remote_ratelimit_parameters():
|
||||
flexmock(module).should_receive('_expand_directory').and_return(['foo']).and_return(['bar'])
|
||||
flexmock(module).should_receive('_write_exclude_file').and_return(None)
|
||||
flexmock(module).should_receive('_make_exclude_flags').and_return(())
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--remote-ratelimit', '100'))
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
location_config={
|
||||
'source_directories': ['foo', 'bar'],
|
||||
'repositories': ['repo'],
|
||||
'exclude_patterns': None,
|
||||
},
|
||||
storage_config={'remote_rate_limit': 100},
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_one_file_system_calls_borg_with_one_file_system_parameters():
|
||||
flexmock(module).should_receive('_expand_directory').and_return(['foo']).and_return(['bar'])
|
||||
flexmock(module).should_receive('_write_exclude_file').and_return(None)
|
||||
@@ -250,6 +268,25 @@ def test_create_archive_with_one_file_system_calls_borg_with_one_file_system_par
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_files_cache_calls_borg_with_files_cache_parameters():
|
||||
flexmock(module).should_receive('_expand_directory').and_return(['foo']).and_return(['bar'])
|
||||
flexmock(module).should_receive('_write_exclude_file').and_return(None)
|
||||
flexmock(module).should_receive('_make_exclude_flags').and_return(())
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--files-cache', 'ctime,size'))
|
||||
|
||||
module.create_archive(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
location_config={
|
||||
'source_directories': ['foo', 'bar'],
|
||||
'repositories': ['repo'],
|
||||
'files_cache': 'ctime,size',
|
||||
'exclude_patterns': None,
|
||||
},
|
||||
storage_config={},
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_remote_path_calls_borg_with_remote_path_parameters():
|
||||
flexmock(module).should_receive('_expand_directory').and_return(['foo']).and_return(['bar'])
|
||||
flexmock(module).should_receive('_write_exclude_file').and_return(None)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
|
||||
VERSION = '1.1.9'
|
||||
VERSION = '1.1.10'
|
||||
|
||||
|
||||
setup(
|
||||
@@ -11,7 +11,6 @@ setup(
|
||||
author='Dan Helfman',
|
||||
author_email='witten@torsion.org',
|
||||
url='https://torsion.org/borgmatic',
|
||||
download_url='https://projects.torsion.org/witten/borgmatic/archive/%s.tar.gz' % VERSION,
|
||||
classifiers=(
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Environment :: Console',
|
||||
|
||||
Reference in New Issue
Block a user