<h5 dir="auto">ISSUE TYPE</h5>
<ul dir="auto">
<li>Bug Report</li>
</ul>
<h5 dir="auto">COMPONENT NAME</h5>
<p dir="auto">ansible-2.2.0.0</p>
<h5 dir="auto">ANSIBLE VERSION</h5>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="ansible-playbook 2.2.0.0
  config file = /home/ansible/ansible/ansible.cfg
  configured module search path = ['/usr/local/share/ansible/modules']"><pre class="notranslate"><code class="notranslate">ansible-playbook 2.2.0.0
  config file = /home/ansible/ansible/ansible.cfg
  configured module search path = ['/usr/local/share/ansible/modules']
</code></pre></div>
<h5 dir="auto">CONFIGURATION</h5>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="[defaults]
inventory      = /home/ansible/ansible/inventory/hosts
library        = /usr/local/share/ansible/modules
forks          = 20
roles_path    = /home/ansible/ansible/roles
remote_user = ansible
log_path = /home/ansible/ansible/ansible.log
hash_behaviour = merge
private_role_vars = no
private_key_file = /home/ansible/.ssh/id_rsa
vault_password_file = /home/ansible/ansible/secrets/vault-password-file
ansible_managed = Ansible managed: {file} on {host}
action_plugins     = /usr/local/share/ansible/plugins/action
callback_plugins   = /usr/local/share/ansible/plugins/callback
connection_plugins = /usr/local/share/ansible/plugins/connection
lookup_plugins     = /usr/local/share/ansible/plugins/lookup
vars_plugins       = /usr/local/share/ansible/plugins/vars
filter_plugins     = /usr/local/share/ansible/plugins/filter
test_plugins       = /usr/local/share/ansible/plugins/test
strategy_plugins   = /usr/local/share/ansible/plugins/strategy
[privilege_escalation]
[paramiko_connection]
[ssh_connection]
[accelerate]
[selinux]
[colors]
diff_remove = bright blue"><pre class="notranslate"><code class="notranslate">[defaults]
inventory      = /home/ansible/ansible/inventory/hosts
library        = /usr/local/share/ansible/modules
forks          = 20
roles_path    = /home/ansible/ansible/roles
remote_user = ansible
log_path = /home/ansible/ansible/ansible.log
hash_behaviour = merge
private_role_vars = no
private_key_file = /home/ansible/.ssh/id_rsa
vault_password_file = /home/ansible/ansible/secrets/vault-password-file
ansible_managed = Ansible managed: {file} on {host}
action_plugins     = /usr/local/share/ansible/plugins/action
callback_plugins   = /usr/local/share/ansible/plugins/callback
connection_plugins = /usr/local/share/ansible/plugins/connection
lookup_plugins     = /usr/local/share/ansible/plugins/lookup
vars_plugins       = /usr/local/share/ansible/plugins/vars
filter_plugins     = /usr/local/share/ansible/plugins/filter
test_plugins       = /usr/local/share/ansible/plugins/test
strategy_plugins   = /usr/local/share/ansible/plugins/strategy
[privilege_escalation]
[paramiko_connection]
[ssh_connection]
[accelerate]
[selinux]
[colors]
diff_remove = bright blue
</code></pre></div>
<h5 dir="auto">OS / ENVIRONMENT</h5>
<p dir="auto">Controller: ansible-2.2.0.0<br>
Controlled node: FreeBSD</p>
<h5 dir="auto">SUMMARY</h5>
<p dir="auto">I have a role, ossec_hids_agent, which has a dependency on a subordinate role, ossec_hids_common. The subordinate role has a dynamic task include and loads tasks from ossec_hids_agent space. I believe this is incorrect, a child role should not run tasks defined in it's parent.</p>
<p dir="auto">This behaviour changed in ansible-2.2.0.0<br>
ansible-2.1.3.0 has what I consider the correct behaviour</p>
<h5 dir="auto">STEPS TO REPRODUCE</h5>
<p dir="auto"><strong>roles/ossec_hids_agent/meta/main.yml</strong>:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="dependencies:
  - role: ossec_hids_common"><pre class="notranslate"><code class="notranslate">dependencies:
  - role: ossec_hids_common
</code></pre></div>
<p dir="auto"><strong>roles/ossec_hids_common_tasks/config.yml</strong> (included from main.yml):</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="- name: Include OS-specific config tasks.
  include: &quot;{{ item }}&quot;
  with_first_found:
    ...
    - &quot;./includes/{{ ansible_distribution }}-config.yml&quot;
    ..."><pre class="notranslate"><code class="notranslate">- name: Include OS-specific config tasks.
  include: "{{ item }}"
  with_first_found:
    ...
    - "./includes/{{ ansible_distribution }}-config.yml"
    ...
