Ansible include task only if file exists

2019-03-23 03:15发布

问题:

I'm trying to include a file only if it exists. This allows for custom "tasks/roles" between existing "tasks/roles" if needed by the user of my role. I found this:

- include: ...
  when: condition

But the Ansible docs state that:

"All the tasks get evaluated, but the conditional is applied to each and every task" - http://docs.ansible.com/playbooks_conditionals.html#applying-when-to-roles-and-includes

So

- stat: path=/home/user/optional/file.yml
  register: optional_file
- include: /home/user/optional/file.yml
  when: optional_file.stat.exists

Will fail if the file being included doesn't exist. I guess there might be another mechanism for allowing a user to add tasks to an existing recipe. I can't let the user to add a role after mine, because they wouldn't have control of the order: their role will be executed after mine.

回答1:

The with_first_found conditional can accomplish this without a stat or local_action. This conditional will go through a list of local files and execute the task with item set to the path of the first file that exists. Including skip: true on the with_first_found options will prevent it from failing if the file does not exist.

Example:

- hosts: localhost
  connection: local
  gather_facts: false

  tasks:
    - include: "{{ item }}"
      with_first_found:
        - files:
            - /home/user/optional/file.yml
          skip: true


回答2:

Thanks all for your help! I'm aswering my own question after finally trying all responses and my own question's code back in today's Ansible: ansible 2.0.1.0

My original code seems to work now, except the optional file I was looking was in my local machine, so I had to run stat through local_action and set become: no for that particular tasks, so ansible wouldn't attempt to do sudo in my local machine and error with: "sudo: a password is required\n"

- local_action: stat path=/home/user/optional/file.yml
  register: optional_file
  become: no
- include: /home/user/optional/file.yml
  when: optional_file.stat.exists


回答3:

I using something similar but for the file module and what did the trick for me is to check for the variable definition, try something like:

when: optional_file.stat.exists is defined and optional_file.stat.exists

the task will run only when the variable exists.



回答4:

I could spend time here to bash ansible's error handling provisions, but in short, you are right and you can't use stat module for this purpose due to stated reasons.

Simplest solution for most ansible problems is to do it outside ansible. E.g.

- shell: test -f /home/user/optional/file.yml     # or use -r if you're too particular.
  register: optional_file
  failed_when: False
- include: /home/user/optional/file.yml
  when: optional_file.rc == 0
- debug: msg="/home/user/optional/file.yml did not exist and was not included"
  when: optional_file.rc != 0

* failed_when added to avoid host getting excluded from further tasks when the file doesn't exist.



回答5:

Using ansible-2.1.0, I'm able to use snippets like this in my playbook:

- hosts: all
  gather_facts: false
  tasks:
    - name: Determine if local playbook exists
      local_action: stat path=local.yml
      register: st

- include: local.yml
  when: st.stat.exists

I get no errors/failures when local.yml does not exist, and the playbook is executed (as a playbook, meaning it starts with the hosts: line, etc.)

You can do the same at the task level instead with similar code. Using stat appears to work correctly.



回答6:

If I am not wrong, you want to continue the playbook even the when statement false?

If so, please add this line after when:

ignore_errors: True

So your tasks will be look like this:

- stat: path=/home/user/optional/file.yml
  register: optional_file
- include: /home/user/optional/file.yml
  when: optional_file.stat.exists
  ignore_errors: True

Please let me know, if I understand your question correctly, or can help further. Thanks



回答7:

The best option I have come up with so far is this:

- include: "{{ hook_variable | default(lookup('pipe', 'pwd') ~ '/hooks/empty.yml') }}"

It's not exactly an "if-exists", but it gives users of your role the same result. Create a few variables within your role and a default empty file. The Jinja filters "default" and "lookup" take care of falling back on the empty file in case the variable was not set.

For convenience, a user could use the {{ playbook_dir }} variable for setting the paths:

hook_variable: "{{ playbook_dir }}/hooks/tasks-file.yml" 


回答8:

There's also the option to use a Jinja2 filter for that:

 - set_fact: optional_file="/home/user/optional/file.yml"

 - include: ....
   when: optional_file|exists


标签: ansible