mirror of
https://github.com/zebrajr/ansible.git
synced 2025-12-06 12:19:53 +01:00
Add an undef global Jinja function (#75435)
* add tests for fail filter
also tests that fail does not block inspectability
* add fail filter
fallback message is a bit clunky,
since you can't invoke a filter without specifying an input.
That is, "{{ fail }}" doesn't work,
so you have to do "{{ None | fail }}"
* document 'fail' filter
* add changelog fragment
* fail filter uses default message on Undefined or emptystring
makes it slightly easier to use the default message:
```diff
- "{{ None | fail }}"
+ "{{ '' | fail }}"
```
and the user sees a slightly more relevant message
if the message itself is undefined:
```diff
- The error was: {{ failmsg | fail }}: 'failmsg' is undefined
+ The error was: {{ failmsg | fail }}: Mandatory variable has not been overridden
```
* rebuild as the builtin `Undefined`
* harmonise `hint` parameter for make_undefined with jinja
* use code block for documentation item
[ref](https://github.com/ansible/ansible/pull/75435#discussion_r707661035)
* rename to `undef` to expose less Python into the Jinja
[ref](https://github.com/ansible/ansible/pull/75435#pullrequestreview-757799031)
* explicitly instantiate undefined value now that it's possible
see I knew we would break something with reflection
* preserve test coverage of undefined variable
Co-authored-by: Matt Davis <nitzmahone@users.noreply.github.com>
This commit is contained in:
parent
47b644570f
commit
989eeb243f
2
changelogs/fragments/75435-creating-Undefined.yml
Normal file
2
changelogs/fragments/75435-creating-Undefined.yml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
minor_changes:
|
||||
- adds the ``undef`` keyword to the templating environment. This allows for directly creating Undefined values in templates. It is most useful for providing a hint for variables which must be overridden.
|
||||
|
|
@ -68,6 +68,13 @@ If you configure Ansible to ignore undefined variables, you may want to define s
|
|||
|
||||
The variable value will be used as is, but the template evaluation will raise an error if it is undefined.
|
||||
|
||||
A convenient way of requiring a variable to be overridden is to give it an undefined value using the ``undef`` keyword. This can be useful in a role's defaults.
|
||||
|
||||
.. code-block:: yaml+jinja
|
||||
|
||||
galaxy_url: "https://galaxy.ansible.com"
|
||||
galaxy_api_key: {{ undef(hint="You must specify your Galaxy API key") }}
|
||||
|
||||
Defining different values for true/false/null (ternary)
|
||||
=======================================================
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ from ansible.module_utils.common._collections_compat import Mapping
|
|||
from ansible.module_utils.common.yaml import yaml_load, yaml_load_all
|
||||
from ansible.parsing.ajson import AnsibleJSONEncoder
|
||||
from ansible.parsing.yaml.dumper import AnsibleDumper
|
||||
from ansible.template import recursive_check_defined
|
||||
from ansible.template import AnsibleUndefined, recursive_check_defined
|
||||
from ansible.utils.display import Display
|
||||
from ansible.utils.encrypt import passlib_or_crypt
|
||||
from ansible.utils.hashing import md5s, checksum_s
|
||||
|
|
|
|||
|
|
@ -693,6 +693,7 @@ class Templar:
|
|||
self.environment.globals['query'] = self.environment.globals['q'] = self._query_lookup
|
||||
self.environment.globals['now'] = self._now_datetime
|
||||
self.environment.globals['finalize'] = self._finalize
|
||||
self.environment.globals['undef'] = self._make_undefined
|
||||
|
||||
# the current rendering context under which the templar class is working
|
||||
self.cur_context = None
|
||||
|
|
@ -1068,6 +1069,13 @@ class Templar:
|
|||
|
||||
return ran
|
||||
|
||||
def _make_undefined(self, hint=None):
|
||||
from jinja2.runtime import Undefined
|
||||
|
||||
if hint is None or isinstance(hint, Undefined) or hint == '':
|
||||
hint = "Mandatory variable has not been overridden"
|
||||
return AnsibleUndefined(hint)
|
||||
|
||||
def do_template(self, data, preserve_trailing_newlines=True, escape_backslashes=True, fail_on_undefined=None, overrides=None, disable_lookups=False):
|
||||
if self.jinja2_native and not isinstance(data, string_types):
|
||||
return data
|
||||
|
|
|
|||
|
|
@ -494,6 +494,39 @@
|
|||
- mandatory_2 is failed
|
||||
- "mandatory_2.msg == 'You did not give me a variable. I am a sad wolf.'"
|
||||
|
||||
- name: Verify undef throws if resolved
|
||||
set_fact:
|
||||
foo: '{{ fail_foo }}'
|
||||
vars:
|
||||
fail_foo: '{{ undef("Expected failure") }}'
|
||||
ignore_errors: yes
|
||||
register: fail_1
|
||||
|
||||
- name: Setup fail_foo for overriding in test
|
||||
block:
|
||||
- name: Verify undef not executed if overridden
|
||||
set_fact:
|
||||
foo: '{{ fail_foo }}'
|
||||
vars:
|
||||
fail_foo: 'overridden value'
|
||||
register: fail_2
|
||||
vars:
|
||||
fail_foo: '{{ undef(hint="Expected failure") }}'
|
||||
|
||||
- name: Verify undef is inspectable
|
||||
debug:
|
||||
var: fail_foo
|
||||
vars:
|
||||
fail_foo: '{{ undef("Expected failure") }}'
|
||||
register: fail_3
|
||||
|
||||
- name: Verify undef
|
||||
assert:
|
||||
that:
|
||||
- fail_1 is failed
|
||||
- not (fail_2 is failed)
|
||||
- not (fail_3 is failed)
|
||||
|
||||
- name: Verify comment
|
||||
assert:
|
||||
that:
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ set -eux
|
|||
|
||||
ANSIBLE_ROLES_PATH=../ ansible-playbook template.yml -i ../../inventory -v "$@"
|
||||
|
||||
# Test for #35571
|
||||
ansible testhost -i testhost, -m debug -a 'msg={{ hostvars["localhost"] }}' -e "vars1={{ undef }}" -e "vars2={{ vars1 }}"
|
||||
# Test for https://github.com/ansible/ansible/pull/35571
|
||||
ansible testhost -i testhost, -m debug -a 'msg={{ hostvars["localhost"] }}' -e "vars1={{ undef() }}" -e "vars2={{ vars1 }}"
|
||||
|
||||
# Test for https://github.com/ansible/ansible/issues/27262
|
||||
ansible-playbook ansible_managed.yml -c ansible_managed.cfg -i ../../inventory -v "$@"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@
|
|||
|
||||
- assert:
|
||||
that:
|
||||
- '"%r"|format(undef) == "AnsibleUndefined"'
|
||||
- '"%r"|format(an_undefined_var) == "AnsibleUndefined"'
|
||||
- '"%r"|format(undef()) == "AnsibleUndefined"'
|
||||
# The existence of AnsibleUndefined in a templating result
|
||||
# prevents safe_eval from turning the value into a python object
|
||||
- names is string
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
from jinja2.runtime import Undefined
|
||||
from jinja2.exceptions import UndefinedError
|
||||
__metaclass__ = type
|
||||
|
||||
import pytest
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user