puravidaso
puravidaso

Reputation: 1223

A playbook with two roles: running role B complains with role A's code which successfully ran

I am experiencing a strange behavior: when I run role B, it complains role A's code which I can successfully run! I have reproduced this to this minimal example:

$ cat playbooka.yml 

- hosts:
    - host_a

  roles:
    - role: rolea
      tags:
        - taga
    - role: roleb
      tags:
        - tagb

I have tagged two roles because I want to selectively run role A or role B, they consist simple tasks as shown below in this minimal example:

$ cat roles/rolea/tasks/main.yml

- name: Get service_facts
  service_facts:

- debug:
    msg: '{{ ansible_facts.services["amazon-ssm-agent"]["state"] }}'

- when: ansible_facts.services["amazon-ssm-agent"]["state"] != "running"
  meta: end_play

$ cat roles/roleb/tasks/main.yml

- debug:
    msg: "I am roleb"

The preview confirms that I can run individual roles as specified by tags:

$ ansible-playbook playbooka.yml -t taga -D -C --list-hosts --list-tasks

playbook: playbooka.yml

  play #1 (host_a): host_a  TAGS: []
    pattern: ['host_a']
    hosts (1):
      3.11.111.4
    tasks:
      rolea : Get service_facts TAGS: [taga]
      debug TAGS: [taga]

$ ansible-playbook playbooka.yml -t tagb -D -C --list-hosts --list-tasks

playbook: playbooka.yml

  play #1 (host_a): host_a  TAGS: []
    pattern: ['host_a']
    hosts (1):
      3.11.111.4
    tasks:
      debug TAGS: [tagb]

I can run role A OK:

$ ansible-playbook playbooka.yml -t taga -D -C

PLAY [host_a] *************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************************************************************************************************************
ok: [3.11.111.4]

TASK [rolea : Get service_facts] ******************************************************************************************************************************************************************************************************************
ok: [3.11.111.4]

TASK [rolea : debug] ******************************************************************************************************************************************************************************************************************************
ok: [3.11.111.4] => {
    "msg": "running"
}

PLAY RECAP ****************************************************************************************************************************************************************************************************************************************
3.11.111.4               : ok=3    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   

But when I run role B, it complains the code in role A which I just successfully ran!

$ ansible-playbook playbooka.yml -t tagb -D -C

PLAY [host_a] *************************************************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ****************************************************************************************************************************************************************************************************************************
ok: [3.11.111.4]
ERROR! The conditional check 'ansible_facts.services["amazon-ssm-agent"]["state"] != "running"' failed. The error was: error while evaluating conditional (ansible_facts.services["amazon-ssm-agent"]["state"] != "running"): 'dict object' has no attribute 'services'

The error appears to be in '<path>/roles/rolea/tasks/main.yml': line 9, column 3, but may
be elsewhere in the file depending on the exact syntax problem.

The offending line appears to be:

- when: ansible_facts.services["amazon-ssm-agent"]["state"] != "running"
  ^ here
We could be wrong, but this one looks like it might be an issue with
unbalanced quotes. If starting a value with a quote, make sure the
line ends with the same set of quotes. For instance this arbitrary
example:

    foo: "bad" "wolf"

Could be written as:

    foo: '"bad" "wolf"'

I have two questions:

PS: I am using the latest Ansible 2.10.2 and latest python 3.9.1 locally on a MacOS. The remote python can be either 2.7.12 or 3.5.2 (Ubuntu 16_04). I worked around the problem by testing if the dictionary has the services key:

ansible_facts.services is not defined or ansible_facts.services["amazon-ssm-agent"]["state"] != "running"

but it still surprises me that role B will interpret role A's code and interpreted it incorrectly. Is this a bug that I should report?

Upvotes: 3

Views: 207

Answers (1)

Zeitounator
Zeitounator

Reputation: 44615

From the notes in meta module documentation:

Skipping meta tasks with tags is not supported before Ansible 2.11.

Since you run ansible 2.10, the when condition for your meta task in rolea is always evaluated, whatever tag you use. When you use -t tagb, ansible_facts.services["amazon-ssm-agent"] does not exist as you skipped service_facts, and you then get the error you reported.

You can either:

  • upgrade to ansible 2.11 (might be a little soon as I write this answer since it is not yet available over pip...)
  • rewrite your condition so that the meta task skips when the var does not exists e.g.
    when:
      - ansible_facts.services["amazon-ssm-agent"]["state"] is defined
      - ansible_facts.services["amazon-ssm-agent"]["state"] != "running"
    

The second solution is still a good practice IMO in whatever condition (e.g. share your work with someone running an older version, running accidentally against a host without the agent installed....).

One other possibility in your specific case is to move the service_facts tasks to an other role higher in play order, or in the pre_tasks section of your playbook, and tag it always. In this case the task will always play and the fact will always exists, whatever tag you use.

Upvotes: 3

Related Questions