mirror of
https://github.com/willfarrell/docker-crontab.git
synced 2025-04-03 21:45:12 +02:00
Merge pull request #40 from chiqomar/master
Adding python conversion for TOML and YAML
This commit is contained in:
commit
c2331fc9e7
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,3 +2,5 @@
|
||||
*.iml
|
||||
|
||||
config.json
|
||||
.vscode
|
||||
.DS_Store
|
||||
|
16
Dockerfile
16
Dockerfile
@ -1,12 +1,24 @@
|
||||
FROM alpine:3.12 as rq-build
|
||||
|
||||
ENV RQ_VERSION=1.0.2
|
||||
WORKDIR /root/
|
||||
|
||||
RUN apk --update add upx \
|
||||
&& wget https://github.com/dflemstr/rq/releases/download/v${RQ_VERSION}/rq-v${RQ_VERSION}-x86_64-unknown-linux-musl.tar.gz \
|
||||
&& tar -xvf rq-v1.0.2-x86_64-unknown-linux-musl.tar.gz \
|
||||
&& upx --brute rq
|
||||
|
||||
FROM library/docker:stable
|
||||
|
||||
COPY --from=rq-build /root/rq /usr/local/bin
|
||||
|
||||
ENV HOME_DIR=/opt/crontab
|
||||
RUN apk add --no-cache --virtual .run-deps gettext bash jq \
|
||||
RUN apk add --no-cache --virtual .run-deps gettext jq bash tini \
|
||||
&& mkdir -p ${HOME_DIR}/jobs ${HOME_DIR}/projects \
|
||||
&& adduser -S docker -D
|
||||
|
||||
COPY docker-entrypoint /
|
||||
ENTRYPOINT ["/docker-entrypoint"]
|
||||
ENTRYPOINT ["/sbin/tini", "--", "/docker-entrypoint"]
|
||||
|
||||
HEALTHCHECK --interval=5s --timeout=3s \
|
||||
CMD ps aux | grep '[c]rond' || exit 1
|
||||
|
@ -21,7 +21,10 @@ A great project, don't get me wrong. It was just missing certain key enterprise
|
||||
- Run command on a instances of a scaled container using `project`.
|
||||
- Ability to trigger scripts in other containers on completion cron job using `trigger`.
|
||||
|
||||
## Config.json
|
||||
## Config file
|
||||
|
||||
The config file can be specifed in any of `json`, `toml`, or `yaml`, and can be defined as either an array or mapping (top-level keys will be ignored; can be useful for organizing commands)
|
||||
|
||||
- `name`: Human readable name that will be used as the job filename. Will be converted into a slug. Optional.
|
||||
- `comment`: Comments to be included with crontab entry. Optional.
|
||||
- `schedule`: Crontab schedule syntax as described in https://en.wikipedia.org/wiki/Cron. Ex `@hourly`, `@every 1h30m`, `* * * * *`. Required.
|
||||
@ -33,7 +36,7 @@ A great project, don't get me wrong. It was just missing certain key enterprise
|
||||
- `trigger`: Array of docker-crontab subset objects. Subset includes: `image`,`project`,`container`,`command`,`dockerargs`
|
||||
- `onstart`: Run the command on `crontab` container start, set to `true`. Optional, defaults to falsey.
|
||||
|
||||
See [`config.sample.json`](https://github.com/willfarrell/docker-crontab/blob/master/config.sample.json) for examples.
|
||||
See [`config-samples`](config-samples) for examples.
|
||||
|
||||
```json
|
||||
[{
|
||||
|
60
config-samples/config.sample.json
Normal file
60
config-samples/config.sample.json
Normal file
@ -0,0 +1,60 @@
|
||||
[
|
||||
{
|
||||
"comment": "cron with triggered commands",
|
||||
"schedule": "* * * * *",
|
||||
"command": "echo hello",
|
||||
"project": "crontab",
|
||||
"container": "myapp",
|
||||
"trigger": [
|
||||
{
|
||||
"command": "echo world",
|
||||
"container": "crontab_myapp_1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"comment": "map a volume",
|
||||
"schedule": "* * * * *",
|
||||
"dockerargs": "-d -v /tmp:/tmp",
|
||||
"command": "echo new",
|
||||
"image": "alpine:3.5"
|
||||
},
|
||||
{
|
||||
"comment": "use an ENV from inside a container",
|
||||
"schedule": "@hourly",
|
||||
"dockerargs": "-d -e FOO=BAR",
|
||||
"command": "sh -c 'echo hourly ${FOO}'",
|
||||
"image": "alpine:3.5"
|
||||
},
|
||||
{
|
||||
"comment": "trigger every 2 min",
|
||||
"schedule": "@every 2m",
|
||||
"command": "echo 2 minute",
|
||||
"image": "alpine:3.5",
|
||||
"trigger": [
|
||||
{
|
||||
"command": "echo world",
|
||||
"container": "crontab_myapp_1"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"schedule": "*/5 * * * *",
|
||||
"command": "/usr/sbin/logrotate /etc/logrotate.conf"
|
||||
},
|
||||
{
|
||||
"comment": "Regenerate Certificate then reload nginx",
|
||||
"schedule": "43 6,18 * * *",
|
||||
"command": "sh -c 'dehydrated --cron --out /etc/ssl --domain ${LE_DOMAIN} --challenge dns-01 --hook dehydrated-dns'",
|
||||
"dockerargs": "--env-file /opt/crontab/env/letsencrypt.env -v webapp_nginx_tls_cert:/etc/ssl -v webapp_nginx_acme_challenge:/var/www/.well-known/acme-challenge",
|
||||
"image": "willfarrell/letsencrypt",
|
||||
"trigger": [
|
||||
{
|
||||
"command": "sh -c '/etc/scripts/make_hpkp ${NGINX_DOMAIN} && /usr/sbin/nginx -t && /usr/sbin/nginx -s reload'",
|
||||
"project": "conduit",
|
||||
"container": "nginx"
|
||||
}
|
||||
],
|
||||
"onstart": true
|
||||
}
|
||||
]
|
50
config-samples/config.sample.mapping.json
Normal file
50
config-samples/config.sample.mapping.json
Normal file
@ -0,0 +1,50 @@
|
||||
{
|
||||
"cron with triggered commands": {
|
||||
"comment": "cron with triggered commands",
|
||||
"schedule": "* * * * *",
|
||||
"command": "echo hello",
|
||||
"project": "crontab",
|
||||
"container": "myapp",
|
||||
"trigger": [{ "command": "echo world", "container": "crontab_myapp_1" }]
|
||||
},
|
||||
"map a volume": {
|
||||
"comment": "map a volume",
|
||||
"schedule": "* * * * *",
|
||||
"dockerargs": "-d -v /tmp:/tmp",
|
||||
"command": "echo new",
|
||||
"image": "alpine:3.5"
|
||||
},
|
||||
"use an ENV from inside a container": {
|
||||
"comment": "use an ENV from inside a container",
|
||||
"schedule": "@hourly",
|
||||
"dockerargs": "-d -e FOO=BAR",
|
||||
"command": "sh -c 'echo hourly ${FOO}'",
|
||||
"image": "alpine:3.5"
|
||||
},
|
||||
"trigger every 2 min": {
|
||||
"comment": "trigger every 2 min",
|
||||
"schedule": "@every 2m",
|
||||
"command": "echo 2 minute",
|
||||
"image": "alpine:3.5",
|
||||
"trigger": [{ "command": "echo world", "container": "crontab_myapp_1" }]
|
||||
},
|
||||
"null": {
|
||||
"schedule": "*/5 * * * *",
|
||||
"command": "/usr/sbin/logrotate /etc/logrotate.conf"
|
||||
},
|
||||
"Regenerate Certificate then reload nginx": {
|
||||
"comment": "Regenerate Certificate then reload nginx",
|
||||
"schedule": "43 6,18 * * *",
|
||||
"command": "sh -c 'dehydrated --cron --out /etc/ssl --domain ${LE_DOMAIN} --challenge dns-01 --hook dehydrated-dns'",
|
||||
"dockerargs": "--env-file /opt/crontab/env/letsencrypt.env -v webapp_nginx_tls_cert:/etc/ssl -v webapp_nginx_acme_challenge:/var/www/.well-known/acme-challenge",
|
||||
"image": "willfarrell/letsencrypt",
|
||||
"trigger": [
|
||||
{
|
||||
"command": "sh -c '/etc/scripts/make_hpkp ${NGINX_DOMAIN} && /usr/sbin/nginx -t && /usr/sbin/nginx -s reload'",
|
||||
"project": "conduit",
|
||||
"container": "nginx"
|
||||
}
|
||||
],
|
||||
"onstart": true
|
||||
}
|
||||
}
|
46
config-samples/config.sample.mapping.yml
Normal file
46
config-samples/config.sample.mapping.yml
Normal file
@ -0,0 +1,46 @@
|
||||
cron with triggered commands:
|
||||
command: echo hello
|
||||
comment: cron with triggered commands
|
||||
container: myapp
|
||||
project: crontab
|
||||
schedule: '* * * * *'
|
||||
trigger:
|
||||
- command: echo world
|
||||
container: crontab_myapp_1
|
||||
map a volume:
|
||||
command: echo new
|
||||
comment: map a volume
|
||||
dockerargs: -d -v /tmp:/tmp
|
||||
image: alpine:3.5
|
||||
schedule: '* * * * *'
|
||||
use an ENV from inside a container:
|
||||
command: sh -c 'echo hourly ${FOO}'
|
||||
comment: use an ENV from inside a container
|
||||
dockerargs: -d -e FOO=BAR
|
||||
image: alpine:3.5
|
||||
schedule: '@hourly'
|
||||
trigger every 2 min:
|
||||
command: echo 2 minute
|
||||
comment: trigger every 2 min
|
||||
image: alpine:3.5
|
||||
schedule: '@every 2m'
|
||||
trigger:
|
||||
- command: echo world
|
||||
container: crontab_myapp_1
|
||||
null:
|
||||
command: /usr/sbin/logrotate /etc/logrotate.conf
|
||||
schedule: '*/5 * * * *'
|
||||
Regenerate Certificate then reload nginx:
|
||||
command: sh -c 'dehydrated --cron --out /etc/ssl --domain ${LE_DOMAIN} --challenge
|
||||
dns-01 --hook dehydrated-dns'
|
||||
comment: Regenerate Certificate then reload nginx
|
||||
dockerargs: --env-file /opt/crontab/env/letsencrypt.env -v webapp_nginx_tls_cert:/etc/ssl
|
||||
-v webapp_nginx_acme_challenge:/var/www/.well-known/acme-challenge
|
||||
image: willfarrell/letsencrypt
|
||||
onstart: true
|
||||
schedule: 43 6,18 * * *
|
||||
trigger:
|
||||
- command: sh -c '/etc/scripts/make_hpkp ${NGINX_DOMAIN} && /usr/sbin/nginx -t &&
|
||||
/usr/sbin/nginx -s reload'
|
||||
container: nginx
|
||||
project: conduit
|
50
config-samples/config.sample.toml
Normal file
50
config-samples/config.sample.toml
Normal file
@ -0,0 +1,50 @@
|
||||
# toml files can only have top-loevl mappings, so this is the only sample
|
||||
["cron with triggered commands"]
|
||||
comment = "cron with triggered commands"
|
||||
schedule = "* * * * *"
|
||||
command = "echo hello"
|
||||
project = "crontab"
|
||||
container = "myapp"
|
||||
[["cron with triggered commands".trigger]]
|
||||
command = "echo world"
|
||||
container = "crontab_myapp_1"
|
||||
|
||||
["map a volume"]
|
||||
comment = "map a volume"
|
||||
schedule = "* * * * *"
|
||||
dockerargs = "-d -v /tmp:/tmp"
|
||||
command = "echo new"
|
||||
image = "alpine:3.5"
|
||||
|
||||
["use an ENV from inside a container"]
|
||||
comment = "use an ENV from inside a container"
|
||||
schedule = "@hourly"
|
||||
dockerargs = "-d -e FOO=BAR"
|
||||
command = "sh -c 'echo hourly ${FOO}'"
|
||||
image = "alpine:3.5"
|
||||
|
||||
["trigger every 2 min"]
|
||||
comment = "trigger every 2 min"
|
||||
schedule = "@every 2m"
|
||||
command = "echo 2 minute"
|
||||
image = "alpine:3.5"
|
||||
[["trigger every 2 min".trigger]]
|
||||
command = "echo world"
|
||||
container = "crontab_myapp_1"
|
||||
|
||||
["? /usr/sbin/logrotate /etc/logrotate.conf*/5 * * * *"]
|
||||
schedule = "*/5 * * * *"
|
||||
command = "/usr/sbin/logrotate /etc/logrotate.conf"
|
||||
|
||||
["Regenerate Certificate then reload nginx"]
|
||||
comment = "Regenerate Certificate then reload nginx"
|
||||
schedule = "43 6,18 * * *"
|
||||
command = "sh -c 'dehydrated --cron --out /etc/ssl --domain ${LE_DOMAIN} --challenge dns-01 --hook dehydrated-dns'"
|
||||
dockerargs = "--env-file /opt/crontab/env/letsencrypt.env -v ${PWD}:/etc/ssl -v webapp_nginx_acme_challenge:/var/www/.well-known/acme-challenge"
|
||||
image = "willfarrell/letsencrypt"
|
||||
onstart = true
|
||||
[["Regenerate Certificate then reload nginx".trigger]]
|
||||
command = "sh -c '/etc/scripts/make_hpkp ${NGINX_DOMAIN} && /usr/sbin/nginx -t && /usr/sbin/nginx -s reload'"
|
||||
project = "conduit"
|
||||
container = "nginx"
|
||||
|
40
config-samples/config.sample.yml
Normal file
40
config-samples/config.sample.yml
Normal file
@ -0,0 +1,40 @@
|
||||
- command: echo hello
|
||||
comment: cron with triggered commands
|
||||
container: myapp
|
||||
project: crontab
|
||||
schedule: '* * * * *'
|
||||
trigger:
|
||||
- command: echo world
|
||||
container: crontab_myapp_1
|
||||
- command: echo new
|
||||
comment: map a volume
|
||||
dockerargs: -d -v /tmp:/tmp
|
||||
image: alpine:3.5
|
||||
schedule: '* * * * *'
|
||||
- command: sh -c 'echo hourly ${FOO}'
|
||||
comment: use an ENV from inside a container
|
||||
dockerargs: -d -e FOO=BAR
|
||||
image: alpine:3.5
|
||||
schedule: '@hourly'
|
||||
- command: echo 2 minute
|
||||
comment: trigger every 2 min
|
||||
image: alpine:3.5
|
||||
schedule: '@every 2m'
|
||||
trigger:
|
||||
- command: echo world
|
||||
container: crontab_myapp_1
|
||||
- command: /usr/sbin/logrotate /etc/logrotate.conf
|
||||
schedule: '*/5 * * * *'
|
||||
- command: sh -c 'dehydrated --cron --out /etc/ssl --domain ${LE_DOMAIN} --challenge
|
||||
dns-01 --hook dehydrated-dns'
|
||||
comment: Regenerate Certificate then reload nginx
|
||||
dockerargs: --env-file /opt/crontab/env/letsencrypt.env -v webapp_nginx_tls_cert:/etc/ssl
|
||||
-v webapp_nginx_acme_challenge:/var/www/.well-known/acme-challenge
|
||||
image: willfarrell/letsencrypt
|
||||
onstart: true
|
||||
schedule: 43 6,18 * * *
|
||||
trigger:
|
||||
- command: sh -c '/etc/scripts/make_hpkp ${NGINX_DOMAIN} && /usr/sbin/nginx -t &&
|
||||
/usr/sbin/nginx -s reload'
|
||||
container: nginx
|
||||
project: conduit
|
@ -1,47 +0,0 @@
|
||||
[{
|
||||
"comment":"cron with triggered commands",
|
||||
"schedule":"* * * * *",
|
||||
"command":"echo hello",
|
||||
"project":"crontab",
|
||||
"container":"myapp",
|
||||
"trigger":[{
|
||||
"command":"echo world",
|
||||
"container":"crontab_myapp_1"
|
||||
}]
|
||||
},{
|
||||
"comment":"map a volume",
|
||||
"schedule":"* * * * *",
|
||||
"dockerargs":"-d -v /tmp:/tmp",
|
||||
"command":"echo new",
|
||||
"image":"alpine:3.5"
|
||||
},{
|
||||
"comment":"use an ENV from inside a container",
|
||||
"schedule":"@hourly",
|
||||
"dockerargs":"-d -e FOO=BAR",
|
||||
"command":"sh -c 'echo hourly ${FOO}'",
|
||||
"image":"alpine:3.5"
|
||||
},{
|
||||
"comment":"trigger every 2 min",
|
||||
"schedule":"@every 2m",
|
||||
"command":"echo 2 minute",
|
||||
"image":"alpine:3.5",
|
||||
"trigger":[{
|
||||
"command":"echo world",
|
||||
"container":"crontab_myapp_1"
|
||||
}]
|
||||
},{
|
||||
"schedule":"*/5 * * * *",
|
||||
"command":"/usr/sbin/logrotate /etc/logrotate.conf"
|
||||
},{
|
||||
"comment":"Regenerate Certificate then reload nginx",
|
||||
"schedule":"43 6,18 * * *",
|
||||
"command":"sh -c 'dehydrated --cron --out /etc/ssl --domain ${LE_DOMAIN} --challenge dns-01 --hook dehydrated-dns'",
|
||||
"dockerargs":"--env-file /opt/crontab/env/letsencrypt.env -v webapp_nginx_tls_cert:/etc/ssl -v webapp_nginx_acme_challenge:/var/www/.well-known/acme-challenge",
|
||||
"image":"willfarrell/letsencrypt",
|
||||
"trigger":[{
|
||||
"command":"sh -c '/etc/scripts/make_hpkp ${NGINX_DOMAIN} && /usr/sbin/nginx -t && /usr/sbin/nginx -s reload'",
|
||||
"project":"conduit",
|
||||
"container":"nginx"
|
||||
}],
|
||||
"onstart":true
|
||||
}]
|
@ -11,5 +11,4 @@ services:
|
||||
restart: always
|
||||
volumes:
|
||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||
# - "/usr/bin/docker:/usr/bin/docker:ro"
|
||||
- "/Users/willfarrell/Development/docker/docker-crontab/config.json:/opt/crontab/config.json:rw"
|
||||
- "${PWD}/config-samples/config.sample.mapping.json:/opt/crontab/config.json:rw"
|
||||
|
@ -15,7 +15,18 @@ if [ "${LOG_FILE}" == "" ]; then
|
||||
touch ${LOG_FILE}
|
||||
fi
|
||||
|
||||
CONFIG=${HOME_DIR}/config.json
|
||||
get_config() {
|
||||
if [ -f "${HOME_DIR}/config.json" ]; then
|
||||
jq 'map(.)' ${HOME_DIR}/config.json > ${HOME_DIR}/config.working.json
|
||||
elif [ -f "${HOME_DIR}/config.toml" ]; then
|
||||
rq -t <<< $(cat ${HOME_DIR}/config.toml) | jq 'map(.)' > ${HOME_DIR}/config.json
|
||||
elif [ -f "${HOME_DIR}/config.yml" ]; then
|
||||
rq -y <<< $(cat ${HOME_DIR}/config.yml) | jq 'map(.)' > ${HOME_DIR}/config.json
|
||||
elif [ -f "${HOME_DIR}/config.yaml" ]; then
|
||||
rq -y <<< $(cat ${HOME_DIR}/config.yaml) | jq 'map(.)' > ${HOME_DIR}/config.json
|
||||
fi
|
||||
}
|
||||
|
||||
DOCKER_SOCK=/var/run/docker.sock
|
||||
CRONTAB_FILE=/etc/crontabs/docker
|
||||
|
||||
@ -49,7 +60,22 @@ slugify() {
|
||||
|
||||
make_image_cmd() {
|
||||
DOCKERARGS=$(echo ${1} | jq -r .dockerargs)
|
||||
VOLUMES=$(echo ${1} | jq -r '.volumes | map(" -v " + .) | join("")')
|
||||
PORTS=$(echo ${1} | jq -r '.ports | map(" -p " + .) | join("")')
|
||||
EXPOSE=$(echo ${1} | jq -r '.expose | map(" --expose " + .) | join("")')
|
||||
# We'll add name in, if it exists
|
||||
NAME=$(echo ${1} | jq -r 'select(.name != null) | .name')
|
||||
NETWORK=$(echo ${1} | jq -r 'select(.network != null) | .network')
|
||||
ENVIRONMENT=$(echo ${1} | jq -r '.environment | map(" -e " + .) | join("")')
|
||||
# echo ${1} | jq -r '.environment | join("\n")' > ${PWD}/${NAME}.env
|
||||
# ENVIRONMENT=" --env-file ${PWD}/${NAME}.env"
|
||||
if [ "${DOCKERARGS}" == "null" ]; then DOCKERARGS=; fi
|
||||
if [ ! -z "${NAME}" ]; then DOCKERARGS="${DOCKERARGS} --rm --name ${NAME} "; fi
|
||||
if [ ! -z "${NETWORK}" ]; then DOCKERARGS="${DOCKERARGS} --network ${NETWORK} "; fi
|
||||
if [ ! -z "${VOLUMES}" ]; then DOCKERARGS="${DOCKERARGS}${VOLUMES}"; fi
|
||||
if [ ! -z "${ENVIRONMENT}" ]; then DOCKERARGS="${DOCKERARGS}${ENVIRONMENT}"; fi
|
||||
if [ ! -z "${PORTS}" ]; then DOCKERARGS="${DOCKERARGS}${PORTS}"; fi
|
||||
if [ ! -z "${EXPOSE}" ]; then DOCKERARGS="${DOCKERARGS}${EXPOSE}"; fi
|
||||
IMAGE=$(echo ${1} | jq -r .image | envsubst)
|
||||
TMP_COMMAND=$(echo ${1} | jq -r .command)
|
||||
echo "docker run ${DOCKERARGS} ${IMAGE} ${TMP_COMMAND}"
|
||||
@ -80,6 +106,7 @@ for CONTAINER_NAME in \$CONTAINERS; do
|
||||
done
|
||||
EOF
|
||||
echo "/bin/bash ${HOME_DIR}/projects/${SCRIPT_NAME}.sh"
|
||||
# cat "/bin/bash ${HOME_DIR}/projects/${SCRIPT_NAME}.sh"
|
||||
else
|
||||
echo "docker exec ${DOCKERARGS} ${CONTAINER} ${TMP_COMMAND}"
|
||||
fi
|
||||
@ -153,6 +180,7 @@ parse_schedule() {
|
||||
}
|
||||
|
||||
function build_crontab() {
|
||||
|
||||
rm -rf ${CRONTAB_FILE}
|
||||
|
||||
ONSTART=()
|
||||
@ -230,15 +258,27 @@ EOF
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
ensure_docker_socket_accessible
|
||||
|
||||
if [ "$1" = "crond" ]; then
|
||||
if [ -f ${CONFIG} ]; then
|
||||
build_crontab
|
||||
start_app() {
|
||||
get_config
|
||||
if [ -f "${HOME_DIR}/config.working.json" ]; then
|
||||
export CONFIG=${HOME_DIR}/config.working.json
|
||||
elif [ -f "${HOME_DIR}/config.json" ]; then
|
||||
export CONFIG=${HOME_DIR}/config.json
|
||||
else
|
||||
echo "Unable to find ${HOME_DIR}/config.json"
|
||||
echo "NO CONFIG FILE FOUND"
|
||||
fi
|
||||
fi
|
||||
if [ "$1" = "crond" ]; then
|
||||
if [ -f ${CONFIG} ]; then
|
||||
build_crontab
|
||||
else
|
||||
echo "Unable to find ${CONFIG}"
|
||||
fi
|
||||
fi
|
||||
echo "$@"
|
||||
exec "$@"
|
||||
}
|
||||
|
||||
echo "$@"
|
||||
exec "$@"
|
||||
start_app "$@"
|
||||
|
Loading…
x
Reference in New Issue
Block a user