summaryrefslogtreecommitdiff
path: root/src/leap/base/config.py
diff options
context:
space:
mode:
authorkali <kali@leap.se>2012-10-02 05:31:47 +0900
committerkali <kali@leap.se>2012-10-02 05:31:47 +0900
commitb81337d8a183bffe32ebf512c49a6af07fc7622e (patch)
tree0352010716fcc0118eab6c0d6c9e5e543c8b150f /src/leap/base/config.py
parentf56e89199c063ea76a901ae187933c7604b4858b (diff)
parentabf481cab381a86d8a9c5607a131b56636081382 (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.py96
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