</code></pre></div>
<p dir="auto"><strong>ls roles/ossec_hids_agent/tasks/includes/</strong>:<br>
FreeBSD-config.yml</p>
<p dir="auto"><strong>ls roles/ossec_hids_common/tasks/includes/</strong>:<br>
</p>
<p dir="auto"><strong>playbooks/test/ossec_hids.yml</strong>:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="- hosts: ossec-agent
  roles:
  - role: ossec_hids_agent
    tags: [ &quot;ossec&quot;, &quot;ossec-agent&quot; ]"><pre class="notranslate"><code class="notranslate">- hosts: ossec-agent
  roles:
  - role: ossec_hids_agent
    tags: [ "ossec", "ossec-agent" ]
</code></pre></div>
<p dir="auto"><strong>playbook command</strong>:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="ansible-playbook playbooks/test/ossec_hids.yml -b -D -t ossec-agent"><pre class="notranslate"><code class="notranslate">ansible-playbook playbooks/test/ossec_hids.yml -b -D -t ossec-agent
</code></pre></div>
<p dir="auto">The intent is that on e.g. a FreeBSD node, if a file includes/FreeBSD-config.yml exists, it will be included and executed. I expect that ossec_hids_common will only include files defined in it's own role structure and not search through the parent's includes as well</p>
<h5 dir="auto">EXPECTED RESULTS</h5>
<p dir="auto">The include in ossec_hids_common should be a noop</p>
<h5 dir="auto">ACTUAL RESULTS</h5>
<p dir="auto"><strong>ansible-2.2.0.0</strong>:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="TASK [ossec_hids_common : Include OS-specific config tasks.] *******************
included: /home/alan/gitwork/ansible/roles/role_ossec_hids_agent/tasks/includes/FreeBSD-config.yml for freebsd-9-3, freebsd-10-2, freebsd-10-3"><pre class="notranslate"><code class="notranslate">TASK [ossec_hids_common : Include OS-specific config tasks.] *******************
included: /home/alan/gitwork/ansible/roles/role_ossec_hids_agent/tasks/includes/FreeBSD-config.yml for freebsd-9-3, freebsd-10-2, freebsd-10-3
</code></pre></div>
<p dir="auto">Clearly, the child role is including files defined in it's parent.</p>
<p dir="auto"><strong>ansible-2.1.3.0</strong></p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="TASK [ossec_hids_common : Include OS-specific config tasks.] *******************
TASK [ossec_hids_common : Enable ossec_hids_common services] *******************"><pre class="notranslate"><code class="notranslate">TASK [ossec_hids_common : Include OS-specific config tasks.] *******************
TASK [ossec_hids_common : Enable ossec_hids_common services] *******************
</code></pre></div>
<p dir="auto">With this version the operation is a noop</p>
<h5 dir="auto">ISSUE TYPE</h5>

<ul dir="auto">
<li>Bug Report</li>
</ul>
<h5 dir="auto">COMPONENT NAME</h5>
<p dir="auto">roles</p>

<h5 dir="auto">ANSIBLE VERSION</h5>

<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="ansible 2.2.1.0
  config file = 
  configured module search path = Default w/o overrides"><pre class="notranslate"><code class="notranslate">ansible 2.2.1.0
  config file = 
  configured module search path = Default w/o overrides
</code></pre></div>
<h5 dir="auto">CONFIGURATION</h5>
<p dir="auto">N/A</p>
<h5 dir="auto">OS / ENVIRONMENT</h5>
<p dir="auto">N/A</p>
<h5 dir="auto">SUMMARY</h5>
<p dir="auto">We include <strong>role3</strong> from both <strong>role1</strong> and <strong>role2</strong> in <code class="notranslate">meta/main.yml</code> using <code class="notranslate">dependencies:</code> keyword.  These roles both pass the same parameters (that is: none directly, only via extra arguments) to <strong>role3</strong>, but it's (wrongly) running twice, merely because the depending role has a condition on itself.</p>
<p dir="auto">We have the condition because we want to run <strong>role2</strong> only sometimes, so we put a <code class="notranslate">when:</code> on the <code class="notranslate">role:</code> in its dependency declaration.  This seems to work fine as far as only including <strong>role2</strong> when it should be.  The condition is properly checked, and properly includes the role only if true.</p>
<p dir="auto">However, including any condition at all results in the dependency running twice, even if the condition is always true (i.e. <code class="notranslate">when: true</code>).  This is <em>not</em> a different parameter or value, so the role calls are 100% identical, and <code class="notranslate">allow_duplicates</code> is <em>false</em>, yet it runs twice.</p>
<h5 dir="auto">STEPS TO REPRODUCE</h5>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content=" $ tail test.yml roles/**/*.yml
==&gt; test.yml &lt;==
- hosts: all
  gather_facts: false
  roles:
    - role1
    - role2

