Catch errors getting filters (#74127)
* Catch errors getting filters, and fail * Add changelog * Switch to warnings instead of errors, to match other plugin loader behavior * Add tests * Handle collections
This commit is contained in:
parent
28a2d9b4ae
commit
1082e2ab79
14 changed files with 159 additions and 5 deletions
5
changelogs/fragments/74127-bad-filter.yml
Normal file
5
changelogs/fragments/74127-bad-filter.yml
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
bugfixes:
|
||||||
|
- Templating - Ensure we catch exceptions when calling ``.filters()`` or
|
||||||
|
``.tests()`` on their respective plugins and properly error, instead of
|
||||||
|
aborting which results in no filters being added to the jinja2 environment
|
||||||
|
(https://github.com/ansible/ansible/pull/74127)
|
|
@ -438,8 +438,12 @@ class JinjaPluginIntercept(MutableMapping):
|
||||||
return
|
return
|
||||||
|
|
||||||
for plugin in self._pluginloader.all():
|
for plugin in self._pluginloader.all():
|
||||||
|
try:
|
||||||
method_map = getattr(plugin, self._method_map_name)
|
method_map = getattr(plugin, self._method_map_name)
|
||||||
self._delegatee.update(method_map())
|
self._delegatee.update(method_map())
|
||||||
|
except Exception as e:
|
||||||
|
display.warning("Skipping %s plugin %s as it seems to be invalid: %r" % (self._dirname, to_text(plugin._original_path), e))
|
||||||
|
continue
|
||||||
|
|
||||||
if self._pluginloader.class_name == 'FilterModule':
|
if self._pluginloader.class_name == 'FilterModule':
|
||||||
for plugin_name, plugin in self._delegatee.items():
|
for plugin_name, plugin in self._delegatee.items():
|
||||||
|
@ -546,7 +550,15 @@ class JinjaPluginIntercept(MutableMapping):
|
||||||
|
|
||||||
method_map = getattr(plugin_impl, self._method_map_name)
|
method_map = getattr(plugin_impl, self._method_map_name)
|
||||||
|
|
||||||
for func_name, func in iteritems(method_map()):
|
try:
|
||||||
|
func_items = iteritems(method_map())
|
||||||
|
except Exception as e:
|
||||||
|
display.warning(
|
||||||
|
"Skipping %s plugin %s as it seems to be invalid: %r" % (self._dirname, to_text(plugin_impl._original_path), e),
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
for func_name, func in func_items:
|
||||||
fq_name = '.'.join((parent_prefix, func_name))
|
fq_name = '.'.join((parent_prefix, func_name))
|
||||||
# FIXME: detect/warn on intra-collection function name collisions
|
# FIXME: detect/warn on intra-collection function name collisions
|
||||||
if self._pluginloader.class_name == 'FilterModule':
|
if self._pluginloader.class_name == 'FilterModule':
|
||||||
|
|
|
@ -106,11 +106,19 @@ def safe_eval(expr, locals=None, include_exceptions=False):
|
||||||
|
|
||||||
filter_list = []
|
filter_list = []
|
||||||
for filter_ in filter_loader.all():
|
for filter_ in filter_loader.all():
|
||||||
|
try:
|
||||||
filter_list.extend(filter_.filters().keys())
|
filter_list.extend(filter_.filters().keys())
|
||||||
|
except Exception:
|
||||||
|
# This is handled and displayed in JinjaPluginIntercept._load_ansible_plugins
|
||||||
|
continue
|
||||||
|
|
||||||
test_list = []
|
test_list = []
|
||||||
for test in test_loader.all():
|
for test in test_loader.all():
|
||||||
|
try:
|
||||||
test_list.extend(test.tests().keys())
|
test_list.extend(test.tests().keys())
|
||||||
|
except Exception:
|
||||||
|
# This is handled and displayed in JinjaPluginIntercept._load_ansible_plugins
|
||||||
|
continue
|
||||||
|
|
||||||
CALL_ENABLED = C.CALLABLE_ACCEPT_LIST + filter_list + test_list
|
CALL_ENABLED = C.CALLABLE_ACCEPT_LIST + filter_list + test_list
|
||||||
|
|
||||||
|
|
1
test/integration/targets/jinja_plugins/aliases
Normal file
1
test/integration/targets/jinja_plugins/aliases
Normal file
|
@ -0,0 +1 @@
|
||||||
|
shippable/posix/group5
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Copyright (c) 2021 Matt Martz <matt@sivel.net>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
# Make coding more python3-ish
|
||||||
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
|
||||||
|
class FilterModule:
|
||||||
|
def filters(self):
|
||||||
|
raise TypeError('bad_collection_filter')
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Copyright (c) 2021 Matt Martz <matt@sivel.net>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
# Make coding more python3-ish
|
||||||
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
|
||||||
|
class FilterModule:
|
||||||
|
def filters(self):
|
||||||
|
return {
|
||||||
|
'hello': lambda x: 'Hello, %s!' % x,
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Copyright (c) 2021 Matt Martz <matt@sivel.net>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
# Make coding more python3-ish
|
||||||
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
|
||||||
|
class TestModule:
|
||||||
|
def tests(self):
|
||||||
|
raise TypeError('bad_collection_test')
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Copyright (c) 2021 Matt Martz <matt@sivel.net>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
# Make coding more python3-ish
|
||||||
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
|
||||||
|
class TestModule:
|
||||||
|
def tests(self):
|
||||||
|
return {
|
||||||
|
'world': lambda x: x.lower() == 'world',
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Copyright (c) 2021 Matt Martz <matt@sivel.net>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
# Make coding more python3-ish
|
||||||
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
|
||||||
|
class FilterModule:
|
||||||
|
def filters(self):
|
||||||
|
raise TypeError('bad_filter')
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Copyright (c) 2021 Matt Martz <matt@sivel.net>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
# Make coding more python3-ish
|
||||||
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
|
||||||
|
class FilterModule:
|
||||||
|
def filters(self):
|
||||||
|
return {
|
||||||
|
'hello': lambda x: 'Hello, %s!' % x,
|
||||||
|
}
|
10
test/integration/targets/jinja_plugins/playbook.yml
Normal file
10
test/integration/targets/jinja_plugins/playbook.yml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
- hosts: localhost
|
||||||
|
gather_facts: false
|
||||||
|
tasks:
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- '"World"|hello == "Hello, World!"'
|
||||||
|
- '"World" is world'
|
||||||
|
|
||||||
|
- '"World"|foo.bar.hello == "Hello, World!"'
|
||||||
|
- '"World" is foo.bar.world'
|
22
test/integration/targets/jinja_plugins/tasks/main.yml
Normal file
22
test/integration/targets/jinja_plugins/tasks/main.yml
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
- shell: ansible-playbook {{ verbosity }} playbook.yml
|
||||||
|
args:
|
||||||
|
chdir: '{{ role_path }}'
|
||||||
|
vars:
|
||||||
|
verbosity: "{{ '' if not ansible_verbosity else '-' ~ ('v' * ansible_verbosity) }}"
|
||||||
|
register: result
|
||||||
|
|
||||||
|
- debug:
|
||||||
|
var: result
|
||||||
|
|
||||||
|
- assert:
|
||||||
|
that:
|
||||||
|
- '"[WARNING]: Skipping filter plugin" in result.stderr'
|
||||||
|
- '"[WARNING]: Skipping test plugin" in result.stderr'
|
||||||
|
- |
|
||||||
|
result.stderr|regex_findall('bad_filter')|length == 2
|
||||||
|
- |
|
||||||
|
result.stderr|regex_findall('bad_test')|length == 2
|
||||||
|
- |
|
||||||
|
result.stderr|regex_findall('bad_collection_filter')|length == 2
|
||||||
|
- |
|
||||||
|
result.stderr|regex_findall('bad_collection_test')|length == 2
|
|
@ -0,0 +1,11 @@
|
||||||
|
# Copyright (c) 2021 Matt Martz <matt@sivel.net>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
# Make coding more python3-ish
|
||||||
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
|
||||||
|
class TestModule:
|
||||||
|
def tests(self):
|
||||||
|
raise TypeError('bad_test')
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Copyright (c) 2021 Matt Martz <matt@sivel.net>
|
||||||
|
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||||
|
|
||||||
|
# Make coding more python3-ish
|
||||||
|
from __future__ import (absolute_import, division, print_function)
|
||||||
|
__metaclass__ = type
|
||||||
|
|
||||||
|
|
||||||
|
class TestModule:
|
||||||
|
def tests(self):
|
||||||
|
return {
|
||||||
|
'world': lambda x: x.lower() == 'world',
|
||||||
|
}
|
Loading…
Reference in a new issue