Prevent executing a role more than once

I have a common role, which i always want executed, but only once. It is also a dependency of a few other roles that may be executed on their own (via dedicated playbooks).

The problem is: in a top-level playbook, e.g. site.yml, if the common role is listed anywhere (in site.yml or an included playbook), and is also a dependency of any other role, its tasks are executed twice.

What I’m thinking is to create a dummy role that depends on ‘common’, and include the dummy role in the top-level playbook for all hosts. That way, ‘common’ is always a dependency, and is always executed exactly once.

Is there a better solution?

More generally, how can I avoid executing a role more than once, whether it’s listed explicitly or pulled in as a dependency?

If a role is a dependency of multiple other roles, it’s executed once, but if it’s also listed explicitly, it is executed twice.

Take a look at the “allow_duplicates” parameter.

http://docs.ansible.com/playbooks_roles.html#id8

This does not solve my problem.

I’m happy with the default (allow_duplicates=no). My issue is that it only prevents the same role from executing multiple times as a dependency. If the same role is also listed somewhere in the playbook (in addition to being a dependency), allow_duplicates is irrelevant (currently).

I am looking for the equivalent of having allow_duplicates affect that role everywhere, whether it is being executed as a dependency, or explicitly due to being listed in a play.

Generally people should write roles so that they are idempotent (grr, that word again) and running them more than one time is a no-op.

If the operation is very destructive and you don’t want to run it again, maybe consider a touch file to indicate the configuration has been done once.

I do understand what you mean about not being a dependency anywhere, but because ansible allows parameterized roles as dependencies, this would also remove some other use cases if we applied it globally.

Some roles might allow deps, others might not, etc. The role gets to pick.

You can try to set a condition on the whole role (you can use an empty main.yml with an inmport: … when: common_executed is defined) based upon a fact/variable that you can define at the end of the common role, thus if it has been executed once, it will be skipped afterwards.

(attachments)

email_smslogo.png
email_icon_mobile.png
email_icon_phone.png
email_icon_skype.png
email_icon_location.png
email_icon_email.png
email_icon_linkedin.png
email_icon_website.png

Right! Good plan.

You could do a “set_fact: role_has_executed=1” at the end of the role

and then on the role:

  • role: { name: foo, when: “role_has_executed is defined” }
(attachments)

email_icon_mobile.png
email_smslogo.png
email_icon_linkedin.png
email_icon_website.png
email_icon_email.png
email_icon_location.png
email_icon_skype.png
email_icon_phone.png

Even if a role ends up being a no-op, it takes time. This makes playbook execution slower, which is annoying.

Why would this not be default behavior?

What good does it do to tell the list you are annoyed? Provide an
alternative, perhaps.

Role dependencies can have parameters.

​Isn’t that what allow_duplicates=yes is for?

Wouldn’t it be more consistent for allow_duplicates not to discriminate​ between executing the role explicitly or as a dependency?

There are cases (again, parameterized roles) - different roles can have the same dependency with different parameters, where that doesn’t make sense.)

People get too hung up on role dependencies. In most of the content we write for customers, we actually don’t use role deps at all. They are not needed.

We generally find people coming over from Chef, specifically, get caught up in overdesign of playbook content, and people should keep things simple.

That being said there is a certain class of users that really really really want role deps, and they exist for that purpose.

it sounds like some people may like a role metadata parameter that is different from this, that is not allow_duplicates, that is something like once_per_play or something.

I’d be open to consideration, but some refactoring of role implementations will need to happen first, that is in plan.

There are cases (again, parameterized roles) - different roles can have the same dependency with different parameters, where that doesn’t make sense.)

​AFAIU, this is exactly the use case for allow_duplicates=yes, isn’t it? It would allow the role to be executed multiple times to accommodate the different parameters. What doesn’t make sense here?

On the other hand, currently, setting allow_duplicates=no does not help parameterized roles, nor does it help the execute at most once use case.​ It allows multiple executions if the role is listed explicitly and is also a dependency.

it sounds like some people may like a role metadata parameter that is different from this, that is not allow_duplicates, that is something like once_per_play or something.

​What I’m after is not once_per_play, it’s absolutely once, no matter how many times the role is listed in top-level or included plays, and no matter how many times it’s pulled in as a dependency.​

If we forget about dependencies for a bit, and consider the case where a role is listed in two plays, which are both included in a top-level role. Should that role be executed multiple times even when allow_duplicates=no?

"AFAIU, this is exactly the use case for allow_duplicates=yes, isn’t it? "

This only deals with role deps. I feel I am repeating myself :slight_smile:

“If we forget about dependencies for a bit, and consider the case where a role is listed in two plays, which are both included in a top-level role. Should that role be executed multiple times even when allow_duplicates=no?”

Yes, because allow_duplicates only is has meaning for role deps.