==&gt; roles/role1/meta/main.yml &lt;==
dependencies:
  - role: role3

==&gt; roles/role1/tasks/main.yml &lt;==
- set_fact: dummy

==&gt; roles/role2/meta/main.yml &lt;==
dependencies:
  - role: role3
    when: true

==&gt; roles/role2/tasks/main.yml &lt;==
- set_fact: dummy

==&gt; roles/role3/tasks/main.yml &lt;==
- set_fact: dummy"><pre class="notranslate"><code class="notranslate"> $ tail test.yml roles/**/*.yml
==&gt; test.yml &lt;==
- hosts: all
  gather_facts: false
  roles:
    - role1
    - role2

==&gt; roles/role1/meta/main.yml &lt;==
dependencies:
  - role: role3

==&gt; roles/role1/tasks/main.yml &lt;==
- set_fact: dummy

==&gt; roles/role2/meta/main.yml &lt;==
dependencies:
  - role: role3
    when: true

==&gt; roles/role2/tasks/main.yml &lt;==
- set_fact: dummy

==&gt; roles/role3/tasks/main.yml &lt;==
- set_fact: dummy
</code></pre></div>

<h5 dir="auto">EXPECTED RESULTS</h5>
<p dir="auto">role3 should be run once only.  it's run once until adding <code class="notranslate">when: true</code> to the dependency.  then it runs twice, and that's a bug.  the duplicate run check should be discerning whether a role with that name and paramaters has run already, without any knowledge of when it's supposed to run.  only if it has.</p>
<h5 dir="auto">ACTUAL RESULTS</h5>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="$ ansible-playbook -c local -i localhost, test.yml

PLAY [all] *********************************************************************

TASK [role3 : set_fact] ********************************************************
ok: [localhost]

TASK [role1 : set_fact] ********************************************************
ok: [localhost]

TASK [role3 : set_fact] ********************************************************
ok: [localhost]

TASK [role2 : set_fact] ********************************************************
ok: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=4    changed=0    unreachable=0    failed=0"><pre class="notranslate"><code class="notranslate">$ ansible-playbook -c local -i localhost, test.yml

PLAY [all] *********************************************************************

TASK [role3 : set_fact] ********************************************************
ok: [localhost]

TASK [role1 : set_fact] ********************************************************
ok: [localhost]

TASK [role3 : set_fact] ********************************************************
ok: [localhost]

TASK [role2 : set_fact] ********************************************************
ok: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=4    changed=0    unreachable=0    failed=0
</code></pre></div>
<p dir="auto">now watch when removing the <code class="notranslate">when: true</code> condition on the role dependency:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content=" $ sed -i /when:.true/d roles/role2/meta/main.yml                                                                                                             

 $ ansible-playbook -c local -i localhost, test.yml

PLAY [all] *********************************************************************

TASK [role3 : set_fact] ********************************************************
ok: [localhost]

TASK [role1 : set_fact] ********************************************************
ok: [localhost]

TASK [role2 : set_fact] ********************************************************
ok: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0"><pre class="notranslate"><code class="notranslate"> $ sed -i /when:.true/d roles/role2/meta/main.yml                                                                                                             

 $ ansible-playbook -c local -i localhost, test.yml

PLAY [all] *********************************************************************

TASK [role3 : set_fact] ********************************************************
ok: [localhost]

TASK [role1 : set_fact] ********************************************************
ok: [localhost]

TASK [role2 : set_fact] ********************************************************
ok: [localhost]

PLAY RECAP *********************************************************************
localhost                  : ok=3    changed=0    unreachable=0    failed=0
</code></pre></div>
<p dir="auto">The condition shouldn't matter, it's whether the role actually has been run or not already, and if it's the same role, and the same parameters.  "when" is not a parameter, it's a control.  Note that I tried altering <code class="notranslate">become_user:</code> instead of <code class="notranslate">when:</code> to see if that would also cause a second run, and that one works correctly: it only runs once (in that case, you might argue that it actually has a different parameter, but I don't see any way to argue that for <code class="notranslate">when:</code>).</p>