为什么Ansible会评估此变量,即使在false if块中也是如此?

时间:2019-04-12 12:33:35

标签: ansible jinja2

当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。

3 个答案:

答案 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]尚未定义。