问题
I'm trying to run a few Ansible commands depending on the outcome of a conditional check in my tasks based on the presence of a boolean and am tying myself up in knots trying to get it to work.
The desired outcome is as follows, and should be run for each item in the hosts
array:
- Check if the
lynchburg
variable istrue
orfalse
- If the
lynchburg
variable istrue
:- Setup the folder structure if it doesn't already exist
- Create
gulpfile.js
from the included template file if it doesn't already exist
- If the
lynchburg
variable isfalse
:- Don't setup the folder structure
- Don't create
gulpfile.js
- Regardless of whether the
lynchburg
istrue
orfalse
, skip all tasks on subsequent reprovisions, as the folder structure andgulpfile.js
will either be there or not (although this should be taken care of by the logic in the conditionals)
From the code included below, I'm seeing the following results:
- When
lynchburg
istrue
, the provision runs through as expected the first time around and any subsequent reprovisions also work as expected. - When
lynchburg
isfalse
, theinc
folder structure is created, howevergulpfile.js
isn't.
The main loop the task should run through is the following:
vhosts:
- site_name: sample
lynchburg: true
and the tasks are as follows:
# Create variable to check whether Lynchburg has already been installed
- name: Check if Lynchburg assets folders have been previously created
stat:
path: /var/www/{{ item.site_name }}/inc
with_items: "{{ vhosts }}"
when: item.lynchburg == true
register: lynchburg_assets
- name: Check if Lynchburg gulpfile.js has been previously created
stat:
path: /var/www/{{ item.site_name }}/gulpfile.js
with_items: "{{ vhosts }}"
when: item.lynchburg == true
register: lynchburg_gulpfile
- name: Create inc folder
with_items: "{{ lynchburg_assets.results }}"
file:
path: /var/www/{{ item.item.site_name }}/inc
state: directory
when: item.stat.isdir is undefined and item.item.lynchburg == true
- name: Create scss folder
with_items: "{{ lynchburg_assets.results }}"
file:
path: /var/www/{{ item.item.site_name }}/inc/scss
state: directory
when: item.stat.isdir is undefined and item.item.lynchburg == true
- name: Create js folder
with_items: "{{ lynchburg_assets.results }}"
file:
path: /var/www/{{ item.item.site_name }}/inc/js
state: directory
when: item.stat.isdir is undefined and item.item.lynchburg == true
- name: Create img folder
with_items: "{{ lynchburg_assets.results }}"
file:
path: /var/www/{{ item.item.site_name }}/inc/img
state: directory
when: item.stat.isdir is undefined and item.item.lynchburg == true
- name: Create fonts folder
with_items: "{{ lynchburg_assets.results }}"
file:
path: /var/www/{{ item.item.site_name }}/inc/fonts
state: directory
when: item.stat.isdir is undefined and item.item.lynchburg == true
- name: Create gulpfile.js
with_items: "{{ lynchburg_gulpfile.results }}"
template:
src: gulpfile.js
dest: /var/www/{{ item.item.site_name }}/gulpfile.js
when: item.stat.exists is defined and item.stat.exists == false and item.item.lynchburg == true
N.B. If there's any other way to create the full folder structure easily without running five different tasks - one for each folder/subfolder - and someone could advise what that is, that'd also be appreciated!
回答1:
When
lynchburg
isfalse
, theinc
folder structure is created, howevergulpfile.js
isn't.
I know how to solve it, I don't know (yet) how to explain it:
Change the order of expressions in the conditional:
when: item.item.lynchburg == true and item.stat.isdir is undefined
For some reason:
when: dummy.key is undefined and false
and
when: false and dummy.key is undefined
give different results.
It works as expected only when checking the top-level variable (dummy is undefined
).
回答2:
I seem to have fixed this in a moment of blind experimentation. I've removed the conditional check from the folder structure tasks, so it's just checking the raw vhosts
loop, rather than looping through them first and then running the tasks on the results of the loop.
My tasks now look as follows, which seem to work exactly as I'd hope they would:
---
- name: Create inc folder
with_items: "{{ vhosts }}"
file:
path: /var/www/{{ item.site_name }}/inc
state: directory
when: item.lynchburg == true
- name: Create scss folder
with_items: "{{ vhosts }}"
file:
path: /var/www/{{ item.site_name }}/inc/scss
state: directory
when: item.lynchburg == true
- name: Create js folder
with_items: "{{ vhosts }}"
file:
path: /var/www/{{ item.site_name }}/inc/js
state: directory
when: item.lynchburg == true
- name: Create img folder
with_items: "{{ vhosts }}"
file:
path: /var/www/{{ item.site_name }}/inc/img
state: directory
when: item.lynchburg == true
- name: Create fonts folder
with_items: "{{ vhosts }}"
file:
path: /var/www/{{ item.site_name }}/inc/fonts
state: directory
when: item.lynchburg == true
- name: Check if Lynchburg gulpfile.js has been previously created
stat:
path: /var/www/{{ item.site_name }}/gulpfile.js
with_items: "{{ vhosts }}"
when: item.lynchburg == true
register: lynchburg_gulpfile
- name: Create gulpfile.js
with_items: "{{ lynchburg_gulpfile.results }}"
template:
src: gulpfile.js
dest: /var/www/{{ item.item.site_name }}/gulpfile.js
when: item.stat.exists is defined and item.stat.exists == false and item.item.lynchburg == true
I'll leave it to the community to tell me if this is correct or not but, as I say, it seems to work perfectly.
回答3:
It's possible I'm missing something, but it looks to me like the stat
tasks and the conditional checks that go with them are unnecessary:
In the case of the folder structure, the first time the task runs, the directories will be created. But if it runs again on the same host, the tasks will be skipped, and you'll get output like this:
localhost : ok=3 changed=0 unreachable=0 failed=0
- In the case of the
template
task, you can set its force option tofalse
which means the task won't run if the file already exists.
Finally, your folder structure can be created fairly easily in one task by using the with_nested style of loop (Ansible < 2.5), or a lookup (Ansible ≥ 2.5).
I'd recreate your playbook to look something like this (note that it will create the file structure in its own directory):
---
- hosts: localhost
vars:
vhosts:
- site_name: sample
lynchburg: true
- site_name: sample2
lynchburg: false
tasks:
- name: Create required folder structure.
file:
path: "{{ playbook_dir }}/var/www/{{ item.0.site_name }}/inc/{{ item.1 }}"
state: directory
with_nested:
- "{{ vhosts }}"
- ["scss", "img", "js", "fonts"]
when: item.0.lynchburg
- name: Template gulpfile into place.
template:
src: "{{ playbook_dir }}/gulpfile.js.j2"
dest: "{{ playbook_dir }}/var/www/{{ item.site_name }}/gulpfile.js"
force: false
with_items: "{{ vhosts }}"
when: item.lynchburg
Running this the first time creates the following result and folder structure. Note that:
Ansible reports two changed tasks:
____________ < PLAY RECAP > ------------ \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || localhost : ok=3 changed=2 unreachable=0 failed=0
only the
sample
host directory and files are created.var └── www └── sample ├── gulpfile.js └── inc ├── fonts ├── img ├── js └── scss
If we run ansible-playbook playbook.yml
again:
- the folder and file structure is un-altered, and
Ansible reports zero changes:
____________ < PLAY RECAP > ------------ \ ^__^ \ (oo)\_______ (__)\ )\/\ ||----w | || || localhost : ok=3 changed=0 unreachable=0 failed=0
来源:https://stackoverflow.com/questions/42608284/ansible-2-conditionals-using-with-items-in-loop