How can I make sure one Upstart job starts before other Upstart jobs?

This is a general Upstart question, but let me use a specific case:

Centrify is a NIS to ActiveDirectory gateway. It needs to load before any service that will depend the authentication service that it provides, e.g. autofs, cron, nis, et al.

This has proven to be quite challenging to achieve, even when trying to change the dependencies of the other services (which I don't think we should be doing anyway, I don't want to touch the other Upstart jobs if at all possible).

Suggestions?


Solution 1:

The solution is to approach the problem from the other direction: to satisfy the start criteria for Centrify, it is not necessary to make existing services depend on the new Centrify service, rather make the new Centrify service depend on existing services.

For example, an Upstart configuration file /etc/init/centrify.conf could say:

start on (starting cron or starting autofs or starting nis)

Converting this into English, this would translate as:

start the Centrify service just before either cron, autofs or nis start (whichever starts first).

The order in which cron, autofs or nis start is irrelevant: Upstart will ensure that Centrify will start before whichever service starts first, thus ensuring that Centrify is running before any one of those services start.

Note too that Upstart will block the start of the first service that wants to start until Centrify has started running.

Very elegant and simple once you get used to thinking in this manner.

Solution 2:

James's answer works for a 1 to 1 dependency. For a 1 to many, i.e., to make sure service A starts before services B, C, and D, you need to take another approach. You can look at the current portmap scripts for reference but here is the general approach: create a wait script.

Scenario: you want your Service A to always run before service-b, service-c, and service-d.

Solution: create a wait script for Service A. Call it "/etc/init/service-a-wait.conf"

# service-a-wait

start on (starting service-b 
    or starting service-c
    or starting service-d)
stop on (started service-a or stopped service-a)

# We know that we have more than one job that needs to wait for service-a and
# will make use of this service, so we need to instantiate.
instance $JOB

# Needed to make starting the job successful despite being killed
normal exit 2
task

script

    status service-a | grep -q "start/running" && exit 0
    start service-a || true

    # Waiting forever is ok.. upstart will kill this job when
    # the service-a we tried to start above either starts or stops
    while sleep 3600 ; do :; done

end script

What this means in plain English is: when service b, c, or d signals that they want to start, they must wait to start until service-a is running. The service-a-wait job is designed to run until service-a has started. Once service-a-wait exits, now services b, c, and d are free to carry on and run.

This will assure service-a is up and running before any of its reverse dependencies attempts to start.

Note: the "instance $JOB" line is important in this "start on... or.. or.." scenario. Otherwise you will only really block for whichever of B, C, or D fires off first.

(instantiation deserves a better explanation honestly. for now, just do it. ;)