summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgJinja2Generator.py45
-rw-r--r--testsuite/Testsrc/Testlib/TestServer/TestPlugins/TestCfg/TestCfgJinja2Generator.py45
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)