diff options
-rw-r--r-- | src/lib/Bcfg2/Server/Plugins/Cfg/CfgJinja2Generator.py | 45 | ||||
-rw-r--r-- | testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgJinja2Generator.py | 45 |
2 files changed, 77 insertions, 13 deletions
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgJinja2Generator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgJinja2Generator.py index e36ee78aa..cff9ff61e 100644 --- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgJinja2Generator.py +++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgJinja2Generator.py @@ -2,18 +2,26 @@ <http://jinja.pocoo.org/>`_ templating system to generate :ref:`server-plugins-generators-cfg` files. """ +import os +import sys import Bcfg2.Options from Bcfg2.Server.Plugin import PluginExecutionError, \ DefaultTemplateDataProvider, get_template_data from Bcfg2.Server.Plugins.Cfg import CfgGenerator try: - from jinja2 import Template + from jinja2 import Environment, FileSystemLoader HAS_JINJA2 = True except ImportError: HAS_JINJA2 = False +class RelEnvironment(Environment): + """Override join_path() to enable relative template paths.""" + def join_path(self, template, parent): + return os.path.join(os.path.dirname(parent), template) + + class DefaultJinja2DataProvider(DefaultTemplateDataProvider): """ Template data provider for Jinja2 templates. Jinja2 and Genshi currently differ over the value of the ``path`` variable, @@ -34,6 +42,20 @@ class CfgJinja2Generator(CfgGenerator): #: Handle .jinja2 files __extensions__ = ['jinja2'] + #: ``__loader_cls__`` is the class that will be instantiated to + #: load the template files. It must implement one public function, + #: ``load()``, as :class:`genshi.template.TemplateLoader`. + __loader_cls__ = FileSystemLoader + + #: ``__environment_cls__`` is the class that will be instantiated to + #: store the jinja2 environment. It must implement one public function, + #: ``get_template()``, as :class:`jinja2.Environment`. + __environment_cls__ = RelEnvironment + + #: Ignore ``.jinja2_include`` files so they can be used with the + #: Jinja2 ``{% include ... %}`` directive without raising warnings. + __ignore__ = ["jinja2_include"] + #: Low priority to avoid matching host- or group-specific #: .crypt.jinja2 files __priority__ = 50 @@ -42,11 +64,28 @@ class CfgJinja2Generator(CfgGenerator): CfgGenerator.__init__(self, fname, spec) if not HAS_JINJA2: raise PluginExecutionError("Jinja2 is not available") + self.template = None + encoding = Bcfg2.Options.setup.encoding + self.loader = self.__loader_cls__('/', + encoding=encoding) + self.environment = self.__environment_cls__(loader=self.loader) __init__.__doc__ = CfgGenerator.__init__.__doc__ def get_data(self, entry, metadata): - template = Template(self.data.decode(Bcfg2.Options.setup.encoding)) - return template.render( + if self.template is None: + raise PluginExecutionError("Failed to load template %s" % + self.name) + return self.template.render( get_template_data(entry, metadata, self.name, default=DefaultJinja2DataProvider())) get_data.__doc__ = CfgGenerator.get_data.__doc__ + + def handle_event(self, event): + CfgGenerator.handle_event(self, event) + try: + self.template = \ + self.environment.get_template(self.name) + except: + raise PluginExecutionError("Failed to load template: %s" % + sys.exc_info()[1]) + handle_event.__doc__ = CfgGenerator.handle_event.__doc__ diff --git a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgJinja2Generator.py b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgJinja2Generator.py index 036380d56..8cf02e328 100644 --- a/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgJinja2Generator.py +++ b/testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgJinja2Generator.py @@ -25,23 +25,48 @@ class TestCfgJinja2Generator(TestCfgGenerator): TestCfgGenerator.setUp(self) set_setup_default("repository", datastore) - @patch("Bcfg2.Server.Plugins.Cfg.CfgJinja2Generator.Template") + def test__init(self): + TestCfgGenerator.test__init(self) + cgg = self.get_obj() + self.assertIsInstance(cgg.loader, cgg.__loader_cls__) + self.assertIsInstance(cgg.environment, cgg.__environment_cls__) + + @patch("Bcfg2.Server.Plugins.Cfg.CfgJinja2Generator.Environment") @patch("Bcfg2.Server.Plugins.Cfg.CfgJinja2Generator.get_template_data") - def test_get_data(self, mock_get_template_data, mock_Template): - ccg = self.get_obj() - ccg.data = "data" + def test_get_data(self, mock_get_template_data, mock_Environment): + cgg = self.get_obj() entry = lxml.etree.Element("Path", name="/test.txt") metadata = Mock() + # self.template is currently None + self.assertRaises(PluginExecutionError, + cgg.get_data, entry, metadata) + + cgg.template = mock_Environment.return_value.get_template.return_value + template_vars = dict(name=entry.get("name"), metadata=metadata, - path=ccg.name, - source_path=ccg.name, + path=cgg.name, + source_path=cgg.name, repo=datastore) mock_get_template_data.return_value = template_vars - self.assertEqual(ccg.get_data(entry, metadata), - mock_Template.return_value.render.return_value) - mock_Template.assert_called_with("data".decode(Bcfg2.Options.setup.encoding)) - tmpl = mock_Template.return_value + tmpl = mock_Environment.return_value.get_template.return_value + self.assertEqual(cgg.get_data(entry, metadata), + tmpl.render.return_value) tmpl.render.assert_called_with(template_vars) + + def test_handle_event(self): + cgg = self.get_obj() + cgg.environment = Mock() + event = Mock() + cgg.handle_event(event) + cgg.environment.get_template.assert_called_with( + cgg.name) + + cgg.environment.reset_mock() + cgg.environment.get_template.side_effect = OSError + self.assertRaises(PluginExecutionError, + cgg.handle_event, event) + cgg.environment.get_template.assert_called_with( + cgg.name) |