4

Does ansible pass Role Default variables to the Handlers within the same Role?

Here's a minimal excerpt of the playbook that has the issue:

Role hierarchy

- playbook.yml
- roles/
  - gunicorn/
    - defaults/
      - main.yml
    - handlers/
      - main.yml
  - code-checkout/
    - tasks/
      - main.yml

Here's the file contents

gunicorn/defaults/main.yml

---
gu_log: "/tmp/gunicorn.log"

gunicorn/handlers/main.yml

---
- name: Clear Gunicorn Log
  shell: rm {{ gu_log }}

finalize/tasks/main.yml

---
- name: Test Handlers
  shell: ls
  notify:
    - Restart Gunicorn

playbook.yml

---
  - name: Deploy
    hosts: webservers
    tasks:
      - include: roles/finalize/tasks/main.yml
    handlers:
      - include: roles/gunicorn/handlers/main.yml

AFAIK everything looks good. However, I get this error during the playbook execution

FAILED! => {"failed": true, "msg": "the field 'args' has an invalid value, which appears to include a variable that is undefined. The error was: 'gu_log' is undefined\n\nThe error appears to have been in '/roles/gunicorn/handlers/main.yml': line 3, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n\n- name: Restart Gunicorn\n ^ here\n"}

Using Ansible 2.2 on Ubuntu 12.04 LTS

Here's a modified version of techraf's script that creates all the directories and demonstrates my issue

#!/bin/bash

mkdir -p ./rtindru-test/roles/gunicorn
mkdir -p ./rtindru-test/roles/gunicorn/defaults
mkdir -p ./rtindru-test/roles/gunicorn/handlers
mkdir -p ./rtindru-test/roles/finalize/tasks

cat >./rtindru-test/roles/finalize/tasks/main.yml <<HANDLERS_END
---
- name: Test Handlers
  shell: rm {{ gu_log }}
HANDLERS_END

cat >./rtindru-test/roles/gunicorn/handlers/main.yml <<HANDLERS_END
---
- name: Clear Gunicorn Log
  shell: rm {{ gu_log }}
HANDLERS_END

cat >./rtindru-test/roles/gunicorn/defaults/main.yml <<DEFAULTS_END
---
gu_log: "/tmp/gunicorn.log"
DEFAULTS_END

cat >./rtindru-test/playbook.yml <<PLAYBOOK_END
---
  - name: Deploy
    hosts: localhost
    tasks:
      - include: roles/finalize/tasks/main.yml
    handlers:
      - include: roles/gunicorn/handlers/main.yml
PLAYBOOK_END

touch /tmp/gunicorn.log
ls -l /tmp/gunicorn.log
ansible-playbook ./rtindru-test/playbook.yml
ls -l /tmp/gunicorn.log

Output

PLAY [Deploy]


TASK [setup] ******************************************************************* ok: [localhost]

TASK [Test Handlers] *********************************************************** fatal: [localhost]: FAILED! => {"failed": true, "msg": "the field 'args' has an invalid value, which appears to include a variable that is undefined. The error was: 'gu_log' is undefined\n\nThe error appears to have been in '/rtindru-test/roles/finalize/tasks/main.yml': line 2, column 3, but may\nbe elsewhere in the file depending on the exact syntax problem.\n\nThe offending line appears to be:\n\n---\n- name: Test Handlers\n ^ here\n"} to retry, use: --limit @/rtindru-test/playbook.retry

PLAY RECAP ********************************************************************* localhost : ok=1 changed=0 unreachable=0
failed=1

techraf
  • 64,883
  • 27
  • 193
  • 198
