当Ansible遇到if
块,并且if
条件涉及groups
变量时,似乎扩展了之前块的内容评估了if
条件。这将导致未定义的变量错误,否则if
条件会防止该错误。
为什么会发生错误?是预期的行为还是错误?
我已将行为减少到最小的测试用例。
inventory.yml
group1:
group2:
hosts:
localhost:
vars:
foo: "{{ groups.group1[0] }}"
一个空字符串,因为在两种情况下,if
条件均为假
$ ansible -i inventory.yml group2 -mdebug -amsg="{% if false %}{{ foo }}{% endif %}"
localhost | SUCCESS => {
"msg": ""
}
$ ansible -i inventory.yml group2 -mdebug -amsg="{% if groups.group1 %}{{ foo }}{% endif %}"
localhost | SUCCESS => {
"msg": ""
}
当if
条件涉及groups
变量时,将评估foo
,从而产生未定义的变量消息
$ ansible -i inventory.yml group2 -mdebug -amsg="{% if false %}{{ foo }}{% endif %}"
localhost | SUCCESS => {
"msg": ""
}
$ ansible -i inventory.yml group2 -mdebug -amsg="{% if groups.group1 %}{{ foo }}{% endif %}"
localhost | FAILED! => {
"msg": "The task includes an option with an undefined variable. The error was: list object has no element 0"
}
我正在使用Ansible 2.7.9。
答案 0 :(得分:1)
Ansible会根据Jinja的要求,从传递给Jinja的魔幻上下文字典中按需扩展模板密钥,但是Jinja会在处理开始之前将模板引用的任何名称早绑定。
Jinja希望上下文产生具体的值或等效的KeyError
(“未定义” IIRC),Ansible OTOH使用这一时刻来递归调用Jinja,以建立将值传递到原始模板的方法调用。正是在此递归调用中发生了错误。
查看类似模板(由jinja2.Environment().compile(..., raw=True)
准备的原始Jinja来源可能会有所帮助:
from __future__ import division
from jinja2.runtime import LoopContext, TemplateReference, Macro, Markup, TemplateRuntimeError, missing, concat, escape, markup_join, unicode_join, to_string, identity, TemplateNotFound, Namespace
name = None
def root(context, missing=missing, environment=environment):
resolve = context.resolve_or_missing
undefined = environment.undefined
if 0: yield None
l_0_foo = resolve('foo')
l_0_groups = resolve('groups')
pass
if environment.getattr((undefined(name='groups') if l_0_groups is missing else l_0_groups), 'group1'):
pass
yield to_string((undefined(name='foo') if l_0_foo is missing else l_0_foo))
blocks = {}
debug_info = '1=12'
请注意,在进行任何条件评估之前,对resolve()
的调用是如何完成的。 Ansible尝试在resolve()
中递归扩展您的foo
变量。
应该可以进行调整,以使foo
仅在Jinja尝试将其转换为字符串(或类似字符串)时才会扩展,因此建议您提交上游错误。
答案 1 :(得分:0)
Ansilbe与Jinja2结合使用之前,将首先将模板化的var评估为字符串,然后再有效地播放条件,从而导致失败。
处理案件的ansible / jinja2方法是在定义foo
时使用default
过滤器
foo: "{{ groups.group1[0] | default('') }}"
然后,您无需测试任何其他间接值。只需使用var
ansible -i inventory.yml group2 -mdebug -amsg="{{ foo }}"
localhost | SUCCESS => {
"msg": ""
}
答案 2 :(得分:-3)
似乎groups.group1[0]
尚未定义。