diff --git a/app/__main__.py b/app/__main__.py index 535d616..a98dd9c 100644 --- a/app/__main__.py +++ b/app/__main__.py @@ -7,19 +7,32 @@ from specs import parse_specs_file def except_hook(ext_type, value, traceback): sys.stderr.write("{}: {}\n".format(ext_type.__name__, value)) + sys.excepthook = except_hook # Define parser parser = argparse.ArgumentParser( - description='Backup directories and Docker volumes.') -parser.add_argument('-f', '--file', action='append', dest='file', - help='File containing spec definitions.') -parser.add_argument('-j', '--json', action='store_const', const=True, - default=False, help='Print out the parsed specs as JSON ' - 'and exit') -parser.add_argument('spec', nargs='*', - help='The specs to process. Defaults to all.') + description="Backup directories and Docker volumes." +) +parser.add_argument( + "-f", + "--file", + action="append", + dest="file", + help="File containing spec definitions.", +) +parser.add_argument( + "-j", + "--json", + action="store_const", + const=True, + default=False, + help="Print out the parsed specs as JSON " "and exit", +) +parser.add_argument( + "spec", nargs="*", help="The specs to process. Defaults to all." +) # Parse arguments args = parser.parse_args() @@ -32,6 +45,7 @@ if args.spec: # Dump parsed data as json if args.json: import json + print(json.dumps([spec.to_dict() for spec in specs], indent=4)) else: diff --git a/app/specs/parser.py b/app/specs/parser.py index fab362a..60d0011 100644 --- a/app/specs/parser.py +++ b/app/specs/parser.py @@ -37,10 +37,7 @@ def parse_specs_file(path: Path) -> List[Spec]: "destination": None, "limit": None, "volume": False, - "notify": { - "title": "Backup Notification", - "events": ["failure"] - } + "notify": {"title": "Backup Notification", "events": ["failure"]}, } # Read YAML file @@ -58,9 +55,11 @@ def parse_specs_file(path: Path) -> List[Spec]: specs = [] # Check format for each spec for key in data["specs"]: - specs.append(Spec.from_dict(key, combine_with_skeleton( - data["specs"][key], spec_skel) - )) + specs.append( + Spec.from_dict( + key, combine_with_skeleton(data["specs"][key], spec_skel) + ) + ) return specs @@ -95,20 +94,3 @@ def combine_with_skeleton(data: Dict, skel: Dict) -> Dict: data[key] = combine_with_skeleton(data[key], value) return data - - -# Test cases -if __name__ == "__main__": - d1 = { - "a": 5 - } - s1 = { - "a": 7, - "b": 2 - } - r1 = { - "a": 5, - "b": 2 - } - - assert combine_with_skeleton(d1, s1) == r1 diff --git a/app/specs/specs.py b/app/specs/specs.py index d76f9bb..d0dd8f1 100644 --- a/app/specs/specs.py +++ b/app/specs/specs.py @@ -1,4 +1,3 @@ -from pathlib import Path from datetime import datetime import requests import os @@ -9,32 +8,32 @@ class Spec: __SKELETON = {} def __init__(self, name, destination, limit, title, events=None): - self.name = name + self.name = name self.destination = Path(destination) - self.limit = limit - self.title = title - self.events = [] if events is None else events + self.limit = limit + self.title = title + self.events = [] if events is None else events def to_dict(self): return { "name": self.name, "destination": str(self.destination), "limit": self.limit, - "notify": { - "title": self.title, - "events": self.events - } + "notify": {"title": self.title, "events": self.events}, } def backup(self): raise NotImplementedError() def remove_redundant(self): - tarballs = sorted(self.destination.glob('*.tar.gz'), - key=os.path.getmtime, reverse=True) + tarballs = sorted( + self.destination.glob("*.tar.gz"), + key=os.path.getmtime, + reverse=True, + ) if len(tarballs) >= self.limit: - for path in tarballs[self.limit - 1:]: + for path in tarballs[self.limit - 1 :]: path.unlink() def notify(self, status_code): @@ -59,21 +58,16 @@ class Spec: return url = "https://maker.ifttt.com/trigger/{}/with/key/{}".format( - "phone_notifications", - key + "phone_notifications", key ) - data = { - "value1": self.title, - "value2": message - } + data = {"value1": self.title, "value2": message} requests.post(url, data=data) def get_filename(self): - return '{}_{}.tar.gz'.format( - self.name, - datetime.now().strftime('%Y-%m-%d_%H-%M-%S') + return "{}_{}.tar.gz".format( + self.name, datetime.now().strftime("%Y-%m-%d_%H-%M-%S") ) @staticmethod @@ -85,11 +79,12 @@ class Spec: @staticmethod def from_file(path: str): - with open(path, 'r') as yaml_file: + with open(path, "r") as yaml_file: data = yaml.load(yaml_file, Loader=yaml.Loader) - return [Spec.from_dict(name, info) - for name, info in data["specs"].items()] + return [ + Spec.from_dict(name, info) for name, info in data["specs"].items() + ] class DirSpec(Spec): @@ -103,8 +98,7 @@ class DirSpec(Spec): status_code = os.system( "tar -C '{}' -czf '{}' -- .".format( - self.source, - self.destination / self.get_filename() + self.source, self.destination / self.get_filename() ) ) @@ -118,9 +112,10 @@ class DirSpec(Spec): data["destination"], data["limit"], data["notify"]["title"], - data["notify"]["events"] + data["notify"]["events"], ) + class VolumeSpec(Spec): def __init__(self, name, volume, destination, limit, title, events=None): super().__init__(name, destination, limit, title, events) @@ -131,9 +126,7 @@ class VolumeSpec(Spec): status_code = os.system( "docker run --rm -v '{}:/from' -v '{}:/to' alpine:latest " "tar -C /from -czf '/to/{}' -- .".format( - self.volume, - self.destination, - self.get_filename() + self.volume, self.destination, self.get_filename() ) ) @@ -145,5 +138,5 @@ class VolumeSpec(Spec): data["destination"], data["limit"], data["notify"]["title"], - data["notify"]["events"] + data["notify"]["events"], )