After pruning, run attic's consistency checks on all archives.

This commit is contained in:
Dan Helfman 2015-02-14 09:23:40 -08:00
parent 6d4e40a819
commit e3fa18b892
5 changed files with 64 additions and 11 deletions

3
NEWS
View File

@ -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.

View File

@ -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:

View File

@ -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)

View File

@ -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)

View File

@ -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',
)