Aleritty
Aleritty

Reputation: 25

Ansible: how to include a task file specified by an environment variable

I have a folder containing a list of named tasks-files and want to include them based on a specific selector.

Eg.

inventory_dir
| stacks
  | tasks
    | app1.yml
    | app2.yml
    | app3.yml

The Selector can be a list, and can contain elements that doesn't have a taskfile.

I'm trying different approaches to reach my goal, but

- name: tentative 1
  block:
  - name: Tasks to execute always
    include_tasks: ./generic-tasks.yml
  - name: Include specific tasks for specific selector
    include_tasks:
      file: "{{ inventory_dir }}/stacks/tasks/{{ item }}.yml"
    loop: 
    - '{{ SELECTOR }}'

Here I cannot use a stat task, because I don't know in advance what is the value of {{ item }} (and I cannot loop over a block). So this will fail in case the selector doesn't have a taskfile.

To avoid this I tried to do the contrary: looping over the folder content, and filtering by selector.

  - name: Include specific tasks for specific selector
    include_tasks:
      file: "{{ item }}"
    loop: "{{ lookup('fileglob', '{{ inventory_dir }}/stacks/tasks/*.yml',wantlist=True) }}"
    when: "item|regex_replace("(?:\/[ \w]+){1,}\/([ \w]+)(?:\.yml)", "\1") in SELECTOR"

But it fail, probably because of the "in" check failing. (The regexp is working correctly anyway).

Do you have a better strategy?

Upvotes: 0

Views: 970

Answers (2)

Zeitounator
Zeitounator

Reputation: 44799

You can use the find module and put it inside a block. This will fit on a single include level.

- hosts: localhost
  gather_facts: false

  vars:
    include_file_list: >
      {{ (SELECTOR | d([], true)) | map('regex_replace', '^(.*)$', '\1.yml') }}

  tasks:
    - when: include_file_list | length > 0
      block:

        - name: check which files exist based on selector
          find:
            paths:
              - '{{ inventory_dir }}'
            patterns: "{{ include_file_list }}"
          register: check_task_files

        - name: include existing task files
          include_tasks: "{{ item.path }}"
          loop: "{{ check_task_files.files }}"

Meanwhile, note that this will basically only succeed when targeting localhost as find runs on the target host. If you ever run this on a remote target you might get errors because either:

  • the inventory dir does not exist on the target
  • a file found on the target does not exist on the controller

So although a bit more complicated to read/understand, the following which will only look for files on the controller using the fileglob lookup is more appropriate.

The template expression to aggregate found files is needed because python glob does not understand the curly braces notation and we need to expand and search for each possible path ourselves. (i.e. /some/path/{a,b,c}.yml will not return any matches)

- hosts: all
  gather_facts: false

  vars:
    include_file_list: >
      {{ (SELECTOR | d([], true)) | map('regex_replace', '^(.*)$', inventory_dir ~ '/stacks/tasks/\1.yml') }}
    found_files: >
      {%- set found = [] -%}
      {%- for path in include_file_list -%}
      {{ found.append(q('fileglob', path)) }}
      {%- endfor -%}
      {{ found | flatten}}

  tasks:
    - name: include existing files
      include_tasks: "{{ item }}"
      loop: "{{ found_files }}"

Upvotes: 0

Aleritty
Aleritty

Reputation: 25

I found a solution, using a separate task-file.

main.yml

- name: tentative 1
  block:
  - name: Tasks to execute always
    include_tasks: ./generic-tasks.yml
  - name: Include specific tasks for specific selector
    include_tasks:
      file: "specific-tasks.yml"
    loop: 
    - '{{ SELECTOR }}'

specific-tasks.yml

  - name: Check for file existence
    stat:
      path: "{{ inventory_dir }}/stacks/tasks/{{item}}.yml"
    register: "taskfile"
    delegate_to: localhost
    become: false

  - name: Include tasks specific to this stack
    include_tasks:
      file: "{{ inventory_dir }}/stacks/tasks/{{ item }}.yml"
    when: "taskfile.stat.exists"

  - name: Unregister taskfile
    set_fact:
      taskfile: ''

I'm still posting this in case there are better strategies

Upvotes: 0

Related Questions