From ac4efac3944d180cffd0ad9d63f631dc928e1d28 Mon Sep 17 00:00:00 2001 From: Jason Gerard DeRose Date: Fri, 24 Oct 2008 20:02:14 -0600 Subject: Finished Env._finalize_core() and corresponding unit tests --- ipalib/config.py | 62 ++++++++++++++++---- ipalib/constants.py | 38 +++++++++++- tests/test_ipalib/test_config.py | 124 +++++++++++++++++++++++++++++++++++---- tests/util.py | 8 +++ 4 files changed, 208 insertions(+), 24 deletions(-) diff --git a/ipalib/config.py b/ipalib/config.py index dd00d713f..bced62fda 100644 --- a/ipalib/config.py +++ b/ipalib/config.py @@ -145,13 +145,20 @@ class Env(object): self.home = path.abspath(os.environ['HOME']) self.dot_ipa = path.join(self.home, '.ipa') - def __do(self, name): + def __doing(self, name): if name in self.__done: raise StandardError( '%s.%s() already called' % (self.__class__.__name__, name) ) self.__done.add(name) + def __do_if_not_done(self, name): + if name not in self.__done: + getattr(self, name)() + + def _isdone(self, name): + return name in self.__done + def _bootstrap(self, **overrides): """ Initialize basic environment. @@ -159,10 +166,8 @@ class Env(object): This method will initialize only enough environment information to determine whether ipa is running in-tree, what the context is, and the location of the configuration file. - - This method should be called before any plugins are loaded. """ - self.__do('_bootstrap') + self.__doing('_bootstrap') for (key, value) in overrides.items(): self[key] = value if 'in_tree' not in self: @@ -180,7 +185,46 @@ class Env(object): else: self.conf = path.join('/', 'etc', 'ipa', name) - def _load_config(self, conf_file): + def _finalize_core(self, **defaults): + """ + Complete initialization of standard IPA environment. + + After this method is called, the all environment variables + used by all the built-in plugins will be available. + + This method should be called before loading any plugins. It will + automatically call `Env._bootstrap()` if it has not yet been called. + + After this method has finished, the `Env` instance is still writable + so that third + """ + self.__doing('_finalize_core') + self.__do_if_not_done('_bootstrap') + self._merge_config(self.conf) + if 'in_server' not in self: + self.in_server = (self.context == 'server') + if 'log' not in self: + name = '%s.log' % self.context + if self.in_tree or self.context == 'cli': + self.log = path.join(self.dot_ipa, 'log', name) + else: + self.log = path.join('/', 'var', 'log', 'ipa', name) + for (key, value) in defaults.items(): + if key not in self: + self[key] = value + + def _finalize(self): + """ + Finalize and lock environment. + + This method should be called after all plugins have bean loaded and + after `plugable.API.finalize()` has been called. + """ + self.__doing('_finalize') + self.__do_if_not_done('_finalize_core') + self.__lock__() + + def _merge_config(self, conf_file): """ Merge in values from ``conf_file`` into this `Env`. """ @@ -204,14 +248,6 @@ class Env(object): i += 1 return (i, len(items)) - def _finalize(self, **defaults): - """ - Finalize and lock environment. - - This method should be called after all plugins have bean loaded and - after `plugable.API.finalize()` has been called. - """ - def __lock__(self): """ Prevent further changes to environment. diff --git a/ipalib/constants.py b/ipalib/constants.py index d817fda45..a1cc5b4c6 100644 --- a/ipalib/constants.py +++ b/ipalib/constants.py @@ -21,5 +21,41 @@ Constants centralized in one file. """ -# The section read in config files, i.e. [global] +# The section to read in the config files, i.e. [global] CONFIG_SECTION = 'global' + + +# The default configuration for api.env +DEFAULT_CONFIG = ( + ('lite_xmlrpc_port', 8888), + ('lite_webui_port', 9999), + ('xmlrpc_uri', 'http://localhost:8888'), + ('ldap_uri', ''), + + ('verbose', False), + ('debug', False), + + # Env.__init__() or Env._bootstrap() or Env._finalize_core() + # will have filled in all the keys below by the time DEFAULT_CONFIG + # is merged in, so the values below are never actually used. They are + # listed both to provide a big picture and so DEFAULT_CONFIG contains + # the keys that should be present after Env._load_standard is called. + + # Set in Env.__init__(): + ('ipalib', None), # The directory containing ipalib/__init__.py + ('site_packages', None), # The directory contaning ipalib + ('script', None), # sys.argv[0] + ('bin', None), # The directory containing script + ('home', None), # The home directory of user underwhich process is running + ('dot_ipa', None), # ~/.ipa directory + + # Set in Env._bootstrap(): + ('in_tree', None), # Whether or not running in-tree (bool) + ('context', None), # Name of context, default is 'default' + ('conf', None), # Path to configuration file + + # Set in Env._finalize_core(): + ('in_server', None), # Whether or not running in-server (bool) + ('log', None), # Path to log file + +) diff --git a/tests/test_ipalib/test_config.py b/tests/test_ipalib/test_config.py index 01ff2f01a..b4c86319f 100644 --- a/tests/test_ipalib/test_config.py +++ b/tests/test_ipalib/test_config.py @@ -28,7 +28,7 @@ import sys from tests.util import raises, setitem, delitem, ClassChecker from tests.util import getitem, setitem, delitem from tests.util import TempDir, TempHome -from ipalib import config +from ipalib import config, constants def test_Environment(): @@ -157,6 +157,7 @@ WfUu+/mDOAGOjsRo0UkIo+pPl6Rckl7ehuR1INGAj9u0kW2nXvK45YlQp1odukaICSAjgSQWf//Z # A config file that tries to override some standard vars: config_override = """ [global] + key0 = var0 home = /home/sweet/home key1 = var1 @@ -165,9 +166,10 @@ key2 = var2 key3 = var3 """ -# A config file that test the automatic type conversion +# A config file that tests the automatic type conversion config_good = """ [global] + yes = TRUE no = False number = 42 @@ -205,7 +207,9 @@ class test_Env(ClassChecker): def bootstrap(self, **overrides): (o, home) = self.new() + assert o._isdone('_bootstrap') is False o._bootstrap(**overrides) + assert o._isdone('_bootstrap') is True e = raises(StandardError, o._bootstrap) assert str(e) == 'Env._bootstrap() already called' return (o, home) @@ -214,7 +218,6 @@ class test_Env(ClassChecker): """ Test the `ipalib.config.Env._bootstrap` method. """ - # Test defaults created by _bootstrap(): (o, home) = self.new() assert 'in_tree' not in o @@ -253,9 +256,110 @@ class test_Env(ClassChecker): assert getattr(o, key) == value assert o[key] == value - def test_load_config(self): + def finalize_core(self, **defaults): + (o, home) = self.new() + assert o._isdone('_finalize_core') is False + o._finalize_core(**defaults) + assert o._isdone('_finalize_core') is True + e = raises(StandardError, o._finalize_core) + assert str(e) == 'Env._finalize_core() already called' + return (o, home) + + def test_finalize_core(self): + """ + Test the `ipalib.config.Env._finalize_core` method. + """ + # Check that calls cascade up the chain: + (o, home) = self.new() + assert o._isdone('_bootstrap') is False + assert o._isdone('_finalize_core') is False + assert o._isdone('_finalize') is False + o._finalize_core() + assert o._isdone('_bootstrap') is True + assert o._isdone('_finalize_core') is True + assert o._isdone('_finalize') is False + + # Check that it can't be called twice: + e = raises(StandardError, o._finalize_core) + assert str(e) == 'Env._finalize_core() already called' + + # Check that _bootstrap() did its job: + (o, home) = self.bootstrap() + assert 'in_tree' in o + assert 'conf' in o + assert 'context' in o + + # Check that keys _finalize_core() will set are not set yet: + assert 'log' not in o + assert 'in_server' not in o + + # Check that _finalize_core() did its job: + o._finalize_core() + assert 'in_server' in o + assert 'log' in o + assert o.in_tree is False + assert o.context == 'default' + assert o.in_server is False + assert o.log == '/var/log/ipa/default.log' + + # Check log is in ~/.ipa/log when context='cli' + (o, home) = self.bootstrap(context='cli') + o._finalize_core() + assert o.in_tree is False + assert o.log == home.join('.ipa', 'log', 'cli.log') + + # Check **defaults can't set in_server nor log: + (o, home) = self.bootstrap(in_server='tRUE') + o._finalize_core(in_server=False) + assert o.in_server is True + (o, home) = self.bootstrap(log='/some/silly/log') + o._finalize_core(log='/a/different/log') + assert o.log == '/some/silly/log' + + # Test loading config file, plus test some in-tree stuff + (o, home) = self.bootstrap(in_tree=True, context='server') + for key in ('yes', 'no', 'number'): + assert key not in o + home.write(config_good, '.ipa', 'server.conf') + o._finalize_core() + assert o.in_tree is True + assert o.context == 'server' + assert o.in_server is True + assert o.log == home.join('.ipa', 'log', 'server.log') + assert o.yes is True + assert o.no is False + assert o.number == 42 + + # Test using DEFAULT_CONFIG: + defaults = dict(constants.DEFAULT_CONFIG) + (o, home) = self.finalize_core(**defaults) + assert list(o) == sorted(defaults) + for (key, value) in defaults.items(): + if value is None: + continue + assert o[key] is value + + def test_finalize(self): + """ + Test the `ipalib.config.Env._finalize` method. + """ + # Check that calls cascade up the chain: + (o, home) = self.new() + assert o._isdone('_bootstrap') is False + assert o._isdone('_finalize_core') is False + assert o._isdone('_finalize') is False + o._finalize() + assert o._isdone('_bootstrap') is True + assert o._isdone('_finalize_core') is True + assert o._isdone('_finalize') is True + + # Check that it can't be called twice: + e = raises(StandardError, o._finalize) + assert str(e) == 'Env._finalize() already called' + + def test_merge_config(self): """ - Test the `ipalib.config.Env._load_config` method. + Test the `ipalib.config.Env._merge_config` method. """ tmp = TempDir() assert callable(tmp.join) @@ -266,27 +370,27 @@ class test_Env(ClassChecker): o = self.cls() keys = tuple(o) orig = dict((k, o[k]) for k in o) - assert o._load_config(no_exist) is None + assert o._merge_config(no_exist) is None assert tuple(o) == keys # Test an empty config file empty = tmp.touch('empty.conf') assert path.isfile(empty) - assert o._load_config(empty) is None + assert o._merge_config(empty) is None assert tuple(o) == keys # Test a mal-formed config file: bad = tmp.join('bad.conf') open(bad, 'w').write(config_bad) assert path.isfile(bad) - assert o._load_config(bad) is None + assert o._merge_config(bad) is None assert tuple(o) == keys # Test a valid config file that tries to override override = tmp.join('override.conf') open(override, 'w').write(config_override) assert path.isfile(override) - assert o._load_config(override) == (4, 6) + assert o._merge_config(override) == (4, 6) for (k, v) in orig.items(): assert o[k] is v assert list(o) == sorted(keys + ('key0', 'key1', 'key2', 'key3')) @@ -298,7 +402,7 @@ class test_Env(ClassChecker): good = tmp.join('good.conf') open(good, 'w').write(config_good) assert path.isfile(good) - assert o._load_config(good) == (3, 3) + assert o._merge_config(good) == (3, 3) assert list(o) == sorted(keys + ('yes', 'no', 'number')) assert o.yes is True assert o.no is False diff --git a/tests/util.py b/tests/util.py index 1cbc4e312..cc761ce72 100644 --- a/tests/util.py +++ b/tests/util.py @@ -61,6 +61,14 @@ class TempDir(object): assert path.isfile(f) and not path.islink(f) return f + def write(self, content, *parts): + d = self.makedirs(*parts[:-1]) + f = path.join(d, parts[-1]) + assert not path.exists(f) + open(f, 'w').write(content) + assert path.isfile(f) and not path.islink(f) + return f + def join(self, *parts): return path.join(self.path, *parts) -- cgit