mirror of
https://github.com/borgmatic-collective/borgmatic.git
synced 2026-07-22 18:13:02 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b22b552bf3 | ||
|
|
938392b25b | ||
|
|
481dbc14c3 | ||
|
|
600c438951 | ||
|
|
2e3e68d2cb | ||
|
|
331adca23e | ||
|
|
e1e5db22f8 | ||
|
|
377e3948ff | ||
|
|
4533fec167 | ||
|
|
633700c0af | ||
|
|
007b3e6d4e | ||
|
|
175761c757 | ||
|
|
c7e23fe9ed |
@@ -25,3 +25,6 @@ e58246fc92bb22c2b2fd8b86a1227de69d2d0315 0.1.4
|
||||
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
|
||||
|
||||
@@ -1,3 +1,20 @@
|
||||
1.0.2
|
||||
|
||||
* #20: Fix for traceback when remote_path option is missing.
|
||||
|
||||
1.0.1
|
||||
|
||||
* #19: Support for Borg's --remote-path option to use an alternate Borg
|
||||
executable. See sample/config.
|
||||
|
||||
1.0.0
|
||||
|
||||
* Attic is no longer supported, as there hasn't been any recent development on
|
||||
it. Dropping Attic support will allow faster iteration on Borg-specific
|
||||
features. If you're still using Attic, this is a good time to switch to Borg!
|
||||
* Project renamed from atticmatic to borgmatic. See the borgmatic README for
|
||||
information on upgrading.
|
||||
|
||||
0.1.8
|
||||
|
||||
* Fix for handling of spaces in source_directories which resulted in backup up everything.
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
title: Atticmatic
|
||||
title: Borgmatic
|
||||
|
||||
## Overview
|
||||
|
||||
atticmatic is a simple Python wrapper script for the
|
||||
[Attic](https://attic-backup.org/) and
|
||||
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
|
||||
@@ -19,7 +18,7 @@ Here's an example config file:
|
||||
source_directories: /home /etc /var/log/syslog*
|
||||
|
||||
# Path to local or remote backup repository.
|
||||
repository: user@backupserver:sourcehostname.attic
|
||||
repository: user@backupserver:sourcehostname.borg
|
||||
|
||||
[retention]
|
||||
# Retention policy for how many backups to keep in each category.
|
||||
@@ -35,37 +34,30 @@ checks: repository archives
|
||||
Additionally, exclude patterns can be specified in a separate excludes config
|
||||
file, one pattern per line.
|
||||
|
||||
atticmatic is hosted at <https://torsion.org/atticmatic> with [source code
|
||||
available](https://torsion.org/hg/atticmatic). It's also mirrored on
|
||||
[GitHub](https://github.com/witten/atticmatic) and
|
||||
[BitBucket](https://bitbucket.org/dhelfman/atticmatic) for convenience.
|
||||
borgmatic is hosted at <https://torsion.org/borgmatic> with [source code
|
||||
available](https://torsion.org/hg/borgmatic). It's also mirrored on
|
||||
[GitHub](https://github.com/witten/borgmatic) and
|
||||
[BitBucket](https://bitbucket.org/dhelfman/borgmatic) for convenience.
|
||||
|
||||
|
||||
## Setup
|
||||
|
||||
To get up and running, follow the [Attic Quick
|
||||
Start](https://attic-backup.org/quickstart.html) or the [Borg Quick
|
||||
Start](https://borgbackup.readthedocs.org/en/latest/quickstart.html) to create a
|
||||
repository on a local or remote host. Note that if you plan to run atticmatic
|
||||
on a schedule with cron, and you encrypt your attic repository with a
|
||||
passphrase instead of a key file, you'll need to set the atticmatic
|
||||
To get up and running, follow the [Borg Quick
|
||||
Start](https://borgbackup.readthedocs.org/en/latest/quickstart.html) to create
|
||||
a repository on a local or remote host. Note that if you plan to run
|
||||
borgmatic on a schedule with cron, and you encrypt your Borg repository with
|
||||
a passphrase instead of a key file, you'll need to set the borgmatic
|
||||
`encryption_passphrase` configuration variable. See the repository encryption
|
||||
section of the Quick Start for more info.
|
||||
|
||||
If the repository is on a remote host, make sure that your local root user has
|
||||
key-based ssh access to the desired user account on the remote host.
|
||||
|
||||
To install atticmatic, run the following command to download and install it:
|
||||
To install borgmatic, run the following command to download and install it:
|
||||
|
||||
sudo pip install --upgrade atticmatic
|
||||
sudo pip install --upgrade borgmatic
|
||||
|
||||
If you are using Attic, copy the following configuration files:
|
||||
|
||||
sudo cp sample/atticmatic.cron /etc/cron.d/atticmatic
|
||||
sudo mkdir /etc/atticmatic/
|
||||
sudo cp sample/config sample/excludes /etc/atticmatic/
|
||||
|
||||
If you are using Borg, copy the files like this instead:
|
||||
Then, copy the following configuration files:
|
||||
|
||||
sudo cp sample/borgmatic.cron /etc/cron.d/borgmatic
|
||||
sudo mkdir /etc/borgmatic/
|
||||
@@ -74,16 +66,31 @@ If you are using Borg, copy the files like this instead:
|
||||
Lastly, modify the /etc files with your desired configuration.
|
||||
|
||||
|
||||
## Upgrading from atticmatic
|
||||
|
||||
You can ignore this section if you're not an atticmatic user (the former name
|
||||
of borgmatic).
|
||||
|
||||
borgmatic only supports Borg now and no longer supports Attic. So if you're
|
||||
an Attic user, consider switching to Borg. See the [Borg upgrade
|
||||
command](https://borgbackup.readthedocs.io/en/stable/usage.html#borg-upgrade)
|
||||
for more information. Then, follow the instructions above about setting up
|
||||
your borgmatic configuration files.
|
||||
|
||||
If you were already using Borg with atticmatic, then you can easily upgrade
|
||||
from atticmatic to borgmatic. Simply run the following commands:
|
||||
|
||||
sudo pip uninstall atticmatic
|
||||
sudo pip install borgmatic
|
||||
|
||||
That's it! borgmatic will continue using your /etc/borgmatic configuration
|
||||
files.
|
||||
|
||||
## Usage
|
||||
|
||||
You can run atticmatic and start a backup simply by invoking it without
|
||||
You can run borgmatic and start a backup simply by invoking it without
|
||||
arguments:
|
||||
|
||||
atticmatic
|
||||
|
||||
Or, if you're using Borg, use this command instead to make use of the Borg
|
||||
backend:
|
||||
|
||||
borgmatic
|
||||
|
||||
This will also prune any old backups as per the configured retention policy,
|
||||
@@ -93,15 +100,15 @@ By default, the backup will proceed silently except in the case of errors. But
|
||||
if you'd like to to get additional information about the progress of the
|
||||
backup as it proceeds, use the verbosity option:
|
||||
|
||||
atticmattic --verbosity 1
|
||||
borgmatic --verbosity 1
|
||||
|
||||
Or, for even more progress spew:
|
||||
|
||||
atticmattic --verbosity 2
|
||||
borgmatic --verbosity 2
|
||||
|
||||
If you'd like to see the available command-line arguments, view the help:
|
||||
|
||||
atticmattic --help
|
||||
borgmatic --help
|
||||
|
||||
|
||||
## Running tests
|
||||
@@ -119,12 +126,12 @@ Then, to actually run tests, run:
|
||||
|
||||
### Broken pipe with remote repository
|
||||
|
||||
When running atticmatic on a large remote repository, you may receive errors
|
||||
like the following, particularly while "attic check" is validating backups for
|
||||
When running borgmatic on a large remote repository, you may receive errors
|
||||
like the following, particularly while "borg check" is validating backups for
|
||||
consistency:
|
||||
|
||||
Write failed: Broken pipe
|
||||
attic: Error: Connection closed by remote host
|
||||
borg: Error: Connection closed by remote host
|
||||
|
||||
This error can be caused by an ssh timeout, which you can rectify by adding
|
||||
the following to the ~/.ssh/config file on the client:
|
||||
@@ -138,8 +145,8 @@ backups.
|
||||
|
||||
## Issues and feedback
|
||||
|
||||
Got an issue or an idea for a feature enhancement? Check out the [atticmatic
|
||||
issue tracker](https://tree.taiga.io/project/witten-atticmatic/issues). In
|
||||
Got an issue or an idea for a feature enhancement? Check out the [borgmatic
|
||||
issue tracker](https://tree.taiga.io/project/witten-borgmatic/issues?page=1&status=399951,399952,399955). In
|
||||
order to create a new issue or comment on an issue, you'll need to [login
|
||||
first](https://tree.taiga.io/login).
|
||||
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
from functools import partial
|
||||
|
||||
from atticmatic.backends import shared
|
||||
|
||||
# An atticmatic backend that supports Attic for actually handling backups.
|
||||
|
||||
COMMAND = 'attic'
|
||||
CONFIG_FORMAT = shared.CONFIG_FORMAT
|
||||
|
||||
|
||||
initialize = partial(shared.initialize, command=COMMAND)
|
||||
create_archive = partial(shared.create_archive, command=COMMAND)
|
||||
prune_archives = partial(shared.prune_archives, command=COMMAND)
|
||||
check_archives = partial(shared.check_archives, command=COMMAND)
|
||||
@@ -1,41 +0,0 @@
|
||||
from functools import partial
|
||||
|
||||
from atticmatic.config import Section_format, option
|
||||
from atticmatic.backends import shared
|
||||
|
||||
# An atticmatic backend that supports Borg for actually handling backups.
|
||||
|
||||
COMMAND = 'borg'
|
||||
CONFIG_FORMAT = (
|
||||
Section_format(
|
||||
'location',
|
||||
(
|
||||
option('source_directories'),
|
||||
option('one_file_system', value_type=bool, required=False),
|
||||
option('repository'),
|
||||
),
|
||||
),
|
||||
Section_format(
|
||||
'storage',
|
||||
(
|
||||
option('encryption_passphrase', required=False),
|
||||
option('compression', required=False),
|
||||
option('umask', required=False),
|
||||
),
|
||||
),
|
||||
shared.CONFIG_FORMAT[2], # retention
|
||||
Section_format(
|
||||
'consistency',
|
||||
(
|
||||
option('checks', required=False),
|
||||
option('check_last', required=False),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
initialize = partial(shared.initialize, command=COMMAND)
|
||||
|
||||
create_archive = partial(shared.create_archive, command=COMMAND)
|
||||
prune_archives = partial(shared.prune_archives, command=COMMAND)
|
||||
check_archives = partial(shared.check_archives, command=COMMAND)
|
||||
@@ -1,75 +0,0 @@
|
||||
from __future__ import print_function
|
||||
from argparse import ArgumentParser
|
||||
from importlib import import_module
|
||||
import os
|
||||
from subprocess import CalledProcessError
|
||||
import sys
|
||||
|
||||
from atticmatic.config import parse_configuration
|
||||
|
||||
|
||||
DEFAULT_CONFIG_FILENAME_PATTERN = '/etc/{}/config'
|
||||
DEFAULT_EXCLUDES_FILENAME_PATTERN = '/etc/{}/excludes'
|
||||
|
||||
|
||||
def parse_arguments(command_name, *arguments):
|
||||
'''
|
||||
Given the name of the command with which this script was invoked and command-line arguments,
|
||||
parse the arguments and return them as an ArgumentParser instance. Use the command name to
|
||||
determine the default configuration and excludes paths.
|
||||
'''
|
||||
config_filename_default = DEFAULT_CONFIG_FILENAME_PATTERN.format(command_name)
|
||||
excludes_filename_default = DEFAULT_EXCLUDES_FILENAME_PATTERN.format(command_name)
|
||||
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument(
|
||||
'-c', '--config',
|
||||
dest='config_filename',
|
||||
default=config_filename_default,
|
||||
help='Configuration filename',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--excludes',
|
||||
dest='excludes_filename',
|
||||
default=excludes_filename_default if os.path.exists(excludes_filename_default) else None,
|
||||
help='Excludes filename',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-v', '--verbosity',
|
||||
type=int,
|
||||
help='Display verbose progress (1 for some, 2 for lots)',
|
||||
)
|
||||
|
||||
return parser.parse_args(arguments)
|
||||
|
||||
|
||||
def load_backend(command_name):
|
||||
'''
|
||||
Given the name of the command with which this script was invoked, return the corresponding
|
||||
backend module responsible for actually dealing with backups.
|
||||
'''
|
||||
backend_name = {
|
||||
'atticmatic': 'attic',
|
||||
'borgmatic': 'borg',
|
||||
}.get(command_name, 'attic')
|
||||
|
||||
return import_module('atticmatic.backends.{}'.format(backend_name))
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
command_name = os.path.basename(sys.argv[0])
|
||||
args = parse_arguments(command_name, *sys.argv[1:])
|
||||
backend = load_backend(command_name)
|
||||
config = parse_configuration(args.config_filename, backend.CONFIG_FORMAT)
|
||||
repository = config.location['repository']
|
||||
|
||||
backend.initialize(config.storage)
|
||||
backend.create_archive(
|
||||
args.excludes_filename, args.verbosity, config.storage, **config.location
|
||||
)
|
||||
backend.prune_archives(args.verbosity, repository, config.retention)
|
||||
backend.check_archives(args.verbosity, repository, config.consistency)
|
||||
except (ValueError, IOError, CalledProcessError) as error:
|
||||
print(error, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
@@ -1,33 +0,0 @@
|
||||
from flexmock import flexmock
|
||||
|
||||
from atticmatic import command as module
|
||||
|
||||
|
||||
def test_load_backend_with_atticmatic_command_should_return_attic_backend():
|
||||
backend = flexmock()
|
||||
(
|
||||
flexmock(module).should_receive('import_module').with_args('atticmatic.backends.attic')
|
||||
.and_return(backend).once()
|
||||
)
|
||||
|
||||
assert module.load_backend('atticmatic') == backend
|
||||
|
||||
|
||||
def test_load_backend_with_unknown_command_should_return_attic_backend():
|
||||
backend = flexmock()
|
||||
(
|
||||
flexmock(module).should_receive('import_module').with_args('atticmatic.backends.attic')
|
||||
.and_return(backend).once()
|
||||
)
|
||||
|
||||
assert module.load_backend('unknownmatic') == backend
|
||||
|
||||
|
||||
def test_load_backend_with_borgmatic_command_should_return_borg_backend():
|
||||
backend = flexmock()
|
||||
(
|
||||
flexmock(module).should_receive('import_module').with_args('atticmatic.backends.borg')
|
||||
.and_return(backend).once()
|
||||
)
|
||||
|
||||
assert module.load_backend('borgmatic') == backend
|
||||
@@ -6,52 +6,16 @@ import subprocess
|
||||
from glob import glob
|
||||
from itertools import chain
|
||||
|
||||
from atticmatic.config import Section_format, option
|
||||
from atticmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
|
||||
|
||||
# Common backend functionality shared by Attic and Borg. As the two backup
|
||||
# commands diverge, these shared functions will likely need to be replaced
|
||||
# with non-shared functions within atticmatic.backends.attic and
|
||||
# atticmatic.backends.borg.
|
||||
# Integration with Borg for actually handling backups.
|
||||
|
||||
|
||||
CONFIG_FORMAT = (
|
||||
Section_format(
|
||||
'location',
|
||||
(
|
||||
option('source_directories'),
|
||||
option('repository'),
|
||||
),
|
||||
),
|
||||
Section_format(
|
||||
'storage',
|
||||
(
|
||||
option('encryption_passphrase', required=False),
|
||||
),
|
||||
),
|
||||
Section_format(
|
||||
'retention',
|
||||
(
|
||||
option('keep_within', required=False),
|
||||
option('keep_hourly', int, required=False),
|
||||
option('keep_daily', int, required=False),
|
||||
option('keep_weekly', int, required=False),
|
||||
option('keep_monthly', int, required=False),
|
||||
option('keep_yearly', int, required=False),
|
||||
option('prefix', required=False),
|
||||
),
|
||||
),
|
||||
Section_format(
|
||||
'consistency',
|
||||
(
|
||||
option('checks', required=False),
|
||||
),
|
||||
)
|
||||
)
|
||||
COMMAND = 'borg'
|
||||
|
||||
|
||||
def initialize(storage_config, command):
|
||||
def initialize(storage_config, command=COMMAND):
|
||||
passphrase = storage_config.get('encryption_passphrase')
|
||||
|
||||
if passphrase:
|
||||
@@ -59,8 +23,8 @@ def initialize(storage_config, command):
|
||||
|
||||
|
||||
def create_archive(
|
||||
excludes_filename, verbosity, storage_config, source_directories, repository, command,
|
||||
one_file_system=None
|
||||
excludes_filename, verbosity, storage_config, source_directories, repository, command=COMMAND,
|
||||
one_file_system=None, remote_path=None,
|
||||
):
|
||||
'''
|
||||
Given an excludes filename (or None), a vebosity flag, a storage config dict, a space-separated
|
||||
@@ -75,6 +39,7 @@ def create_archive(
|
||||
umask = storage_config.get('umask', None)
|
||||
umask_flags = ('--umask', str(umask)) if umask else ()
|
||||
one_file_system_flags = ('--one-file-system',) if one_file_system else ()
|
||||
remote_path_flags = ('--remote-path', remote_path) if remote_path else ()
|
||||
verbosity_flags = {
|
||||
VERBOSITY_SOME: ('--stats',),
|
||||
VERBOSITY_LOTS: ('--verbose', '--stats'),
|
||||
@@ -88,7 +53,7 @@ def create_archive(
|
||||
timestamp=datetime.now().isoformat(),
|
||||
),
|
||||
) + sources + exclude_flags + compression_flags + one_file_system_flags + \
|
||||
umask_flags + verbosity_flags
|
||||
remote_path_flags + umask_flags + verbosity_flags
|
||||
|
||||
subprocess.check_call(full_command)
|
||||
|
||||
@@ -115,12 +80,13 @@ def _make_prune_flags(retention_config):
|
||||
)
|
||||
|
||||
|
||||
def prune_archives(verbosity, repository, retention_config, command):
|
||||
def prune_archives(verbosity, repository, retention_config, command=COMMAND, remote_path=None):
|
||||
'''
|
||||
Given a verbosity flag, a local or remote repository path, a retention config dict, and a
|
||||
command to run, prune attic archives according the the retention policy specified in that
|
||||
configuration.
|
||||
'''
|
||||
remote_path_flags = ('--remote-path', remote_path) if remote_path else ()
|
||||
verbosity_flags = {
|
||||
VERBOSITY_SOME: ('--stats',),
|
||||
VERBOSITY_LOTS: ('--verbose', '--stats'),
|
||||
@@ -133,7 +99,7 @@ def prune_archives(verbosity, repository, retention_config, command):
|
||||
element
|
||||
for pair in _make_prune_flags(retention_config)
|
||||
for element in pair
|
||||
) + verbosity_flags
|
||||
) + remote_path_flags + verbosity_flags
|
||||
|
||||
subprocess.check_call(full_command)
|
||||
|
||||
@@ -191,7 +157,7 @@ def _make_check_flags(checks, check_last=None):
|
||||
) + last_flag
|
||||
|
||||
|
||||
def check_archives(verbosity, repository, consistency_config, command):
|
||||
def check_archives(verbosity, repository, consistency_config, command=COMMAND, remote_path=None):
|
||||
'''
|
||||
Given a verbosity flag, a local or remote repository path, a consistency config dict, and a
|
||||
command to run, check the contained attic archives for consistency.
|
||||
@@ -203,6 +169,7 @@ def check_archives(verbosity, repository, consistency_config, command):
|
||||
if not checks:
|
||||
return
|
||||
|
||||
remote_path_flags = ('--remote-path', remote_path) if remote_path else ()
|
||||
verbosity_flags = {
|
||||
VERBOSITY_SOME: ('--verbose',),
|
||||
VERBOSITY_LOTS: ('--verbose',),
|
||||
@@ -211,7 +178,7 @@ def check_archives(verbosity, repository, consistency_config, command):
|
||||
full_command = (
|
||||
command, 'check',
|
||||
repository,
|
||||
) + _make_check_flags(checks, check_last) + verbosity_flags
|
||||
) + _make_check_flags(checks, check_last) + remote_path_flags + verbosity_flags
|
||||
|
||||
# The check command spews to stdout/stderr even without the verbose flag. Suppress it.
|
||||
stdout = None if verbosity_flags else open(os.devnull, 'w')
|
||||
@@ -0,0 +1,58 @@
|
||||
from __future__ import print_function
|
||||
from argparse import ArgumentParser
|
||||
import os
|
||||
from subprocess import CalledProcessError
|
||||
import sys
|
||||
|
||||
from borgmatic import borg
|
||||
from borgmatic.config import parse_configuration, CONFIG_FORMAT
|
||||
|
||||
|
||||
DEFAULT_CONFIG_FILENAME = '/etc/borgmatic/config'
|
||||
DEFAULT_EXCLUDES_FILENAME = '/etc/borgmatic/excludes'
|
||||
|
||||
|
||||
def parse_arguments(*arguments):
|
||||
'''
|
||||
Given the name of the command with which this script was invoked and command-line arguments,
|
||||
parse the arguments and return them as an ArgumentParser instance. Use the command name to
|
||||
determine the default configuration and excludes paths.
|
||||
'''
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument(
|
||||
'-c', '--config',
|
||||
dest='config_filename',
|
||||
default=DEFAULT_CONFIG_FILENAME,
|
||||
help='Configuration filename',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--excludes',
|
||||
dest='excludes_filename',
|
||||
default=DEFAULT_EXCLUDES_FILENAME if os.path.exists(DEFAULT_EXCLUDES_FILENAME) else None,
|
||||
help='Excludes filename',
|
||||
)
|
||||
parser.add_argument(
|
||||
'-v', '--verbosity',
|
||||
type=int,
|
||||
help='Display verbose progress (1 for some, 2 for lots)',
|
||||
)
|
||||
|
||||
return parser.parse_args(arguments)
|
||||
|
||||
|
||||
def main():
|
||||
try:
|
||||
args = parse_arguments(*sys.argv[1:])
|
||||
config = parse_configuration(args.config_filename, CONFIG_FORMAT)
|
||||
repository = config.location['repository']
|
||||
remote_path = config.location.get('remote_path')
|
||||
|
||||
borg.initialize(config.storage)
|
||||
borg.create_archive(
|
||||
args.excludes_filename, args.verbosity, config.storage, **config.location
|
||||
)
|
||||
borg.prune_archives(args.verbosity, repository, config.retention, remote_path=remote_path)
|
||||
borg.check_archives(args.verbosity, repository, config.consistency, remote_path=remote_path)
|
||||
except (ValueError, IOError, CalledProcessError) as error:
|
||||
print(error, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
@@ -20,6 +20,46 @@ def option(name, value_type=str, required=True):
|
||||
return Config_option(name, value_type, required)
|
||||
|
||||
|
||||
CONFIG_FORMAT = (
|
||||
Section_format(
|
||||
'location',
|
||||
(
|
||||
option('source_directories'),
|
||||
option('one_file_system', value_type=bool, required=False),
|
||||
option('remote_path', required=False),
|
||||
option('repository'),
|
||||
),
|
||||
),
|
||||
Section_format(
|
||||
'storage',
|
||||
(
|
||||
option('encryption_passphrase', required=False),
|
||||
option('compression', required=False),
|
||||
option('umask', required=False),
|
||||
),
|
||||
),
|
||||
Section_format(
|
||||
'retention',
|
||||
(
|
||||
option('keep_within', required=False),
|
||||
option('keep_hourly', int, required=False),
|
||||
option('keep_daily', int, required=False),
|
||||
option('keep_weekly', int, required=False),
|
||||
option('keep_monthly', int, required=False),
|
||||
option('keep_yearly', int, required=False),
|
||||
option('prefix', required=False),
|
||||
),
|
||||
),
|
||||
Section_format(
|
||||
'consistency',
|
||||
(
|
||||
option('checks', required=False),
|
||||
option('check_last', required=False),
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def validate_configuration_format(parser, config_format):
|
||||
'''
|
||||
Given an open RawConfigParser and an expected config file format, validate that the parsed
|
||||
+13
-16
@@ -4,26 +4,23 @@ import sys
|
||||
from flexmock import flexmock
|
||||
import pytest
|
||||
|
||||
from atticmatic import command as module
|
||||
|
||||
|
||||
COMMAND_NAME = 'foomatic'
|
||||
from borgmatic import command as module
|
||||
|
||||
|
||||
def test_parse_arguments_with_no_arguments_uses_defaults():
|
||||
flexmock(os.path).should_receive('exists').and_return(True)
|
||||
|
||||
parser = module.parse_arguments(COMMAND_NAME)
|
||||
parser = module.parse_arguments()
|
||||
|
||||
assert parser.config_filename == module.DEFAULT_CONFIG_FILENAME_PATTERN.format(COMMAND_NAME)
|
||||
assert parser.excludes_filename == module.DEFAULT_EXCLUDES_FILENAME_PATTERN.format(COMMAND_NAME)
|
||||
assert parser.config_filename == module.DEFAULT_CONFIG_FILENAME
|
||||
assert parser.excludes_filename == module.DEFAULT_EXCLUDES_FILENAME
|
||||
assert parser.verbosity == None
|
||||
|
||||
|
||||
def test_parse_arguments_with_filename_arguments_overrides_defaults():
|
||||
flexmock(os.path).should_receive('exists').and_return(True)
|
||||
|
||||
parser = module.parse_arguments(COMMAND_NAME, '--config', 'myconfig', '--excludes', 'myexcludes')
|
||||
parser = module.parse_arguments('--config', 'myconfig', '--excludes', 'myexcludes')
|
||||
|
||||
assert parser.config_filename == 'myconfig'
|
||||
assert parser.excludes_filename == 'myexcludes'
|
||||
@@ -33,9 +30,9 @@ def test_parse_arguments_with_filename_arguments_overrides_defaults():
|
||||
def test_parse_arguments_with_missing_default_excludes_file_sets_filename_to_none():
|
||||
flexmock(os.path).should_receive('exists').and_return(False)
|
||||
|
||||
parser = module.parse_arguments(COMMAND_NAME)
|
||||
parser = module.parse_arguments()
|
||||
|
||||
assert parser.config_filename == module.DEFAULT_CONFIG_FILENAME_PATTERN.format(COMMAND_NAME)
|
||||
assert parser.config_filename == module.DEFAULT_CONFIG_FILENAME
|
||||
assert parser.excludes_filename == None
|
||||
assert parser.verbosity == None
|
||||
|
||||
@@ -43,9 +40,9 @@ def test_parse_arguments_with_missing_default_excludes_file_sets_filename_to_non
|
||||
def test_parse_arguments_with_missing_overridden_excludes_file_retains_filename():
|
||||
flexmock(os.path).should_receive('exists').and_return(False)
|
||||
|
||||
parser = module.parse_arguments(COMMAND_NAME, '--excludes', 'myexcludes')
|
||||
parser = module.parse_arguments('--excludes', 'myexcludes')
|
||||
|
||||
assert parser.config_filename == module.DEFAULT_CONFIG_FILENAME_PATTERN.format(COMMAND_NAME)
|
||||
assert parser.config_filename == module.DEFAULT_CONFIG_FILENAME
|
||||
assert parser.excludes_filename == 'myexcludes'
|
||||
assert parser.verbosity == None
|
||||
|
||||
@@ -53,10 +50,10 @@ def test_parse_arguments_with_missing_overridden_excludes_file_retains_filename(
|
||||
def test_parse_arguments_with_verbosity_flag_overrides_default():
|
||||
flexmock(os.path).should_receive('exists').and_return(True)
|
||||
|
||||
parser = module.parse_arguments(COMMAND_NAME, '--verbosity', '1')
|
||||
parser = module.parse_arguments('--verbosity', '1')
|
||||
|
||||
assert parser.config_filename == module.DEFAULT_CONFIG_FILENAME_PATTERN.format(COMMAND_NAME)
|
||||
assert parser.excludes_filename == module.DEFAULT_EXCLUDES_FILENAME_PATTERN.format(COMMAND_NAME)
|
||||
assert parser.config_filename == module.DEFAULT_CONFIG_FILENAME
|
||||
assert parser.excludes_filename == module.DEFAULT_EXCLUDES_FILENAME
|
||||
assert parser.verbosity == 1
|
||||
|
||||
|
||||
@@ -67,6 +64,6 @@ def test_parse_arguments_with_invalid_arguments_exits():
|
||||
|
||||
try:
|
||||
with pytest.raises(SystemExit):
|
||||
module.parse_arguments(COMMAND_NAME, '--posix-me-harder')
|
||||
module.parse_arguments('--posix-me-harder')
|
||||
finally:
|
||||
sys.stderr = original_stderr
|
||||
+1
-1
@@ -8,7 +8,7 @@ except ImportError:
|
||||
from collections import OrderedDict
|
||||
import string
|
||||
|
||||
from atticmatic import config as module
|
||||
from borgmatic import config as module
|
||||
|
||||
|
||||
def test_parse_section_options_with_punctuation_should_return_section_options():
|
||||
+103
-47
@@ -4,9 +4,9 @@ import os
|
||||
|
||||
from flexmock import flexmock
|
||||
|
||||
from atticmatic.backends import shared as module
|
||||
from atticmatic.tests.builtins import builtins_mock
|
||||
from atticmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
from borgmatic import borg as module
|
||||
from borgmatic.tests.builtins import builtins_mock
|
||||
from borgmatic.verbosity import VERBOSITY_SOME, VERBOSITY_LOTS
|
||||
|
||||
|
||||
def test_initialize_with_passphrase_should_set_environment():
|
||||
@@ -14,8 +14,8 @@ def test_initialize_with_passphrase_should_set_environment():
|
||||
|
||||
try:
|
||||
os.environ = {}
|
||||
module.initialize({'encryption_passphrase': 'pass'}, command='attic')
|
||||
assert os.environ.get('ATTIC_PASSPHRASE') == 'pass'
|
||||
module.initialize({'encryption_passphrase': 'pass'}, command='borg')
|
||||
assert os.environ.get('BORG_PASSPHRASE') == 'pass'
|
||||
finally:
|
||||
os.environ = orig_environ
|
||||
|
||||
@@ -25,8 +25,8 @@ def test_initialize_without_passphrase_should_not_set_environment():
|
||||
|
||||
try:
|
||||
os.environ = {}
|
||||
module.initialize({}, command='attic')
|
||||
assert os.environ.get('ATTIC_PASSPHRASE') == None
|
||||
module.initialize({}, command='borg')
|
||||
assert os.environ.get('BORG_PASSPHRASE') == None
|
||||
finally:
|
||||
os.environ = orig_environ
|
||||
|
||||
@@ -53,11 +53,11 @@ def insert_datetime_mock():
|
||||
).mock
|
||||
|
||||
|
||||
CREATE_COMMAND_WITHOUT_EXCLUDES = ('attic', 'create', 'repo::host-now', 'foo', 'bar')
|
||||
CREATE_COMMAND_WITHOUT_EXCLUDES = ('borg', 'create', 'repo::host-now', 'foo', 'bar')
|
||||
CREATE_COMMAND = CREATE_COMMAND_WITHOUT_EXCLUDES + ('--exclude-from', 'excludes')
|
||||
|
||||
|
||||
def test_create_archive_should_call_attic_with_parameters():
|
||||
def test_create_archive_should_call_borg_with_parameters():
|
||||
insert_subprocess_mock(CREATE_COMMAND)
|
||||
insert_platform_mock()
|
||||
insert_datetime_mock()
|
||||
@@ -68,7 +68,7 @@ def test_create_archive_should_call_attic_with_parameters():
|
||||
storage_config={},
|
||||
source_directories='foo bar',
|
||||
repository='repo',
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
|
||||
@@ -83,11 +83,11 @@ def test_create_archive_with_two_spaces_in_source_directories():
|
||||
storage_config={},
|
||||
source_directories='foo bar',
|
||||
repository='repo',
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_none_excludes_filename_should_call_attic_without_excludes():
|
||||
def test_create_archive_with_none_excludes_filename_should_call_borg_without_excludes():
|
||||
insert_subprocess_mock(CREATE_COMMAND_WITHOUT_EXCLUDES)
|
||||
insert_platform_mock()
|
||||
insert_datetime_mock()
|
||||
@@ -98,11 +98,11 @@ def test_create_archive_with_none_excludes_filename_should_call_attic_without_ex
|
||||
storage_config={},
|
||||
source_directories='foo bar',
|
||||
repository='repo',
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_verbosity_some_should_call_attic_with_stats_parameter():
|
||||
def test_create_archive_with_verbosity_some_should_call_borg_with_stats_parameter():
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--stats',))
|
||||
insert_platform_mock()
|
||||
insert_datetime_mock()
|
||||
@@ -113,11 +113,11 @@ def test_create_archive_with_verbosity_some_should_call_attic_with_stats_paramet
|
||||
storage_config={},
|
||||
source_directories='foo bar',
|
||||
repository='repo',
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_verbosity_lots_should_call_attic_with_verbose_parameter():
|
||||
def test_create_archive_with_verbosity_lots_should_call_borg_with_verbose_parameter():
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--verbose', '--stats'))
|
||||
insert_platform_mock()
|
||||
insert_datetime_mock()
|
||||
@@ -128,11 +128,11 @@ def test_create_archive_with_verbosity_lots_should_call_attic_with_verbose_param
|
||||
storage_config={},
|
||||
source_directories='foo bar',
|
||||
repository='repo',
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_compression_should_call_attic_with_compression_parameters():
|
||||
def test_create_archive_with_compression_should_call_borg_with_compression_parameters():
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--compression', 'rle'))
|
||||
insert_platform_mock()
|
||||
insert_datetime_mock()
|
||||
@@ -143,11 +143,11 @@ def test_create_archive_with_compression_should_call_attic_with_compression_para
|
||||
storage_config={'compression': 'rle'},
|
||||
source_directories='foo bar',
|
||||
repository='repo',
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_one_file_system_should_call_attic_with_one_file_system_parameters():
|
||||
def test_create_archive_with_one_file_system_should_call_borg_with_one_file_system_parameters():
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--one-file-system',))
|
||||
insert_platform_mock()
|
||||
insert_datetime_mock()
|
||||
@@ -158,12 +158,28 @@ def test_create_archive_with_one_file_system_should_call_attic_with_one_file_sys
|
||||
storage_config={},
|
||||
source_directories='foo bar',
|
||||
repository='repo',
|
||||
command='attic',
|
||||
command='borg',
|
||||
one_file_system=True,
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_umask_should_call_attic_with_umask_parameters():
|
||||
def test_create_archive_with_remote_path_should_call_borg_with_remote_path_parameters():
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--remote-path', 'borg1'))
|
||||
insert_platform_mock()
|
||||
insert_datetime_mock()
|
||||
|
||||
module.create_archive(
|
||||
excludes_filename='excludes',
|
||||
verbosity=None,
|
||||
storage_config={},
|
||||
source_directories='foo bar',
|
||||
repository='repo',
|
||||
command='borg',
|
||||
remote_path='borg1',
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_umask_should_call_borg_with_umask_parameters():
|
||||
insert_subprocess_mock(CREATE_COMMAND + ('--umask', '740'))
|
||||
insert_platform_mock()
|
||||
insert_datetime_mock()
|
||||
@@ -174,12 +190,12 @@ def test_create_archive_with_umask_should_call_attic_with_umask_parameters():
|
||||
storage_config={'umask': 740},
|
||||
source_directories='foo bar',
|
||||
repository='repo',
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_source_directories_glob_expands():
|
||||
insert_subprocess_mock(('attic', 'create', 'repo::host-now', 'foo', 'food'))
|
||||
insert_subprocess_mock(('borg', 'create', 'repo::host-now', 'foo', 'food'))
|
||||
insert_platform_mock()
|
||||
insert_datetime_mock()
|
||||
flexmock(module).should_receive('glob').with_args('foo*').and_return(['foo', 'food'])
|
||||
@@ -190,12 +206,12 @@ def test_create_archive_with_source_directories_glob_expands():
|
||||
storage_config={},
|
||||
source_directories='foo*',
|
||||
repository='repo',
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_non_matching_source_directories_glob_passes_through():
|
||||
insert_subprocess_mock(('attic', 'create', 'repo::host-now', 'foo*'))
|
||||
insert_subprocess_mock(('borg', 'create', 'repo::host-now', 'foo*'))
|
||||
insert_platform_mock()
|
||||
insert_datetime_mock()
|
||||
flexmock(module).should_receive('glob').with_args('foo*').and_return([])
|
||||
@@ -206,12 +222,12 @@ def test_create_archive_with_non_matching_source_directories_glob_passes_through
|
||||
storage_config={},
|
||||
source_directories='foo*',
|
||||
repository='repo',
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
|
||||
def test_create_archive_with_glob_should_call_attic_with_expanded_directories():
|
||||
insert_subprocess_mock(('attic', 'create', 'repo::host-now', 'foo', 'food'))
|
||||
def test_create_archive_with_glob_should_call_borg_with_expanded_directories():
|
||||
insert_subprocess_mock(('borg', 'create', 'repo::host-now', 'foo', 'food'))
|
||||
insert_platform_mock()
|
||||
insert_datetime_mock()
|
||||
flexmock(module).should_receive('glob').with_args('foo*').and_return(['foo', 'food'])
|
||||
@@ -222,7 +238,7 @@ def test_create_archive_with_glob_should_call_attic_with_expanded_directories():
|
||||
storage_config={},
|
||||
source_directories='foo*',
|
||||
repository='repo',
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
|
||||
@@ -248,11 +264,11 @@ def test_make_prune_flags_should_return_flags_from_config():
|
||||
|
||||
|
||||
PRUNE_COMMAND = (
|
||||
'attic', 'prune', 'repo', '--keep-daily', '1', '--keep-weekly', '2', '--keep-monthly', '3',
|
||||
'borg', 'prune', 'repo', '--keep-daily', '1', '--keep-weekly', '2', '--keep-monthly', '3',
|
||||
)
|
||||
|
||||
|
||||
def test_prune_archives_should_call_attic_with_parameters():
|
||||
def test_prune_archives_should_call_borg_with_parameters():
|
||||
retention_config = flexmock()
|
||||
flexmock(module).should_receive('_make_prune_flags').with_args(retention_config).and_return(
|
||||
BASE_PRUNE_FLAGS,
|
||||
@@ -263,11 +279,11 @@ def test_prune_archives_should_call_attic_with_parameters():
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
retention_config=retention_config,
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
|
||||
def test_prune_archives_with_verbosity_some_should_call_attic_with_stats_parameter():
|
||||
def test_prune_archives_with_verbosity_some_should_call_borg_with_stats_parameter():
|
||||
retention_config = flexmock()
|
||||
flexmock(module).should_receive('_make_prune_flags').with_args(retention_config).and_return(
|
||||
BASE_PRUNE_FLAGS,
|
||||
@@ -278,11 +294,11 @@ def test_prune_archives_with_verbosity_some_should_call_attic_with_stats_paramet
|
||||
repository='repo',
|
||||
verbosity=VERBOSITY_SOME,
|
||||
retention_config=retention_config,
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
|
||||
def test_prune_archives_with_verbosity_lots_should_call_attic_with_verbose_parameter():
|
||||
def test_prune_archives_with_verbosity_lots_should_call_borg_with_verbose_parameter():
|
||||
retention_config = flexmock()
|
||||
flexmock(module).should_receive('_make_prune_flags').with_args(retention_config).and_return(
|
||||
BASE_PRUNE_FLAGS,
|
||||
@@ -293,7 +309,22 @@ def test_prune_archives_with_verbosity_lots_should_call_attic_with_verbose_param
|
||||
repository='repo',
|
||||
verbosity=VERBOSITY_LOTS,
|
||||
retention_config=retention_config,
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
def test_prune_archive_with_remote_path_should_call_borg_with_remote_path_parameters():
|
||||
retention_config = flexmock()
|
||||
flexmock(module).should_receive('_make_prune_flags').with_args(retention_config).and_return(
|
||||
BASE_PRUNE_FLAGS,
|
||||
)
|
||||
insert_subprocess_mock(PRUNE_COMMAND + ('--remote-path', 'borg1'))
|
||||
|
||||
module.prune_archives(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
retention_config=retention_config,
|
||||
command='borg',
|
||||
remote_path='borg1',
|
||||
)
|
||||
|
||||
|
||||
@@ -345,7 +376,7 @@ def test_make_check_flags_with_last_returns_last_flag():
|
||||
assert flags == ('--last', 3)
|
||||
|
||||
|
||||
def test_check_archives_should_call_attic_with_parameters():
|
||||
def test_check_archives_should_call_borg_with_parameters():
|
||||
checks = flexmock()
|
||||
check_last = flexmock()
|
||||
consistency_config = flexmock().should_receive('get').and_return(check_last).mock
|
||||
@@ -353,7 +384,7 @@ def test_check_archives_should_call_attic_with_parameters():
|
||||
flexmock(module).should_receive('_make_check_flags').with_args(checks, check_last).and_return(())
|
||||
stdout = flexmock()
|
||||
insert_subprocess_mock(
|
||||
('attic', 'check', 'repo'),
|
||||
('borg', 'check', 'repo'),
|
||||
stdout=stdout, stderr=STDOUT,
|
||||
)
|
||||
insert_platform_mock()
|
||||
@@ -365,16 +396,16 @@ def test_check_archives_should_call_attic_with_parameters():
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
consistency_config=consistency_config,
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
|
||||
def test_check_archives_with_verbosity_some_should_call_attic_with_verbose_parameter():
|
||||
def test_check_archives_with_verbosity_some_should_call_borg_with_verbose_parameter():
|
||||
consistency_config = flexmock().should_receive('get').and_return(None).mock
|
||||
flexmock(module).should_receive('_parse_checks').and_return(flexmock())
|
||||
flexmock(module).should_receive('_make_check_flags').and_return(())
|
||||
insert_subprocess_mock(
|
||||
('attic', 'check', 'repo', '--verbose'),
|
||||
('borg', 'check', 'repo', '--verbose'),
|
||||
stdout=None, stderr=STDOUT,
|
||||
)
|
||||
insert_platform_mock()
|
||||
@@ -384,16 +415,16 @@ def test_check_archives_with_verbosity_some_should_call_attic_with_verbose_param
|
||||
verbosity=VERBOSITY_SOME,
|
||||
repository='repo',
|
||||
consistency_config=consistency_config,
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
|
||||
def test_check_archives_with_verbosity_lots_should_call_attic_with_verbose_parameter():
|
||||
def test_check_archives_with_verbosity_lots_should_call_borg_with_verbose_parameter():
|
||||
consistency_config = flexmock().should_receive('get').and_return(None).mock
|
||||
flexmock(module).should_receive('_parse_checks').and_return(flexmock())
|
||||
flexmock(module).should_receive('_make_check_flags').and_return(())
|
||||
insert_subprocess_mock(
|
||||
('attic', 'check', 'repo', '--verbose'),
|
||||
('borg', 'check', 'repo', '--verbose'),
|
||||
stdout=None, stderr=STDOUT,
|
||||
)
|
||||
insert_platform_mock()
|
||||
@@ -403,7 +434,7 @@ def test_check_archives_with_verbosity_lots_should_call_attic_with_verbose_param
|
||||
verbosity=VERBOSITY_LOTS,
|
||||
repository='repo',
|
||||
consistency_config=consistency_config,
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
|
||||
@@ -416,5 +447,30 @@ def test_check_archives_without_any_checks_should_bail():
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
consistency_config=consistency_config,
|
||||
command='attic',
|
||||
command='borg',
|
||||
)
|
||||
|
||||
|
||||
def test_check_archives_with_remote_path_should_call_borg_with_remote_path_parameters():
|
||||
checks = flexmock()
|
||||
check_last = flexmock()
|
||||
consistency_config = flexmock().should_receive('get').and_return(check_last).mock
|
||||
flexmock(module).should_receive('_parse_checks').and_return(checks)
|
||||
flexmock(module).should_receive('_make_check_flags').with_args(checks, check_last).and_return(())
|
||||
stdout = flexmock()
|
||||
insert_subprocess_mock(
|
||||
('borg', 'check', 'repo', '--remote-path', 'borg1'),
|
||||
stdout=stdout, stderr=STDOUT,
|
||||
)
|
||||
insert_platform_mock()
|
||||
insert_datetime_mock()
|
||||
builtins_mock().should_receive('open').and_return(stdout)
|
||||
flexmock(module.os).should_receive('devnull')
|
||||
|
||||
module.check_archives(
|
||||
verbosity=None,
|
||||
repository='repo',
|
||||
consistency_config=consistency_config,
|
||||
command='borg',
|
||||
remote_path='borg1',
|
||||
)
|
||||
@@ -3,7 +3,7 @@ from collections import OrderedDict
|
||||
from flexmock import flexmock
|
||||
import pytest
|
||||
|
||||
from atticmatic import config as module
|
||||
from borgmatic import config as module
|
||||
|
||||
|
||||
def test_option_should_create_config_option():
|
||||
@@ -1,3 +0,0 @@
|
||||
# You can drop this file into /etc/cron.d/ to run atticmatic nightly.
|
||||
|
||||
0 3 * * * root PATH=$PATH:/usr/local/bin /usr/local/bin/atticmatic
|
||||
+15
-10
@@ -3,27 +3,30 @@
|
||||
# Globs are expanded.
|
||||
source_directories: /home /etc /var/log/syslog*
|
||||
|
||||
# For Borg only, you can specify to stay in same file system (do not cross
|
||||
# mount points).
|
||||
# Stay in same file system (do not cross mount points).
|
||||
#one_file_system: True
|
||||
|
||||
# Alternate Borg remote executable (defaults to "borg"):
|
||||
#remote_path: borg1
|
||||
|
||||
# Path to local or remote repository.
|
||||
repository: user@backupserver:sourcehostname.attic
|
||||
repository: user@backupserver:sourcehostname.borg
|
||||
|
||||
[storage]
|
||||
# Passphrase to unlock the encryption key with. Only use on repositories that
|
||||
# were initialized with passphrase/repokey encryption.
|
||||
#encryption_passphrase: foo
|
||||
# For Borg only, you can specify the type of compression to use when creating
|
||||
# archives. See https://borgbackup.readthedocs.org/en/stable/usage.html#borg-create
|
||||
|
||||
# Type of compression to use when creating archives. See
|
||||
# https://borgbackup.readthedocs.org/en/stable/usage.html#borg-create
|
||||
# for details. Defaults to no compression.
|
||||
#compression: lz4
|
||||
# For Borg only, you can specify the umask to be used for borg create.
|
||||
|
||||
# Umask to be used for borg create.
|
||||
#umask: 0740
|
||||
|
||||
[retention]
|
||||
# Retention policy for how many backups to keep in each category. See
|
||||
# https://attic-backup.org/usage.html#attic-prune or
|
||||
# https://borgbackup.readthedocs.org/en/stable/usage.html#borg-prune for details.
|
||||
#keep_within: 3H
|
||||
#keep_hourly: 24
|
||||
@@ -31,13 +34,15 @@ keep_daily: 7
|
||||
keep_weekly: 4
|
||||
keep_monthly: 6
|
||||
keep_yearly: 1
|
||||
|
||||
#prefix: sourcehostname
|
||||
|
||||
[consistency]
|
||||
# Space-separated list of consistency checks to run: "repository", "archives",
|
||||
# or both. Defaults to both. Set to "disabled" to disable all consistency
|
||||
# checks. See https://attic-backup.org/usage.html#attic-check or
|
||||
# https://borgbackup.readthedocs.org/en/stable/usage.html#borg-check for details.
|
||||
# checks. See https://borgbackup.readthedocs.org/en/stable/usage.html#borg-check
|
||||
# for details.
|
||||
checks: repository archives
|
||||
# For Borg only, you can restrict the number of checked archives to the last n.
|
||||
|
||||
# Restrict the number of checked archives to the last n.
|
||||
#check_last: 3
|
||||
|
||||
@@ -1,2 +1,5 @@
|
||||
[metadata]
|
||||
description-file=README.md
|
||||
|
||||
[bdist_wheel]
|
||||
universal=1
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
|
||||
VERSION = '0.1.8'
|
||||
VERSION = '1.0.2'
|
||||
|
||||
|
||||
setup(
|
||||
name='atticmatic',
|
||||
name='borgmatic',
|
||||
version=VERSION,
|
||||
description='A wrapper script for Attic/Borg backup software that creates and prunes backups',
|
||||
description='A wrapper script for Borg backup software that creates and prunes backups',
|
||||
author='Dan Helfman',
|
||||
author_email='witten@torsion.org',
|
||||
url='https://torsion.org/atticmatic',
|
||||
download_url='https://torsion.org/hg/atticmatic/archive/%s.tar.gz' % VERSION,
|
||||
url='https://torsion.org/borgmatic',
|
||||
download_url='https://torsion.org/hg/borgmatic/archive/%s.tar.gz' % VERSION,
|
||||
classifiers=(
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Environment :: Console',
|
||||
@@ -24,10 +24,12 @@ setup(
|
||||
packages=find_packages(),
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'atticmatic = atticmatic.command:main',
|
||||
'borgmatic = atticmatic.command:main',
|
||||
'borgmatic = borgmatic.command:main',
|
||||
]
|
||||
},
|
||||
obsoletes=[
|
||||
'atticmatic',
|
||||
],
|
||||
tests_require=(
|
||||
'flexmock',
|
||||
'pytest',
|
||||
|
||||
Reference in New Issue
Block a user