After pruning, run attic's consistency checks on all archives.
This commit is contained in:
parent
6d4e40a819
commit
e3fa18b892
5 changed files with 64 additions and 11 deletions
3
NEWS
3
NEWS
|
@ -1,5 +1,6 @@
|
||||||
default
|
0.0.3
|
||||||
|
|
||||||
|
* After pruning, run attic's consistency checks on all archives.
|
||||||
* Integration tests for argument parsing.
|
* Integration tests for argument parsing.
|
||||||
* Documentation updates about repository encryption.
|
* Documentation updates about repository encryption.
|
||||||
|
|
||||||
|
|
13
README.md
13
README.md
|
@ -5,10 +5,11 @@ save_as: atticmatic/index.html
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
atticmatic is a simple Python wrapper script for the [Attic backup
|
atticmatic is a simple Python wrapper script for the [Attic backup
|
||||||
software](https://attic-backup.org/) that initiates a backup and prunes any
|
software](https://attic-backup.org/) that initiates a backup, prunes any old
|
||||||
old backups according to a retention policy. The script supports specifying
|
backups according to a retention policy, and validates backups for
|
||||||
your settings in a declarative configuration file rather than having to put
|
consistency. The script supports specifying your settings in a declarative
|
||||||
them all on the command-line, and handles common errors.
|
configuration file rather than having to put them all on the command-line, and
|
||||||
|
handles common errors.
|
||||||
|
|
||||||
Here's an example config file:
|
Here's an example config file:
|
||||||
|
|
||||||
|
@ -68,7 +69,9 @@ arguments:
|
||||||
|
|
||||||
atticmatic
|
atticmatic
|
||||||
|
|
||||||
This will also prune any old backups as per the configured retention policy.
|
This will also prune any old backups as per the configured retention policy,
|
||||||
|
and check backups for consistency problems due to things like file damage.
|
||||||
|
|
||||||
By default, the backup will proceed silently except in the case of errors. But
|
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
|
if you'd like to to get additional information about the progress of the
|
||||||
backup as it proceeds, use the verbose option instead:
|
backup as it proceeds, use the verbose option instead:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
import os
|
||||||
import platform
|
import platform
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
|
@ -63,3 +63,19 @@ def prune_archives(verbose, repository, retention_config):
|
||||||
) + (('--verbose',) if verbose else ())
|
) + (('--verbose',) if verbose else ())
|
||||||
|
|
||||||
subprocess.check_call(command)
|
subprocess.check_call(command)
|
||||||
|
|
||||||
|
|
||||||
|
def check_archives(verbose, repository):
|
||||||
|
'''
|
||||||
|
Given a verbosity flag and a local or remote repository path, check the contained attic archives
|
||||||
|
for consistency.
|
||||||
|
'''
|
||||||
|
command = (
|
||||||
|
'attic', 'check',
|
||||||
|
repository,
|
||||||
|
) + (('--verbose',) if verbose else ())
|
||||||
|
|
||||||
|
# Attic's check command spews to stdout even without the verbose flag. Suppress it.
|
||||||
|
stdout = None if verbose else open(os.devnull, 'w')
|
||||||
|
|
||||||
|
subprocess.check_call(command, stdout=stdout)
|
||||||
|
|
|
@ -3,7 +3,7 @@ from argparse import ArgumentParser
|
||||||
from subprocess import CalledProcessError
|
from subprocess import CalledProcessError
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from atticmatic.attic import create_archive, prune_archives
|
from atticmatic.attic import check_archives, create_archive, prune_archives
|
||||||
from atticmatic.config import parse_configuration
|
from atticmatic.config import parse_configuration
|
||||||
|
|
||||||
|
|
||||||
|
@ -41,9 +41,11 @@ def main():
|
||||||
try:
|
try:
|
||||||
args = parse_arguments(*sys.argv[1:])
|
args = parse_arguments(*sys.argv[1:])
|
||||||
location_config, retention_config = parse_configuration(args.config_filename)
|
location_config, retention_config = parse_configuration(args.config_filename)
|
||||||
|
repository = location_config['repository']
|
||||||
|
|
||||||
create_archive(args.excludes_filename, args.verbose, **location_config)
|
create_archive(args.excludes_filename, args.verbose, **location_config)
|
||||||
prune_archives(args.verbose, location_config['repository'], retention_config)
|
prune_archives(args.verbose, repository, retention_config)
|
||||||
|
check_archives(args.verbose, repository)
|
||||||
except (ValueError, IOError, CalledProcessError) as error:
|
except (ValueError, IOError, CalledProcessError) as error:
|
||||||
print(error, file=sys.stderr)
|
print(error, file=sys.stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
|
@ -5,9 +5,9 @@ from flexmock import flexmock
|
||||||
from atticmatic import attic as module
|
from atticmatic import attic as module
|
||||||
|
|
||||||
|
|
||||||
def insert_subprocess_mock(check_call_command):
|
def insert_subprocess_mock(check_call_command, **kwargs):
|
||||||
subprocess = flexmock()
|
subprocess = flexmock()
|
||||||
subprocess.should_receive('check_call').with_args(check_call_command).once()
|
subprocess.should_receive('check_call').with_args(check_call_command, **kwargs).once()
|
||||||
flexmock(module).subprocess = subprocess
|
flexmock(module).subprocess = subprocess
|
||||||
|
|
||||||
|
|
||||||
|
@ -111,3 +111,34 @@ def test_prune_archives_with_verbose_should_call_attic_with_verbose_parameters()
|
||||||
verbose=True,
|
verbose=True,
|
||||||
retention_config=retention_config,
|
retention_config=retention_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_archives_should_call_attic_with_parameters():
|
||||||
|
stdout = flexmock()
|
||||||
|
insert_subprocess_mock(
|
||||||
|
('attic', 'check', 'repo'),
|
||||||
|
stdout=stdout,
|
||||||
|
)
|
||||||
|
insert_platform_mock()
|
||||||
|
insert_datetime_mock()
|
||||||
|
flexmock(module).open = lambda filename, mode: stdout
|
||||||
|
flexmock(module).os = flexmock().should_receive('devnull').mock
|
||||||
|
|
||||||
|
module.check_archives(
|
||||||
|
verbose=False,
|
||||||
|
repository='repo',
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_check_archives_with_verbose_should_call_attic_with_verbose_parameters():
|
||||||
|
insert_subprocess_mock(
|
||||||
|
('attic', 'check', 'repo', '--verbose'),
|
||||||
|
stdout=None,
|
||||||
|
)
|
||||||
|
insert_platform_mock()
|
||||||
|
insert_datetime_mock()
|
||||||
|
|
||||||
|
module.check_archives(
|
||||||
|
verbose=True,
|
||||||
|
repository='repo',
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in a new issue