Почему Ansible оценивает эту переменную, даже внутри блока false if? - PullRequest
5 голосов
/ 12 апреля 2019

Когда 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 ]

1 голос
/ 12 апреля 2019

Ansible расширяет шаблонные ключи по запросу из магического контекстного словаря, который он передает Jinja, когда Jinja запрашивает их, однако Jinja рано связывает любое имя, на которое ссылается шаблон, до начала какой-либо обработки.

Jinja ожидает контекстчтобы получить конкретное значение или эквивалент KeyError («Undefined» IIRC), Ansible OTOH использует этот момент для рекурсивного вызова Jinja, чтобы построить значение для передачи в исходный вызов шаблона.Именно в этом рекурсивном вызове происходит ваша ошибка.

Может быть полезно взглянуть на необработанный источник Jinja для похожего шаблона (подготовленного с jinja2.Environment().compile(..., raw=True):

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() завершаются до того, как произойдет какая-либо условная оценка. В resolve() Ansible пытается рекурсивно расширить вашу переменную foo.

Должна быть возможность настроить все так, чтобы fooраскрывается только в том случае, если Джинджа попытается преобразовать его в строку (или аналогичную), поэтому я бы посоветовал оставить вышестоящую ошибку.

0 голосов
/ 12 апреля 2019

Ansilbe в сочетании с Jinja2 сначала оценит шаблонную переменную как строку, прежде чем эффективно воспроизвести условие, следовательно, ваш сбой.

Способ управления вашим делом ansible / jinja2 заключается в использовании defaultФильтр при определении foo

foo: "{{ groups.group1[0] | default('') }}"

И тогда вам не нужно проверять любое другое косвенное значение.Просто используйте переменную

ansible -i inventory.yml group2 -mdebug -amsg="{{ foo }}"
localhost | SUCCESS => {
    "msg": ""
}
0 голосов
/ 12 апреля 2019

Кажется, что groups.group1[0] - это то, что не определено.

...