How do I create a single-execution Upstart job guaranteed to complete before two other jobs begin?
Solution 1:
I believe I have an answer to my own question that leverages CameronNemo's partial solution and Mark Russell's answer to a related but somewhat different question.
Two Upstart configuration files are required. The first is a job that starts as soon as the local filesystem is available, performs the desired file modifications as a pre-start script, and then sits idle in the running state forever:
# modify-files - Single-execution file modification job
start on local-filesystems
console log
pre-start script
echo "$(date --rfc-3339=ns) $(hostname) $UPSTART_JOB"
exec /path/to/your/script
end script
The second configuration file is an Upstart task that delays the start of all other jobs that might depend on the files we're trying to modify. It produces one instance of itself per dependent job:
# modify-files-wait - Helper task for modify-files
start on (starting jobA or jobB)
stop on (started modify-files or stopped modify-files)
instance $JOB
console log
normal exit 0 2
task
script
echo "$(date --rfc-3339=ns) $(hostname) $UPSTART_JOB ($UPSTART_INSTANCE)"
status modify-files | grep -q "start/running" && exit 0
start modify-files || true
sleep infinity
end script
Upstart will kill all instances of modify-files-wait
once modify-files
is idling in its running state. That normal exit
line accounts for the possibility of being killed during its infinite sleep. We need the task
line to block jobA and joB until the stopped state is reached. Whichever instance runs first will start modify-files
, if it hasn't already been started.
Since modify-files
never reaches its stopped state, it will never be run anew, regardless of jobA or jobB being restarted.
This solution seems to be working, but I welcome any criticisms or improvements.
Solution 2:
You can define a simple task job that start on event of your choice, run your script and at the end emit event to start the other two job.
For example:
# mainJob -
#
# This service emit myEvent to run firstJob
description "emit myEvent to run firstJob"
start on runlevel [2345]
task
console log
script
echo "(startTask) $UPSTART_JOB -- $UPSTART_EVENTS"
exec /path/to/your/script
initctl emit -n myEvent
end script
In order to do not modify upstart script of the other two jobs, you should override files that allow you to modify the way in which a job starts and stop by modifying the start on and stop on conditions.
Following my examples I created a simple firstJob.conf
like this:
# firstJob -
#
# This service print environment variable
description "print environment variable"
start on runlevel [2345]
stop on runlevel [016]
task
console log
script
if [ "$RUNLEVEL" = "0" -o "$RUNLEVEL" = "1" -o "$RUNLEVEL" = "6" ]; then
exec echo "(stopTask) $UPSTART_JOB -- $UPSTART_EVENTS"
else
exec echo "(startTask) $UPSTART_JOB -- $UPSTART_EVENTS"
fi
end script
And then I override start on condition creating override file:
echo "start on myEvent" > /etc/init/firstJob.override
So firstJob
will start on myEvent
generated by mainJob
and stop on runlevel [016]
I tested these jobs on lubuntu 12.04 and after reboot I found in /var/log/upstart/firstJob.log
:
(startTask) firstJob -- myEvent
You should check if "the other two jobs" need particular event condition to start and be sure that mainJob
start on these events.