diff options
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 | 
