Browse Source

_duplicity(): throw DuplicityCommandFailedError when duplicity command failed

Fabian Peter Hammerle 7 years ago
parent
commit
76fb7d5360

+ 22 - 6
duplitab/__init__.py

@@ -5,10 +5,23 @@ import sys
 import time
 
 
-def _duplicity(params):
-    stdout = subprocess.check_output(
-        ['duplicity'] + params,
-    )
+class DuplicityCommandFailedError(RuntimeError):
+
+    @property
+    def msg(self):
+        return self.__cause__.output.decode(sys.stdout.encoding)
+
+
+def _duplicity(params, timeout_seconds=None):
+    try:
+        stdout = subprocess.check_output(
+            ['duplicity']
+            + (['--timeout', str(timeout_seconds)] if timeout_seconds else [])
+            + params,
+            stderr=subprocess.STDOUT,
+        )
+    except subprocess.CalledProcessError as ex:
+        raise DuplicityCommandFailedError() from ex
     return stdout.decode(sys.stdout.encoding)
 
 
@@ -23,9 +36,12 @@ class Collection(object):
     def __init__(self, url):
         self.url = url
 
-    def request_status(self):
+    def request_status(self, timeout_seconds=None):
         return _CollectionStatus._parse(
-            text=_duplicity(['collection-status', self.url])
+            text=_duplicity(
+                ['collection-status', self.url],
+                timeout_seconds=timeout_seconds,
+            )
         )
 
 

+ 10 - 3
scripts/duplitab

@@ -237,11 +237,18 @@ def run(command, config_path, quiet, duplicity_verbosity,
         table = []
         for backup_attr in filtered_config:
             collection = duplitab.Collection(url = backup_attr['target_url'])
-            status = collection.request_status()
+            if not quiet:
+                sys.stderr.write('request status of collection {!r}\n'.format(collection.url))
+            try:
+                status = collection.request_status()
+            except duplitab.DuplicityCommandFailedError as ex:
+                status = None
+                if not quiet:
+                    sys.stderr.write(ex.msg)
             table.append([
                 collection.url,
-                status.last_full_backup_time or 'never',
-                status.last_incremental_backup_time or 'never',
+                (status.last_full_backup_time or 'never') if status else None,
+                (status.last_incremental_backup_time or 'never') if status else None,
                 ])
         print(tabulate.tabulate(
             table,

+ 1 - 0
tests/data/collections/broken-symlink

@@ -0,0 +1 @@
+/duplitab-test-non-existing

+ 10 - 0
tests/test_collection.py

@@ -106,6 +106,16 @@ def test_collection_request_status(url, expected_status_attr):
         assert value == getattr(s, name)
 
 
+@pytest.mark.parametrize(('url'), [
+    'file://{}'.format(os.path.join(test_collections_dir_path, 'broken-symlink')),
+    'file://{}'.format(os.path.join(test_collections_dir_path, 'broken-symlink', 'backup')),
+])
+def test_collection_request_status_fail(url):
+    c = duplitab.Collection(url=url)
+    with pytest.raises(duplitab.DuplicityCommandFailedError):
+        c.request_status()
+
+
 @pytest.mark.parametrize(('chain_status', 'expected_time'), [
     [
         duplitab._ChainStatus(

+ 9 - 0
tests/test_duplicity.py

@@ -0,0 +1,9 @@
+import pytest
+
+import duplitab
+
+
+def test_command_failure_msg():
+    with pytest.raises(duplitab.DuplicityCommandFailedError) as exinfo:
+        duplitab._duplicity(['--unsupported-duplitab-test-param'])
+    assert '--unsupported-duplitab-test-param' in exinfo.value.msg