My proof
of concept for cron to understand timezones has moved to being
closer to production code and I’m in the process of trying to get it
put back in to Solaris. The biggest sticking point at the moment
appears to be that the UNIX standards all say
you can’t do this in a crontab file.
Meanwhile following Adam’s
point that it’s
tested or it’s broken I have started work on a test suite to test
the new functionality in the blind optimism that a way around the
standards can be found.
First I wrote a script that understands the five time fields used
by cron and a timezone and can tell you if it is running when it
should not. It is called crontest
and can be run from cron like this:
15,45 * 1-18,20 2 * exec lang/sh/crontest 15,45 \* 1-18,20 2 \* Africa/Addis_Ababa
When it thinks things have gone wrong it prints an error with the
process id and the parent process id so that it can be tied back to
the entry in /var/cron/log.
Now I just need a crontab file that
contains all the possible cron entries. Hmm that is not so easy.
However if I can fill the crontab file with random entries I should
get good coverage. So I have another script, crontab_create
which generates random, but legal, crontab entries which call the
crontest
script. So I end up with a crontab that looks like this:
TZ=Asia/Tbilisi
42,50 0 1-25,27-28,30 1 * exec lang/sh/crontest 42,50 0 1-25,27-28,30 1 \* Asia/Tbilisi
37,45 0-21,23 1-20,22-23,25 * * exec lang/sh/crontest 37,45 0-21,23 1-20,22-23,25 \* \* Asia/Tbilisi
32,40 0-16,18-19,21 * 1 * exec lang/sh/crontest 32,40 0-16,18-19,21 \* 1 \* Asia/Tbilisi
27,35 * 1-10,12-13,15-21,23 1-11 * exec lang/sh/crontest 27,35 \* 1-10,12-13,15-21,23 1-11 \* Asia/Tbilisi
22,30,57,59 0-6,8-9,11-17,19 1-5,7-8,10-16,18-28,30 1-6,8-9,11 * exec lang/sh/crontest 22,30,57,59 0-6,8-9,11-17,19 1-5,7-8,10-16,18-28,30 1-6,8-9,11 \* Asia/Tbilisi
TZ=Asia/Tehran
17,25,52,54-56 0-1,3-4,6-12,14 2-3,5-11,13-23,25 1,3-4,6 * exec lang/sh/crontest 17,25,52,54-56 0-1,3-4,6-12,14 2-3,5-11,13-23,25 1,3-4,6 \* Asia/Tehran
12,20,47,49-51 1-7,9-19,21 * * * exec lang/sh/crontest 12,20,47,49-51 1-7,9-19,21 \* \* \* Asia/Tehran
7,15,42,44-46 0-2,4-14,16 1,3-13,15-22,24,26-27,29 1-2,4 * exec lang/sh/crontest 7,15,42,44-46 0-2,4-14,16 1,3-13,15-22,24,26-27,29 1-2,4 \* Asia/Tehran
2,10,37,39-41 0-9,11-18,20,22 1-8,10-17,19,21-22,24 1-9,11 * exec lang/sh/crontest 2,10,37,39-41 0-9,11-18,20,22 1-8,10-17,19,21-22,24 1-9,11 \* Asia/Tehran
5,32,34-36 0-4,6-13,15,17-18,20 1-3,5-12,14,16-17,19 1-4,6 * exec lang/sh/crontest 5,32,34-36 0-4,6-13,15,17-18,20 1-3,5-12,14,16-17,19 1-4,6 \* Asia/Tehran
TZ=Asia/Thimphu
0,27,29-31 1-8,10,12-13,15 1-7,9,11-12,14 * * exec lang/sh/crontest 0,27,29-31 1-8,10,12-13,15 1-7,9,11-12,14 \* \* Asia/Thimphu
22,24-26 * 1-2,4,6-7,9 1-3,5,7-8,10 * exec lang/sh/crontest 22,24-26 \* 1-2,4,6-7,9 1-3,5,7-8,10 \* Asia/Thimphu
17,19-21 0,2-3,5 1-2,4-26,28-30 2-3,5 * exec lang/sh/crontest 17,19-21 0,2-3,5 1-2,4-26,28-30 2-3,5 \* Asia/Thimphu
12,14-16 0-22 * * * exec lang/sh/crontest 12,14-16 0-22 \* \* \* Asia/Thimphu
7,9-11 * 1-16,18-20,22 1 * exec lang/sh/crontest 7,9-11 \* 1-16,18-20,22 1 \* Asia/Thimphu
TZ=Asia/Tokyo
2,4-6,56,59 0-12,14-16,18 1-11,13-15,17 1 * exec lang/sh/crontest 2,4-6,56,59 0-12,14-16,18 1-11,13-15,17 1 \* Asia/Tokyo
0-1,51,54 0-7,9-11,13 1-6,8-10,12 1-7,9-11 * exec lang/sh/crontest 0-1,51,54 0-7,9-11,13 1-6,8-10,12 1-7,9-11 \* Asia/Tokyo
46,49,55 0-2,4-6,8 1,3-5,7 1-2,4-6,8 * exec lang/sh/crontest 46,49,55 0-2,4-6,8 1,3-5,7 1-2,4-6,8 \* Asia/Tokyo
41,44,50 0-1,3 2 1,3 * exec lang/sh/crontest 41,44,50 0-1,3 2 1,3 \* Asia/Tokyo
36,39,45 0 1 * * exec lang/sh/crontest 36,39,45 0 1 \* \* Asia/Tokyo
TZ=Asia/Ulaanbaatar
I needed some tuning so it would not be starting more than 100 jobs
in anyone minute or at least it would not every minute as cron would
then delay some jobs which in turn throws up false positives. I now
have it with 551 timezones and 2756 cron entries which. There is
probably (certainly) an optimization on the script to generate the
cron entries so that they only run in the current month or at least
for the likely length of the test.
It has been running for 24 hours on my
home server. I can see the entries run from the log and I’ve not had
any email telling me things have been submitted at the wrong time. So
I am reasonably happy. Just to prove that it does what I think it
does I added a line were the arguments to the script did not match
the time specification of an entry that is running. Sure enough I get
email (the crontab entry I changed the 25 minute to 50):
Your "cron" job on pearson
exec lang/sh/crontest 23,25 0-11,13-18,20 1-10,12-17,19 \* \* GMT
produced the following output:
17182:24396: Bad min: Should be 23,25 is 50:
This covers the easy cases to test that jobs that run run at the
correct time. The harder part is to verify that every job that should
run does run. Need to think that one through.