What is the order of role-specific vs. imported handlers and can it be changed?

Hello,

our handlers are mostly specified in the role itself (like roles/server/handlers/main.yml), but some handlers, which are required in many roles, are specified in a central “library” role (roles/library/handlers/main.yml) and imported into the main playbook (site.yml) in the block, which also runs the role like this

- hosts: all_servers
  name:  "====== SERVER CONFIGURATION ======"
  roles:
    - server
  handlers:
    - ansible.builtin.import_tasks: roles/library/handlers/main.yml

It now seems, that the imported handlers are placed after the role-specific handlers, which breaks some plays, where the library handlers should run before the role-specific handlers, but they run after them (or rather, the play fails, as the handlers didn’t run).

We are aware, that handlers are executed in the order in which they are written in the code, not how they have been called.

We have two questions regarding this situation:

  1. Can it be confirmed, that imported handlers are attached after the list of role-specific handlers?
  2. Is there a way to load imported handlers before the role-specific handlers?

Thanks in advance

I’m not sure about the first question but I can give you the answer for the second.

You can make your library role to be a dummy role by making an empty tasks/main.yml. After that, you can call the role together with other roles like so:

roles:
  - library
  - server

This way handlers from library role will be imported into play before all other role handlers and no additional tasks will be executed since library role is empty. You can also define variable defaults in library role which will also be imported without running any tasks.

This is not necessary. Roles are not required to contain tasks.

1 Like

Hi.
I think a better approach would be to add the handler from the central library role to each respective role. It might be a “little” more work, but this ensures that your roles can function, and are easily shareable, without depending on a “central” role.

1 Like

Yes but then when you update the handler code you have to update it in multiple places. With a complex code this quickly leads to high maintenance overhead.

I think the OP presented here a simple example but has much more in mind hence a role that is called a “library” (something complex and shared).

Yes, it would be more work, as you have to copy the handler to all the roles that use it, but I believe creating a dependency to a central role causes more complexity :man_shrugging:

1 Like

Dear @bvitnik , @flowerysong, thanks for your ideas and suggestions. I clearly want to stay with the centrally managed handlers and not add code redundancy to our Ansible setup. IMHO this is generally much easier to maintain, even though it might come with some small additional complexity :slight_smile:. Our central “library” role contains anyway much more than just handlers (like parametrizable generic task “templates” ecc.).

@bvitnik’s idea of importing the library as a role is interesting. As a variant of this, I am now importing the library handler as first task in the role’s handler file (roles/xxx/handlers.main.yml)

---
- name: "Import library handlers"
  ansible.builtin.import_tasks: roles/library/handlers/main.yml

- name: ....

This ensures the right order. I’m still considering adding the complete role as suggested by @bvitnik, though.

Also thanks to @jholland of course (the forum software didn’t allow me to mention more than two users in the post above :smile:)

You should not access things that are inside of other roles using non-role imports. It confuses people reading your code and defeats the purpose of putting it in a role.

If you want to import a loose task file using import_tasks, don’t put it in a role. If you want to share handlers using a role, use one of the supported methods for calling that role.