3

I have this piece of code that checks if the variable "owner" matches with the following regex and accepts that it is undefined, that is, the playbook can work without that variable being passed.

varexample is undefined or varexample is match('^[a-zA-Z]+$')

What I would like to do is that this variable accepts empty or null values. What I tried is something like this

varexample is null or varexample is match('^[a-zA-Z]+$')

But I got this error:

The conditional check 'varexample is null or varexample is match('[a-zA-Z]+')' failed. The error was: template error while templating string: no test named 'null'. String: {% if varexample is null or varexample is match('[a-zA-Z]+') %} True {% else %} False {% endif %}

Can someone give me a hint or some help?

2
  • accepts empty then just match('^[a-zA-Z]*$') ? Commented Jan 4, 2022 at 21:01
  • null is not a thing in Python (language behind Ansible and Jinja) None is, on the other hand. Commented Jan 4, 2022 at 21:25

4 Answers 4

3

Update:

Q: "Test the variable owner matches the following regex and accepts that it is undefined, empty or null value."

A: Use the filter default

owner | d('', true) is match('^[a-zA-Z]+$')

The 2nd argument true (default=false) is needed to convert null to an empty string (that doesn't match the regex). Quoting:

"If you want to use default with variables that evaluate to false you have to set the second parameter to true."

As a result:

  • Undefined owner converts to an empty string and match returns false
  • Defined no value owner: converts to an empty string and match returnes false
  • Defined value owner: value returns the match result

Details:

Q: "variable accepts null values"

A: It's a bug. Ansible shouldn't match null to '^[a-zA-Z]+$'

    - set_fact:
        varexample:
    - debug:
        var: varexample
    - debug:
        msg: "undefined: {{ varexample is undefined }}"
    - debug:
        msg: "match: {{ varexample is match('^[a-zA-Z]+$') }}"

gives

  varexample: null
  msg: 'undefined: False'
  msg: 'match: True'

As a result of this bug, your condition should be working as expected

  varexample is undefined or varexample is match('^[a-zA-Z]+$')

To be on the save side, for the case the bug will be fixed, you can additionally test None, e.g.

    - debug:
        msg: "Test passed. varexample: {{ item.varexample }}"
      when: item.varexample is undefined or
            item.varexample == None or
            item.varexample is match('^[a-zA-Z]+$')
      loop:
        - varexample: ABC
        - varexample: 123
        - varexample:

gives

ok: [localhost] => (item={'varexample': 'ABC'}) => 
  msg: 'Test passed. varexample: ABC'
skipping: [localhost] => (item={'varexample': 123}) 
ok: [localhost] => (item={'varexample': None}) => 
  msg: 'Test passed. varexample: '

Details

    - debug:
        msg: |
          Undefined: {{ item.varexample is undefined }}
          Is None: {{ item.varexample == None }}
          Match a-zA-Z: {{ item.varexample is match('^[a-zA-Z]+$') }}
      loop:
        - varexample: ABC
        - varexample: 123
        - varexample:
ok: [localhost] => (item={'varexample': 'ABC'}) => 
  msg: |-
    Undefined: False
    Is None: False
    Match a-zA-Z: True
ok: [localhost] => (item={'varexample': 123}) => 
  msg: |-
    Undefined: False
    Is None: False
    Match a-zA-Z: False
ok: [localhost] => (item={'varexample': None}) => 
  msg: |-
    Undefined: False
    Is None: True
    Match a-zA-Z: True
Sign up to request clarification or add additional context in comments.

2 Comments

Indeed, {{ varexample is match('None') }}" returns true as well. This happens in ansible 2.18, still. That's a pretty nasty bug, are you aware of a github issue about it? I couldn't find one.
I haven't searched for such an issue. I don't use None in the tests. Instead, I use default. For example, the workaround is {{ varexample | d('', true) is match('^[a-zA-Z]+$') }}. The 2nd argument true is needed. Otherwise, default works only on undefined variables.
0

Simply use a default that your match would consider truthy.

For example; with a varexample | default('a') is match('^[a-zA-Z]+$'), you should be able to achieve what you are looking for.

Given the tasks:

- debug:
    msg: "for {{ item }} the result is {{ item | default('a') is match('[a-zA-Z]+') }}"
  loop: "{{ cases }}"
  vars:
    cases:
      - ~
      - 123
      - abc
      - 

- debug:
    msg: "for an undefined variable the result is {{ item | default('a') is match('[a-zA-Z]+') }}"

This yields:

TASK [debug] ****************************************************************************
ok: [localhost] => (item=None) => 
  msg: for  the result is True
ok: [localhost] => (item=123) => 
  msg: for 123 the result is False
ok: [localhost] => (item=abc) => 
  msg: for abc the result is True
ok: [localhost] => (item=None) => 
  msg: for  the result is True

TASK [debug] ****************************************************************************
ok: [localhost] => 
  msg: for an undefined variable the result is True

Comments

0

I think the closest answer to exactly what you asked for is something like:

varexample is defined and varexample is match('^[a-zA-Z]+$')

You can also use "is not defined", or just providing a default like mentioned in the other answer is another good way to go.

varexample|default('') is match('^[a-zA-Z]+$')

Comments

0

Regex matches because var is converted to a string, and since var is undefined the string value will be 'None'. It's easy to check: the following match will return true in case of var is undefined: var is match('^None$').

So my solution for this problem is:

(var | length > 0) if ((var | type_debug) == 'AnsibleUnicode') else false

(var | type_debug) == 'AnsibleUnicode' - checks if type of variable is string and in case it is string it returns true in case string has length not equal to 0: var | length > 0.

Here are the tasks to reproduce what was said above:

- name: Result should be true, since var is interpollated to 'None'
  vars:
    var:
  debug:
    msg: |
      {{ var is match('^None$') }}
- name: Best match that works for all cases and checks if variable is notempty string
  vars:
    var1:
    var2: ""
    var3: "non empty string"
  debug:
    msg: |
      Should be False:
      {{ (var1 | length > 0) if ((var1 | type_debug) == 'AnsibleUnicode') else false }}
      Should be False:
      {{ (var2 | length > 0) if ((var2 | type_debug) == 'AnsibleUnicode') else false }}
      Should be True:
      {{ (var3 | length > 0) if ((var3 | type_debug) == 'AnsibleUnicode') else false }}

Result is the following:

TASK [Result should be true, since var is interpollated to 'None'] **************************************************************************************************************************
ok: [localhost] => 
  msg: true

TASK [Best match that works for all cases and checks if variable is notempty string] ********************************************************************************************************
ok: [localhost] => 
  msg: |-
    Should be False:
    False
    Should be False:
    False
    Should be True:
    True

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.