diff options
author | Daniel Silverstone <daniel.silverstone@codethink.co.uk> | 2012-10-01 16:06:27 +0100 |
---|---|---|
committer | Daniel Silverstone <daniel.silverstone@codethink.co.uk> | 2012-10-01 16:06:27 +0100 |
commit | c1b4d19515b86d3216a6db3cebaf24ff16b2a0bb (patch) | |
tree | f1054bca5961243efecd4871d6101a5d0edc6c2e | |
parent | 6cf00a01a1bc271e281d7999f76ff8e3ab067509 (diff) | |
download | lorry-controller-c1b4d19515b86d3216a6db3cebaf24ff16b2a0bb.tar.gz |
Add initial configuration parser and ignores
-rw-r--r-- | .gitignore | 1 | ||||
-rwxr-xr-x | lorry-controller | 8 | ||||
-rw-r--r-- | lorrycontroller/__init__.py | 4 | ||||
-rw-r--r-- | lorrycontroller/confparser.py | 111 |
4 files changed, 124 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/lorry-controller b/lorry-controller index 1ead6c7..1b2fb74 100755 --- a/lorry-controller +++ b/lorry-controller @@ -11,6 +11,7 @@ defaults = { 'work-area': '/home/lorry/controller-area' } +from lorrycontroller.confparser import LorryControllerConfig class LorryController(cliapp.Application): @@ -39,6 +40,13 @@ class LorryController(cliapp.Application): self.rungit(['reset', '--hard', 'origin/master']) self.rungit(['clean', '-fdx']) + if not os.path.exists('git/lorry-controller.conf'): + logging.error("Unable to find lorry-controller.conf in git") + raise SystemExit(4) + + conf = LorryControllerConfig(self.settings, + 'git/lorry-controller.conf') + def rungit(self, args): self.runcmd(['git']+args, cwd=os.path.join(self.settings['work-area'], diff --git a/lorrycontroller/__init__.py b/lorrycontroller/__init__.py new file mode 100644 index 0000000..3c29d56 --- /dev/null +++ b/lorrycontroller/__init__.py @@ -0,0 +1,4 @@ +# Copyright (C) 2012 Codethink Limited +# + +import confparser diff --git a/lorrycontroller/confparser.py b/lorrycontroller/confparser.py new file mode 100644 index 0000000..ebe1316 --- /dev/null +++ b/lorrycontroller/confparser.py @@ -0,0 +1,111 @@ +# Copyright (C) 2012 Codethink Limited +# + +import json +import logging +import re +import glob +import os + +default_values = [ + ( u'serial', 0 ), + ( u'create', u'never' ), + ( u'destroy', u'never' ), + ( u'interval', u'1m' ), + ( u'stagger', False ), + ( u'type', u'invalid_type' ), +] + +valid_whens = set(["always", "never", "unchanged"]) +valid_interval = re.compile(r"^([1-9][0-9]*)([mhd])?$") +interval_mults = { + None: 1, + 'm': 60, + 'h': 60 * 60, + 'd': 60 * 60 * 24, +} +class LorryControllerConfig(object): + '''This encapsulates the configuration for lorry-controller.''' + + def __init__(self, settings, confpath): + self.settings = settings + confpath = os.path.join(settings['work-area'], confpath) + logging.debug("Parsing configuration: %s" % confpath) + with open(confpath, "r") as fh: + self._raw_conf = json.load(fh) + logging.debug("Validating configuration semantics") + self._validate__raw_conf() + logging.debug("Configuration loaded") + + def _validate__raw_conf(self): + '''Validate the entire raw config.''' + if type(self._raw_conf) != list: + self._give_up("Configuration was not a list.") + for entry in self._raw_conf: + if type(entry) != dict: + self._give_up("Configuration entry was not a dict.") + if type(entry.get('type', None)) != unicode: + self._give_up("Configuration entry lacked a suitable 'type' " + "field.") + # Set the defaults + for key, defval in default_values: + entry[key] = entry.get(key, defval) + # And validate the generic values such as serial + self._validate__generics(entry) + # Now validate the rest + validator = getattr(self, '_validate_' + entry['type'], None) + if validator is None: + self._give_up("Configuration entry had unknown type: %s" % + entry['type']) + validator(entry) + + def _validate__generics(self, entry): + '''Validate the generic entries such as 'serial'.''' + for key, defval in default_values: + if type(defval) != type(entry[key]): + self._give_up("Invalid type for '%s': %r" % (key, entry[key])) + self._validate__when(entry, 'create') + if entry['create'] == "unchanged": + self._give_up("Invalid value for create: unchanged") + self._validate__when(entry, 'destroy') + entry['interval-parsed'] = self._parse_interval(entry['interval']) + + def _validate__when(self, entry, key): + if entry[key] not in valid_whens: + self._give_up("Invalid value for %s: %s" % (key, entry[key])) + + def _parse_interval(self, interval): + m = valid_interval.match(interval.lower()) + if m is None: + self._give_up("Unable to parse '%s' as an interval" % interval) + num, mult = m.groups() + num = int(num) + mult = interval_mults.get(mult, None) + if mult is None: + self._give_up("Somehow, '%s' managed to appear as a multiplier!" % + m.group(2)) + logging.debug("Converted interval %r to %r", interval, (num * mult)) + return num * mult + + def _validate_lorries(self, entry): + '''Validate a 'lorries' stanza.''' + if type(entry.get('globs', None)) != list: + self._give_up("Lorries stanzas need lists for their 'globs'") + all_lorries = set() + git_base = os.path.join(self.settings['work-area'], 'git') + for glob_entry in entry['globs']: + if type(glob_entry) != unicode: + self._give_up("Lorries globs should be strings") + fullglob = os.path.join(git_base, glob_entry) + all_lorries = all_lorries.union(set(glob.iglob(fullglob))) + for lorry in all_lorries: + if not lorry.startswith(git_base): + self._give_up("Glob found %s which is outside the git base") + + logging.debug("Expanded globs in entry to %d lorries" % + len(all_lorries)) + entry['lorries'] = all_lorries + + def _give_up(self, *args, **kwargs): + logging.error(*args, **kwargs) + raise SystemExit(5) |