rtindru
  • 5,107
  • 9
  • 41
  • 59
  • @techraf Ansible roles are intended to be fairly independent - and this is my "Complete" role. What more information would you need to respond? – rtindru Dec 22 '16 at 16:31
  • try `gu_log = "/tmp/gunicorn.log"` just a guess – qwazer Dec 22 '16 at 17:11
  • your inventory and/or playbook is missing – Jojo Dec 22 '16 at 17:24
  • Now that you included the missing parts, it is clear you are neither defining nor using any [role](https://docs.ansible.com/ansible/playbooks_roles.html#roles). You are just including a few files from subdirectories which you named to mimic the directory names inside a role. The answer as I posted before is even more valid - notice `roles:` inside the playbook. – techraf Dec 23 '16 at 07:25
  • @techraf I've added these comments to your response below - pasting here for your response: – rtindru Dec 23 '16 at 07:32
  • @techraf Thanks! Two quick followup questions: 1. Is it recommended to include roles the way you have? What if one of my roles is purely a handler as is the case with the gunicorn role 2. Is it necessary to include a role/task explicitly for the variables to be available to the it's handler? – rtindru Dec 23 '16 at 07:32
  • 1
    I already replied in the answer - if you want to use roles, you need to use roles. You can't include one file and expect that the functionality of roles magically appears. I don't understand what you want to achieve by copying the code from the answer to the question, modifying the code to break it, and claiming it does not work. Yes, if you break the code, it does not work. – techraf Dec 23 '16 at 07:36
  • @techraf Got it thanks. However my question about a "role that is purely a handler" remains. My guniorn role presently only does the log clear. How should I include this in my `playbook.yml`. Should gunicorn be included under roles and the handler will fire *automagically* when other roles notify OR do I need to include it both under roles and also mention the `gunicorn/handlers/main.yml` explicitly under handlers? – rtindru Dec 23 '16 at 07:39
  • @techraf Also re your comment about copying your code and breaking it - the intention is to show what my playbook looked like. I realize I was using roles incorrectly - appreciate your help. – rtindru Dec 23 '16 at 07:42
  • Please read what [handlers](https://docs.ansible.com/ansible/playbooks_intro.html#handlers-running-operations-on-change) are. – techraf Dec 23 '16 at 07:42
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/131324/discussion-between-rtindru-and-techraf). – rtindru Dec 23 '16 at 07:44

1 Answers1

2

You are neither defining nor using any roles. With the following task:

- include: roles/finalize/tasks/main.yml

you are only including a tasks file into your playbook. It has nothing to do with roles.

To assign a role you should specify a list of roles for a play (one or more):

role:
  - my_role1
  - my_role2

Please have a look at the documentation on roles and feel free to use the playbook and structure as created by the below script.

Does ansible pass Role Default variables to the Handlers within the same Role?

Yes it does.

For a proof run the following bash script which creates and runs a minimal example. It takes the contents of gunicorn/defaults/main.yml and gunicorn/handlers/main.yml from the question intact and adds missing components: the tasks and the playbook. It creates a file to be removed and runs the playbook.

#!/bin/bash

mkdir -p ./so41285033/roles/gunicorn
mkdir -p ./so41285033/roles/gunicorn/defaults
mkdir -p ./so41285033/roles/gunicorn/handlers
mkdir -p ./so41285033/roles/gunicorn/tasks

cat >./so41285033/roles/gunicorn/tasks/main.yml <<TASKS_END
---
- debug:
  changed_when: true
  notify: Clear Gunicorn Log
TASKS_END

cat >./so41285033/roles/gunicorn/handlers/main.yml <<HANDLERS_END
---
- name: Clear Gunicorn Log
  shell: rm {{ gu_log }}
  when: "'apiservers' not in group_names"
HANDLERS_END

cat >./so41285033/roles/gunicorn/defaults/main.yml <<DEFAULTS_END
---
gu_log: "/tmp/gunicorn.log"
DEFAULTS_END

cat >./so41285033/playbook.yml <<PLAYBOOK_END
---
- hosts: localhost
  gather_facts: no
  connection: local
  roles:
    - gunicorn
PLAYBOOK_END

touch /tmp/gunicorn.log
ls -l /tmp/gunicorn.log
ansible-playbook ./so41285033/playbook.yml
ls -l /tmp/gunicorn.log

The result:

-rw-r--r--  1 techraf  wheel  0 Dec 23 07:57 /tmp/gunicorn.log
 [WARNING]: Host file not found: /etc/ansible/hosts

 [WARNING]: provided hosts list is empty, only localhost is available


PLAY [localhost] ***************************************************************

TASK [gunicorn : debug] ********************************************************
ok: [localhost] => {
    "msg": "Hello world!"
}

RUNNING HANDLER [gunicorn : Clear Gunicorn Log] ********************************
changed: [localhost]
 [WARNING]: Consider using file module with state=absent rather than running rm


PLAY RECAP *********************************************************************
localhost                  : ok=2    changed=2    unreachable=0    failed=0

ls: /tmp/gunicorn.log: No such file or directory

Interpretation:

  • Before running the playbook the file /tmp/gunicorn.log was created and its existence verified:

    -rw-r--r--  1 techraf  wheel  0 Dec 23 07:57 /tmp/gunicorn.log
    
  • After running the playbook the file /tmp/gunicorn.log does not exist:

    ls: /tmp/gunicorn.log: No such file or directory
    
  • Ansible correctly passed the variable gu_log value to the Clear Gunicorn Log handler which removed the file.

Final remark:

The problem described in question is impossible to reproduce, because the question does not contain complete nor verifiable example in the meaning of MCVE.

techraf
  • 64,883
  • 27
  • 193
  • 198
  • Thanks a bunch! I understand your point about the question being incomplete - I've added details based on your comments. One point of difference I notice immediately is that my playbook includes individual tasks instead of roles. Another difference is that gunicorn (which has no "tasks/main.yml") is included ONLY in the handlers section (not in tasks or roles). – rtindru Dec 23 '16 at 07:31
  • Two quick followup questions: 1. Is it recommended to include roles the way you have? What if one of my roles is purely a handler as is the case with the gunicorn role 2. Is it necessary to include a role/task explicitly for the variables to be available to the it's handler? – rtindru Dec 23 '16 at 07:31