diff --git a/Dockerfile b/Dockerfile index 8d33d0c..b93af9c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,10 +2,22 @@ FROM alpine:latest RUN set -ex; \ \ - mkdir /etc/periodic/5min; \ - chmod 755 /etc/periodic/5min; + mkdir -pm 755 /kiwi-cron; \ + cd /kiwi-cron; \ + mkdir -pm 755 \ + every/5_minutes \ + every/15_minutes \ + hourly \ + daily \ + weekly \ + monthly \ + yearly \ + ; \ + \ + apk --no-cache add \ + docker-cli \ + ; -ENTRYPOINT [ "/usr/sbin/crond" ] -CMD [ "-fd", "8" ] +COPY cron-exec /usr/local/bin/ -COPY crontab /var/spool/cron/crontabs/root +CMD [ "cron-exec" ] \ No newline at end of file diff --git a/README.md b/README.md index 2c14aff..443dcab 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,18 @@ Simple cron-jobs for [`kiwi-scp`](https://github.com/ldericher/kiwi-scp) ## Quick start -kiwi-cron comes with a pre-loaded cron daemon for periodic jobs. Just drop your executables into the relevant directory under `/etc/periodic/` and that's it. +kiwi-cron comes with a pre-configured cron daemon for periodic jobs. +Just drop your scripts into the relevant directory under `/kiwi-cron/` and that's it. -- `/etc/periodic/5min` -- is run every 5 minutes -- `/etc/periodic/15min` -- is run every 15 minutes -- `/etc/periodic/hourly` -- is run every full hour -- `/etc/periodic/daily` -- is run every day at 2am -- `/etc/periodic/weekly` -- is run every saturday at 3am -- `/etc/periodic/month` -- is run on the first day of every month at 5am +- `/kiwi-cron/hourly` – is run every full hour +- `/kiwi-cron/daily` – is run every day at 2 am +- `/kiwi-cron/weekly` – is run every saturday at 3 am +- `/kiwi-cron/monthly` – is run on the first day of every month at 5 am +- `/kiwi-cron/yearly` and `/kiwi-cron/annually` – is run on every January 1st at 12 am + +## `/kiwi-cron/every` directory + +You can use directories like `/kiwi-cron/every/5_minutes` to run scripts every 5 minutes. +`kiwi-cron` automatically picks up on that format and generates cron schedules for you. + +You can define schedules to be run every N minutes, hours, days, or months that way. diff --git a/cron-exec b/cron-exec new file mode 100755 index 0000000..b4f79ed --- /dev/null +++ b/cron-exec @@ -0,0 +1,151 @@ +#!/bin/sh + +crontab_append () { + crontab="$( + printf '%s\n%s' \ + "${crontab}" "${1}" + )" +} + +print_cron_schedule() { + cas_min="${1}" + cas_hour="${2}" + cas_day="${3}" + cas_month="${4}" + cas_weekday="${5}" + cas_command="${6}" + + printf '%-8s%-8s%-8s%-8s%-8s%s\n' \ + "${cas_min}" "${cas_hour}" "${cas_day}" "${cas_month}" "${cas_weekday}" "${cas_command}" +} + +crontab='# crontab generated for kiwi-cron' +crontab_append '# generation time: '"$(date)" +crontab_append '#' +crontab_append "$( + print_cron_schedule \ + "# min" "hour" "day" "month" "weekday" "command" +)" + +# check *ly dirs in root directory +schedule_dirs="$( + find "/kiwi-cron" -type d -name "*ly" -mindepth 1 -maxdepth 1 +)" + +for schedule_dir in ${schedule_dirs}; do + # count files in scheduler directory + schedule_filecount="$( + find "${schedule_dir}" -type f -mindepth 1 -maxdepth 1 \ + | wc -l + )" + + # ignore if empty + if [ "${schedule_filecount}" -eq "0" ]; then + continue + fi + + # infer cron schedule by directory name + units="${schedule_dir##*/}" + + case "${units}" in + hourly) + crontab_append "$( + print_cron_schedule \ + "0" "*" "*" "*" "*" "run-parts '${schedule_dir}'" + )" + ;; + + daily) + crontab_append "$( + print_cron_schedule \ + "0" "2" "*" "*" "*" "run-parts '${schedule_dir}'" + )" + ;; + + weekly) + crontab_append "$( + print_cron_schedule \ + "0" "3" "*" "*" "6" "run-parts '${schedule_dir}'" + )" + ;; + + monthly) + crontab_append "$( + print_cron_schedule \ + "0" "5" "1" "*" "*" "run-parts '${schedule_dir}'" + )" + ;; + + yearly|annually) + crontab_append "$( + print_cron_schedule \ + "0" "0" "1" "1" "*" "run-parts '${schedule_dir}'" + )" + ;; + esac +done + +# check dirs in "every" subdirectory +every_schedule_dirs="$( + find "/kiwi-cron/every" -type d -mindepth 1 -maxdepth 1 +)" + +for schedule_dir in ${every_schedule_dirs}; do + # count files in scheduler directory + schedule_filecount="$( + find "${schedule_dir}" -type f -mindepth 1 -maxdepth 1 \ + | wc -l + )" + + # ignore if empty + if [ "${schedule_filecount}" -eq "0" ]; then + continue + fi + + # infer cron schedule by directory name + schedule="${schedule_dir##*/}" + every="$( echo "${schedule}" | awk -F_ '{print $1}' )" + units="$( echo "${schedule}" | awk -F_ '{print $2}' )" + units="${units%s}" + + # generate valid random values + rand_min=$(( RANDOM % 60 )) + # rand_hour=$(( RANDOM % 24 )) + # generating nighttime values seems like a good idea! + rand_hour=$(( (RANDOM % 7 + 21) % 24 )) + rand_day=$(( RANDOM % 31 + 1 )) + + case "${units}" in + minute) + crontab_append "$( + print_cron_schedule \ + "*/${every}" "*" "*" "*" "*" "run-parts '${schedule_dir}'" + )" + ;; + + hour) + crontab_append "$( + print_cron_schedule \ + "${rand_min}" "*/${every}" "*" "*" "*" "run-parts '${schedule_dir}'" + )" + ;; + + day) + crontab_append "$( + print_cron_schedule \ + "${rand_min}" "${rand_hour}" "*/${every}" "*" "*" "run-parts '${schedule_dir}'" + )" + ;; + + month) + crontab_append "$( + print_cron_schedule \ + "${rand_min}" "${rand_hour}" "${rand_day}" "*/${every}" "*" "run-parts '${schedule_dir}'" + )" + ;; + esac +done + +# replace crontab +echo "${crontab}" | crontab - +exec crond -fd 8 diff --git a/crontab b/crontab deleted file mode 100644 index 9321628..0000000 --- a/crontab +++ /dev/null @@ -1,9 +0,0 @@ -# cron jobs by kiwi-scp -# -# min hour day month weekday command -*/5 * * * * run-parts /etc/periodic/5min -*/15 * * * * run-parts /etc/periodic/15min -0 * * * * run-parts /etc/periodic/hourly -0 2 * * * run-parts /etc/periodic/daily -0 3 * * 6 run-parts /etc/periodic/weekly -0 5 1 * * run-parts /etc/periodic/monthly