1

Using ansible 2.7.5 I am trying to create a multi-line boolean parameter.

I first verified that it works as expected as one-line with:

FOOD: apple
IS_FRUIT: '{% if FOOD == "carrot" %}false{% elif FOOD == "apple" or FOOD == "banana" %}true{% endif %}'

and then a task:

- name: "FOOD is: {{ FOOD }}" 
  debug: msg="FOOD is {{ FOOD }} where IS_FRUIT {{ IS_FRUIT | bool }}"

which prints:

ok: [localhost] => {
    "msg": "FOOD is apple where IS_FRUIT [ True ]"
}

as expected when run.

Based on:

In YAML, how do I break a string over multiple lines?

I have then tried:

IS_FRUIT: >-
 {% if FOOD == "carrot" %}false
 {% elif FOOD == "apple" or FOOD == "banana" %}true
 {% endif %}

but that prints:

ok: [localhost] => {
    "msg": "FOOD is apple where IS_FRUIT [ False ]"
}

which is wrong. I found this post describing a similar issue: https://github.com/ansible/ansible/issues/18142

but it does not really provide a solution. Any suggestion on how to create multi-line boolean variables in Ansible?

Based on below answer I have also tried:

FOOD: apple

IS_FRUIT: |
 {% if FOOD == "carrot" %}false
 {% elif FOOD == "apple" or FOOD == "banana" %}true
 {% endif %}

but when I run that with the same task above I still get:

ok: [localhost] => {
    "msg": "FOOD is apple where IS_FRUIT False"
}

which is wrong.

Gitnik
  • 564
  • 7
  • 26
u123
  • 15,603
  • 58
  • 186
  • 303
  • 1
    Not sure if relevant but you should drop the surrounding `'`s in the `>-` version since they would become part of the content. – flyx Jan 28 '19 at 09:40
  • For the simpler case - just adding one new line - that fixed the error. But for multiple lines - see above updated example it still fails. – u123 Jan 28 '19 at 09:50

3 Answers3

5

Boring explanation about what happens

IS_FRUIT: >-
 {% if FOOD == "carrot" %}false
 {% elif FOOD == "apple" or FOOD == "banana" %}true
 {% endif %}

Mind that in YAML, a folded block scalar (starting with >) replaces single newlines with spaces (and multiple newlines with one fewer newline). So the code above is equivalent to

IS_FRUIT: >-
 {% if FOOD == "carrot" %}false {% elif FOOD == "apple" or FOOD == "banana" %}true {% endif %}

So after Jinja processing, you will get either false or true with a trailing space. YAML will not map those scalars to boolean values because of that trailing space.

Now I guess that Ansible sees it gets a non-boolean value, and it maps that non-boolean value to a boolean one in some way. And thus your scalar evaluates to False even if it is true with trailing space (markdown does not let me render this correctly).


Important part about how to fix it

You can use a literal block scalar instead (based on clockworknet's suggestion):

IS_FRUIT: |-
 {% if FOOD == "carrot" %}false
 {% elif FOOD == "apple" or FOOD == "banana" %}true
 {% endif %}

This works because now the newlines will not be transformed into spaces, but stay newlines instead. After Jinja processing, only one line remains, and the final newline character will get stripped away by the - in |-.

However, this does not help you if you have more complex expressions where you want to actually split the content over multiple lines. There is a better solution which is Jinja whitespace control:

IS_FRUIT: >-
 {% if FOOD == "carrot" %}false
 {%- elif FOOD == "apple" or FOOD == "banana" %}true
 {%- endif %}

The - on the Jinja tags tells Jinja to remove all whitespace before this tag, so this is basically equivalent to

IS_FRUIT: >-
 {% if FOOD == "carrot" %}false{% elif FOOD == "apple" or FOOD == "banana" %}true{% endif %}

You can also use it like this -%} to remove all whitespace after a tag. However, beware: Jinja processing takes place before YAML processing, so it may screw up your carefully written YAML indentation! For example, do not use {%- on the first Jinja tag in the block scalar, for Jinja would then place the text on the same line as |- which is invalid YAML! Same goes for the last tag and -%}.

flyx
  • 35,506
  • 7
  • 89
  • 126
1

Nice explanation of what's going on by @flyx.

I'd recommend to avoid using Jinja statements in your playbooks in favour of Jinja expressions.
Still statements are OK inside file templates.

You can write your vars definition as follows:

IS_FRUIT: >-
  {{ false if FOOD == 'carrot'
     else
     (
       true if FOOD in ['apple', 'banana']
       else 'unknown'
     )
  }}

In this case we do care only about last newline which is stripped away with >-.

Konstantin Suvorov
  • 65,183
  • 9
  • 162
  • 193
-1

This works for me:

IS_FRUIT: |
 {% if FOOD == "carrot" %}false
 {% elif FOOD == "apple" or FOOD == "banana" %}true
 {% endif %}

(change to the | symbol to use literal multiline behaviour)

clockworknet
  • 2,736
  • 1
  • 15
  • 19