Execute regular expression within Ansible playbook

61 Views Asked by At

I am writing an Ansible play that iterates over a list of dictionaries to ensure that all files extensions are of either .txt or .csv format.

My current taks is:

- name: validate file extension
  assert:
    that:
      - item | regex_search('.*(csv|txt)$') is match
    fail_msg: "file with invalid extension in my_dicts"
  loop: "{{ my_dicts | map(attribute='file_name') | flatten }}"
  loop_control:
    label: "{{ item }}"

A sample input that would be valid and pass is:

my_dicts:
  - file_name:
    - foo.txt
    - bar.csv
  - file_name:
    - test.txt
    - helloWorld.csv

My issue is that other file extensions are also passing my regex when I need it to throw an error.
An example would be the following:

my_dicts:
  - file_name:
    - foo.bat
    - bar.exe

Which returns:

{
  "results": [
    {
      "ansible_loop_var": "item",
      "changed": false,
      "failed": false,
      "item": "foo.bat",
      "msg": "All assertions passed."
    },  
    {
      "ansible_loop_var": "item",
      "changed": false,
      "failed": false,
      "item": "bar.exe",
      "msg": "All assertions passed."
    }
  ]
}

Any insight as to why my regex isn't failing would be greatly appreciated!

1

There are 1 best solutions below

0
β.εηοιτ.βε On BEST ANSWER

You are mixing a test and a filter, the two accepting a regular expression, indeed.
So, since you are using the assert module, what you want is indeed a test, match and you will need to pass you regular expression to the test itself:

- name: validate file extension
  assert:
    that:
      - item is match('.*(csv|txt)$') 
    fail_msg: "file with invalid extension in my_dicts"
  loop: "{{ my_dicts | map(attribute='file_name') | flatten }}"
  loop_control:
    label: "{{ item }}"

This said, you could also do the same without the annoying verbosity of the loop, thanks to the reject filter that can apply a test to all the elements of a list:

- name: validate file extension
  assert:
    that:
      - _erroneous | length == 0
    fail_msg: "Some items are erroneous: {{ _erroneous }}"
  vars:
    _erroneous: >-
      {{
        my_dicts
          | map(attribute='file_name')
          | flatten
          | reject('match', '.*(csv|txt)$')
      }}

Which would give an error like:

fatal: [localhost]: FAILED! => changed=false 
  assertion: _erroneous | length == 0
  evaluated_to: false
  msg: 'Some items are erroneous: [''foo.exe'', ''bar.bat'']'

So, given:

- name: validate file extension
  assert:
    that: item is match('.*(csv|txt)$')
    fail_msg: "file with invalid extension in my_dicts"
  loop: "{{ my_dicts | map(attribute='file_name') | flatten }}"
  loop_control:
    label: "{{ item }}"
  vars:
    my_dicts:
      - file_name:
        - foo.csv
        - bar.txt
        - baz.exe

This yields:

ok: [localhost] => (item=foo.csv) => changed=false 
  ansible_loop_var: item
  item: foo.csv
  msg: All assertions passed
ok: [localhost] => (item=bar.txt) => changed=false 
  ansible_loop_var: item
  item: bar.txt
  msg: All assertions passed
failed: [localhost] (item=baz.exe) => changed=false 
  ansible_loop_var: item
  assertion: item is match('.*(csv|txt)$')
  evaluated_to: false
  item: baz.exe
  msg: Assertion failed

And with:

- name: validate file extension
  assert:
    that:
      - _erroneous | length == 0
    fail_msg: "Some items are erroneous: {{ _erroneous }}"
  vars:
    _erroneous: >-
      {{
        my_dicts
          | map(attribute='file_name')
          | flatten
          | reject('match', '.*(csv|txt)$')
      }}
    my_dicts:
      - file_name:
        - foo.csv
        - bar.txt
      - file_name:
        - baz.exe
        - qux.bat

It would yield:

fatal: [localhost]: FAILED! => changed=false 
  assertion: _erroneous | length == 0
  evaluated_to: false
  msg: 'Some items are erroneous: [''baz.exe'', ''qux.bat'']'