Search code examples
ansibleansible-2.xansible-roleansible-handlers

Ansible not detecting Role default variables in its handler


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


Solution

  • 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.