Browse Source

Add dependency tree logic

Nathaniel van Diepen 4 years ago
parent
commit
2083750735
1 changed files with 86 additions and 12 deletions
  1. 86 12
      backup.py

+ 86 - 12
backup.py

@@ -1,17 +1,91 @@
+import contextlib
 import os
-from glob import glob
 import yaml
+import sys
+
+from glob import glob
+
[email protected]
+def pushd(newDir):
+    previousDir = os.getcwd()
+    os.chdir(newDir)
+    try:
+        yield
+
+    finally:
+        os.chdir(previousDir)
+
+class DependencyException(Exception):
+    pass
+
+def deptree(sources, deps=None):
+    if deps is None:
+        deps = []
+
+    deferred = []
+    for name in sources.keys():
+        source = sources[name]
+        if "depends" not in source:
+            deps.append(name)
+
+        else:
+            deferred.append(name)
+
+    while deferred:
+        name = deferred.pop()
+        depends = sources[name]["depends"]
+        # todo - detect dependency loop
+        if name in depends:
+            raise DependencyException('Source {} depends upon itself'.format(name))
+
+        elif set(depends).issubset(set(deps)):
+            deps.append(name)
+
+        elif not set(depends).issubset(set(deps)):
+            missing = ', '.join(set(depends).difference(set(deps)))
+            raise DependencyException(
+                    'Source {0} has missing dependencies: {1}'.format(name, missing))
+
+        else:
+            deferred.append(name)
+
+
+    return deps
+
+def main(args):
+    with pushd('etc/backup.d'):
+        with open("backup.yml") as f:
+            config = yaml.load(f)
+
+        sources = {}
+        for source in config['sources']:
+            source = os.path.realpath(source)
+            for path in glob('{}/*.yml'.format(source)):
+                path = os.path.realpath(path)
+                with pushd(os.path.dirname(path)), open(path) as f:
+                    data = yaml.load(f)
+                    if "depends" in data:
+                        for i in range(0, len(data["depends"])):
+                            data["depends"][i] = os.path.realpath('{}.yml'.format(data["depends"][i]))
+
+                    sources[path] = data
+
+    config['sources'] = sources
+    import json
+    for name in deptree(config["sources"]):
+        source = config["sources"][name]
+        print(json.dumps(source, indent=2))
 
-with open("etc/backup.d/backup.yml") as f:
-    config = yaml.load(f)
+if __name__ == '__main__':
+    try:
+        main(sys.argv[1:])
 
-sources = {}
-for source in config['sources']:
-    for path in glob('etc/backup.d/{}/*.yml'.format(source)):
-        with open(path) as f:
-            if source not in sources:
-                sources[source] = {}
-            sources[source][os.path.basename(path)] = yaml.load(f)
+    except DependencyException as ex:
+        print(ex)
+        sys.exit(1)
 
-config['sources'] = sources
-print(config)
+    except Exception:
+        from traceback import format_exc
+        msg = "Error encountered:\n" + format_exc().strip()
+        print(msg)
+        sys.exit(1)