ansiblejinja2

Why does `default( None )` not always appear to produce `None`


Is there a logical reasoning that explains why the two debug tasks in the Ansible playbook below respectively output "NONE" and "FALSE" and not both "NONE"?

- hosts:  'all'

  tasks:

    - debug:
        msg: '{{ foo | default( None ) | ternary( "TRUE", "FALSE", "NONE" ) }}'

    - debug:
        msg: '{{ bar | ternary( "TRUE", "FALSE", "NONE" ) }}'

  vars:

    bar: '{{ foo | default( None ) }}'

I observed this with ansible@2.8.5 btw


[edit@2019-10-16]

As inferred from @VladimirBotka's answer, one could say the real "problem" is that None isn't transitive with respect to substitution. My use of default(...) was needlessly complicating matters and the example can be simplified to:

- hosts: 'all'
  tasks:
    - debug:
        msg: '{{ None | ternary( "TRUE", "FALSE", "NONE" ) }}'

    - debug:
        msg: '{{ bar | ternary( "TRUE", "FALSE", "NONE" ) }}'

  vars:
    bar: '{{ None }}'

... and still produce the exact same output.


Solution


  • Note: All Jinja expressions closed in double braces {{ expression }} evaluate to strings.

    Given

      bar: "{{ foo | default(None) }}"
    

    the task

      - debug:
          msg: bar is empty string
        when: bar | length == 0
    

    gives

      msg: bar is empty string
    

    But the task

      - debug:
          msg: foo is empty string
        when: foo | default(None) | length == 0
    

    fails with the error:

    "... object of type 'NoneType' has no len() ... "

    As a result in ternary, an empty string selects FALSE and null selects NONE.


    Notes:

    1. See the Note in Omitting Parameters

    2. Both null and empty string "" select FALSE in ternary if the third parameter is omitted. The tasks

      - debug:
          msg: "{{ bar | ternary('TRUE', 'FALSE') }}"
      - debug:
          msg: "{{ foo | default(None) | ternary('TRUE', 'FALSE') }}"
    

    give

      msg: FALSE
      msg: FALSE