From 03c16096d737a43166719e9b8e9f816a533200f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sylvain=20Monn=C3=A9?= Date: Wed, 4 Dec 2019 13:24:52 +0100 Subject: [PATCH] Fix extract filter when key does not exist in container (#64959) Fixes #64957 --- ...extract-filter-when-key-does-not-exist.yml | 3 +++ lib/ansible/plugins/filter/core.py | 23 ++++++++-------- lib/ansible/vars/hostvars.py | 8 +++--- .../targets/filters/tasks/main.yml | 27 +++++++++++++++++-- 4 files changed, 42 insertions(+), 19 deletions(-) create mode 100644 changelogs/fragments/64959-extract-filter-when-key-does-not-exist.yml diff --git a/changelogs/fragments/64959-extract-filter-when-key-does-not-exist.yml b/changelogs/fragments/64959-extract-filter-when-key-does-not-exist.yml new file mode 100644 index 00000000000..e421bd15acf --- /dev/null +++ b/changelogs/fragments/64959-extract-filter-when-key-does-not-exist.yml @@ -0,0 +1,3 @@ +bugfixes: + - core filters - fix ``extract()`` filter when key does not exist in container + (https://github.com/ansible/ansible/issues/64957) diff --git a/lib/ansible/plugins/filter/core.py b/lib/ansible/plugins/filter/core.py index 2c9ad8893d8..eecf7eafe80 100644 --- a/lib/ansible/plugins/filter/core.py +++ b/lib/ansible/plugins/filter/core.py @@ -410,19 +410,18 @@ def comment(text, style='plain', **kw): str_end) -def extract(item, container, morekeys=None): - from jinja2.runtime import Undefined +@environmentfilter +def extract(environment, item, container, morekeys=None): + if morekeys is None: + keys = [item] + elif isinstance(morekeys, list): + keys = [item] + morekeys + else: + keys = [item, morekeys] - value = container[item] - - if value is not Undefined and morekeys is not None: - if not isinstance(morekeys, list): - morekeys = [morekeys] - - try: - value = reduce(lambda d, k: d[k], morekeys, value) - except KeyError: - value = Undefined() + value = container + for key in keys: + value = environment.getitem(value, key) return value diff --git a/lib/ansible/vars/hostvars.py b/lib/ansible/vars/hostvars.py index 1d3c5ef9432..3159b920ce0 100644 --- a/lib/ansible/vars/hostvars.py +++ b/lib/ansible/vars/hostvars.py @@ -19,10 +19,8 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -from jinja2.runtime import Undefined - from ansible.module_utils.common._collections_compat import Mapping -from ansible.template import Templar +from ansible.template import Templar, AnsibleUndefined STATIC_VARS = [ 'ansible_version', @@ -75,13 +73,13 @@ class HostVars(Mapping): ''' host = self._find_host(host_name) if host is None: - return Undefined(name="hostvars['%s']" % host_name) + return AnsibleUndefined(name="hostvars['%s']" % host_name) return self._variable_manager.get_vars(host=host, include_hostvars=False) def __getitem__(self, host_name): data = self.raw_get(host_name) - if isinstance(data, Undefined): + if isinstance(data, AnsibleUndefined): return data return HostVarsVars(data, loader=self._loader) diff --git a/test/integration/targets/filters/tasks/main.yml b/test/integration/targets/filters/tasks/main.yml index 678d26f2efb..13e14ae8248 100644 --- a/test/integration/targets/filters/tasks/main.yml +++ b/test/integration/targets/filters/tasks/main.yml @@ -116,6 +116,29 @@ # map was added to jinja2 in version 2.7 when: "{{ ( lookup('pipe', '{{ ansible_python[\"executable\"] }} -c \"import jinja2; print(jinja2.__version__)\"') is version('2.7', '>=') ) }}" +- name: Test extract filter with defaults + vars: + container: + key: + subkey: value + assert: + that: + - "'key' | extract(badcontainer) | default('a') == 'a'" + - "'key' | extract(badcontainer, 'subkey') | default('a') == 'a'" + - "('key' | extract(badcontainer)).subkey | default('a') == 'a'" + - "'badkey' | extract(container) | default('a') == 'a'" + - "'badkey' | extract(container, 'subkey') | default('a') == 'a'" + - "('badkey' | extract(container)).subsubkey | default('a') == 'a'" + - "'key' | extract(container, 'badsubkey') | default('a') == 'a'" + - "'key' | extract(container, ['badsubkey', 'subsubkey']) | default('a') == 'a'" + - "('key' | extract(container, 'badsubkey')).subsubkey | default('a') == 'a'" + - "'badkey' | extract(hostvars) | default('a') == 'a'" + - "'badkey' | extract(hostvars, 'subkey') | default('a') == 'a'" + - "('badkey' | extract(hostvars)).subsubkey | default('a') == 'a'" + - "'localhost' | extract(hostvars, 'badsubkey') | default('a') == 'a'" + - "'localhost' | extract(hostvars, ['badsubkey', 'subsubkey']) | default('a') == 'a'" + - "('localhost' | extract(hostvars, 'badsubkey')).subsubkey | default('a') == 'a'" + - name: Test json_query filter assert: that: @@ -239,10 +262,10 @@ assert: that: - "'00' | random_mac is match('^00:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]$')" - - "'00:00' | random_mac is match('^00:00:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]$')" + - "'00:00' | random_mac is match('^00:00:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]$')" - "'00:00:00' | random_mac is match('^00:00:00:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]$')" - "'00:00:00:00' | random_mac is match('^00:00:00:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]:[a-f0-9][a-f0-9]$')" - - "'00:00:00:00:00' | random_mac is match('^00:00:00:00:00:[a-f0-9][a-f0-9]$')" + - "'00:00:00:00:00' | random_mac is match('^00:00:00:00:00:[a-f0-9][a-f0-9]$')" - "'00:00:00' | random_mac != '00:00:00' | random_mac" - name: Verify random_mac filter with seed