浏览代码

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

Dan Helfman 10 年之前
父节点
当前提交
e3fa18b892
共有 5 个文件被更改,包括 64 次插入11 次删除
  1. 2 1
      NEWS
  2. 8 5
      README.md
  3. 17 1
      atticmatic/attic.py
  4. 4 2
      atticmatic/command.py
  5. 33 2
      atticmatic/tests/unit/test_attic.py

+ 2 - 1
NEWS

@@ -1,5 +1,6 @@
-default
+0.0.3
 
+ * After pruning, run attic's consistency checks on all archives.
  * Integration tests for argument parsing.
  * Documentation updates about repository encryption.
 

+ 8 - 5
README.md

@@ -5,10 +5,11 @@ save_as: atticmatic/index.html
 ## Overview
 
 atticmatic is a simple Python wrapper script for the [Attic backup
-software](https://attic-backup.org/) that initiates a backup and prunes any
-old backups according to a retention policy. The script supports specifying
-your settings in a declarative configuration file rather than having to put
-them all on the command-line, and handles common errors.
+software](https://attic-backup.org/) that initiates a backup, prunes any old
+backups according to a retention policy, and validates backups for
+consistency. The script supports specifying your settings in a declarative
+configuration file rather than having to put them all on the command-line, and
+handles common errors.
 
 Here's an example config file:
 
@@ -68,7 +69,9 @@ arguments:
 
     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
 if you'd like to to get additional information about the progress of the
 backup as it proceeds, use the verbose option instead:

+ 17 - 1
atticmatic/attic.py

@@ -1,5 +1,5 @@
 from datetime import datetime
-
+import os
 import platform
 import subprocess
 
@@ -63,3 +63,19 @@ def prune_archives(verbose, repository, retention_config):
     ) + (('--verbose',) if verbose else ())
 
     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)

+ 4 - 2
atticmatic/command.py

@@ -3,7 +3,7 @@ from argparse import ArgumentParser
 from subprocess import CalledProcessError
 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
 
 
@@ -41,9 +41,11 @@ def main():
     try:
         args = parse_arguments(*sys.argv[1:])
         location_config, retention_config = parse_configuration(args.config_filename)
+        repository = location_config['repository']
 
         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:
         print(error, file=sys.stderr)
         sys.exit(1)

+ 33 - 2
atticmatic/tests/unit/test_attic.py

@@ -5,9 +5,9 @@ from flexmock import flexmock
 from atticmatic import attic as module
 
 
-def insert_subprocess_mock(check_call_command):
+def insert_subprocess_mock(check_call_command, **kwargs):
     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
 
 
@@ -111,3 +111,34 @@ def test_prune_archives_with_verbose_should_call_attic_with_verbose_parameters()
         verbose=True,
         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',
+    )