diff options
author | kali <kali@leap.se> | 2012-10-02 05:31:47 +0900 |
---|---|---|
committer | kali <kali@leap.se> | 2012-10-02 05:31:47 +0900 |
commit | b81337d8a183bffe32ebf512c49a6af07fc7622e (patch) | |
tree | 0352010716fcc0118eab6c0d6c9e5e543c8b150f /src/leap/base/config.py | |
parent | f56e89199c063ea76a901ae187933c7604b4858b (diff) | |
parent | abf481cab381a86d8a9c5607a131b56636081382 (diff) |
Merge branch 'feature/json-validation' into develop
Conflicts:
src/leap/eip/config.py
src/leap/eip/specs.py
Diffstat (limited to 'src/leap/base/config.py')
-rw-r--r-- | src/leap/base/config.py | 96 |
1 files changed, 44 insertions, 52 deletions
diff --git a/src/leap/base/config.py b/src/leap/base/config.py index 76fbee3c..dc047f80 100644 --- a/src/leap/base/config.py +++ b/src/leap/base/config.py @@ -9,13 +9,12 @@ import tempfile import os logger = logging.getLogger(name=__name__) -logger.setLevel('DEBUG') -import configuration import requests from leap.base import exceptions from leap.base import constants +from leap.base.pluggableconfig import PluggableConfig from leap.util.fileutil import (mkdir_p) # move to base! @@ -38,13 +37,9 @@ class BaseLeapConfig(object): def get_config(self, *kwargs): raise NotImplementedError("abstract base class") - #XXX todo: enable this property after - #fixing name clash with "config" in use at - #vpnconnection - - #@property - #def config(self): - #return self.get_config() + @property + def config(self): + return self.get_config() def get_value(self, *kwargs): raise NotImplementedError("abstract base class") @@ -54,55 +49,51 @@ class MetaConfigWithSpec(type): """ metaclass for JSONLeapConfig classes. It creates a configuration spec out of - the `spec` dictionary. + the `spec` dictionary. The `properties` attribute + of the spec dict is turn into the `schema` attribute + of the new class (which will be used to validate against). """ # XXX in the near future, this is the # place where we want to enforce - # singletons, read-only and stuff. - - # TODO: - # - add a error handler for missing options that - # we can act easily upon (sys.exit is ugly, for $deity's sake) + # singletons, read-only and similar stuff. def __new__(meta, classname, bases, classDict): - spec_options = classDict.get('spec', None) + schema_obj = classDict.get('spec', None) + # not quite happy with this workaround. # I want to raise if missing spec dict, but only # for grand-children of this metaclass. # maybe should use abc module for this. abcderived = ("JSONLeapConfig",) - if spec_options is None and classname not in abcderived: + if schema_obj is None and classname not in abcderived: raise exceptions.ImproperlyConfigured( - "missing spec dict on your derived class") + "missing spec dict on your derived class (%s)" % classname) - # we create a configuration spec attribute from the spec dict + # we create a configuration spec attribute + # from the spec dict config_class = type( classname + "Spec", - (configuration.Configuration, object), - {'options': spec_options}) + (PluggableConfig, object), + {'options': schema_obj}) classDict['spec'] = config_class return type.__new__(meta, classname, bases, classDict) ########################################################## -# hacking in progress: +# some hacking still in progress: # Configs have: + # - a slug (from where a filename/folder is derived) # - a spec (for validation and defaults). -# this spec is basically a dict that will be used +# this spec is conformant to the json-schema. +# basically a dict that will be used # for type casting and validation, and defaults settings. # all config objects, since they are derived from BaseConfig, implement basic # useful methods: # - save # - load -# - get_config (returns a optparse.OptionParser object) - -# TODO: -# - have a good type cast repertory (uris, version, hashes...) -# - raise validation errors -# - multilingual objects ########################################################## @@ -125,10 +116,10 @@ class JSONLeapConfig(BaseLeapConfig): raise exceptions.ImproperlyConfigured( "missing spec on JSONLeapConfig" " derived class") - assert issubclass(self.spec, configuration.Configuration) + assert issubclass(self.spec, PluggableConfig) - self._config = self.spec() - self._config.parse_args(list(args)) + self._config = self.spec(format="json") + self._config.load() self.fetcher = kwargs.pop('fetcher', requests) # mandatory baseconfig interface @@ -139,13 +130,6 @@ class JSONLeapConfig(BaseLeapConfig): folder, filename = os.path.split(to) if folder and not os.path.isdir(folder): mkdir_p(folder) - # lazy evaluation until first level of nesting - # to allow lambdas with context-dependant info - # like os.path.expanduser - config = self.get_config() - for k, v in config.iteritems(): - if callable(v): - config[k] = v() self._config.serialize(to) def load(self, fromfile=None, from_uri=None, fetcher=None, verify=False): @@ -155,28 +139,36 @@ class JSONLeapConfig(BaseLeapConfig): return if fromfile is None: fromfile = self.filename - newconfig = self._config.deserialize(fromfile) - # XXX check for no errors, etc - self._config.config = newconfig + if os.path.isfile(fromfile): + self._config.load(fromfile=fromfile) + else: + logger.error('tried to load config from non-existent path') + logger.error('Not Found: %s', fromfile) def fetch(self, uri, fetcher=None, verify=True): if not fetcher: fetcher = self.fetcher logger.debug('verify: %s', verify) request = fetcher.get(uri, verify=verify) + # XXX should send a if-modified-since header # XXX get 404, ... # and raise a UnableToFetch... request.raise_for_status() fd, fname = tempfile.mkstemp(suffix=".json") - if not request.json: + + if request.json: + self._config.load(json.dumps(request.json)) + + else: + # not request.json + # might be server did not announce content properly, + # let's try deserializing all the same. try: - json.loads(request.content) + self._config.load(request.content) except ValueError: raise eipexceptions.LeapBadConfigFetchedError - with open(fname, 'w') as tmp: - tmp.write(json.dumps(request.json)) - self._loadtemp(fname) + return True def get_config(self): @@ -191,16 +183,16 @@ class JSONLeapConfig(BaseLeapConfig): def filename(self): return self.get_filename() - # private + def validate(self, data): + logger.debug('validating schema') + self._config.validate(data) + return True - def _loadtemp(self, filename): - self.load(fromfile=filename) - os.remove(filename) + # private def _slug_to_filename(self): # is this going to work in winland if slug is "foo/bar" ? folder, filename = os.path.split(self.slug) - # XXX fix import config_file = get_config_file(filename, folder) return config_file |