mirror of
				https://github.com/willfarrell/docker-crontab.git
				synced 2025-10-30 21:27:18 +01:00 
			
		
		
		
	init commit
This commit is contained in:
		
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| .idea | ||||
| *.iml | ||||
|  | ||||
| config.json | ||||
							
								
								
									
										16
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								Dockerfile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| FROM library/alpine:3.5 | ||||
|  | ||||
| ENV HOME_DIR=/opt/crontab | ||||
| RUN apk add --no-cache --virtual .run-deps bash curl jq docker \ | ||||
|     && mkdir -p ${HOME_DIR} | ||||
|  | ||||
| # Dev | ||||
| COPY config.json ${HOME_DIR}/ | ||||
|  | ||||
| COPY docker-entrypoint / | ||||
| ENTRYPOINT ["/docker-entrypoint"] | ||||
|  | ||||
| HEALTHCHECK --interval=5s --timeout=3s \ | ||||
|     CMD ps aux | grep '[c]rond' || exit 1 | ||||
|  | ||||
| CMD ["crond","-f"] | ||||
							
								
								
									
										62
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| # docker-crontab | ||||
|  | ||||
| A simple wrapper over `docker` to all complex cron job to be run in other containers. | ||||
|  | ||||
| ## Why? | ||||
| Yes, I'm aware of [mcuadros/ofelia](https://github.com/mcuadros/ofelia), it was the main inspiration for this project.  | ||||
| A great project, don't get me wrong. It was just missing certain key enterprise features. | ||||
|  | ||||
| ## Features | ||||
| - Easy to read schedule syntax allowed. | ||||
| - Allows for comments, cause we all need friendly reminders of what `update_script.sh` actually does. | ||||
| - Start an image using `image`. | ||||
| - Run command in a container using `container`. | ||||
| - 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 | ||||
| - `comment`: Comments to be included with crontab entry | ||||
| - `schedule`: Crontab schedule syntax as described in https://godoc.org/github.com/robfig/cron. Ex `@hourly`, `@every 1h30m`, `* * * * * *`. Required. | ||||
| - `command`: Command to be run on docker container/image. Required. | ||||
| - `image`: Docker images name (ex `library/alpine:3.5`). Optional. | ||||
| - `project`: Docker Compose/Swarm project name. Optional, only applies when `contain` is included. | ||||
| - `container`: Full container name or container alias if `project` is set. Ignored if `image` is included. | ||||
| - `dockerargs`: Command line docker `run`/`exec` arguments for full control. Defaults to ` `. | ||||
| - `trigger`: Array of docker-crontab subset objects. Subset includes: `image`,`project`,`container`,`command`,`dockerargs`  | ||||
|  | ||||
| See `./config.sample.json` for examples. | ||||
|  | ||||
| ## Examples | ||||
|  | ||||
| ### Command Line | ||||
| ```bash | ||||
| docer build -t crontab . | ||||
| docker run -d \ | ||||
|     -v /var/run/docker.sock:/var/run/docker.sock \ | ||||
|     crontab | ||||
| ``` | ||||
|  | ||||
| ### Dockerfile | ||||
| ```Dockerfile | ||||
| FROM willfarrell/crontab | ||||
|  | ||||
| COPY config.json ${HOME_DIR}/ | ||||
| ``` | ||||
|  | ||||
| ### Logrotate Dockerfile | ||||
| ```Dockerfile | ||||
| FROM willfarrell/crontab | ||||
|  | ||||
| RUN apk add --no-cache logrotate | ||||
| RUN echo "*/5 *	* * *  /usr/sbin/logrotate /etc/logrotate.conf" >> /etc/crontabs/logrotate | ||||
| ADD logrotate.conf /etc/logrotate.conf | ||||
|  | ||||
| CMD ["crond", "-f"] | ||||
| ``` | ||||
|  | ||||
| ## TODO | ||||
| - [ ] Make smaller by using busybox? | ||||
| - [ ] Have ability to auto regenerate crontab on file change | ||||
| - [ ] Run commands on host machine | ||||
| - [ ] Write tests | ||||
| - [ ] Setup TravisCI | ||||
							
								
								
									
										32
									
								
								config.sample.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								config.sample.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| [{ | ||||
| 	"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" | ||||
| 	}] | ||||
| }] | ||||
							
								
								
									
										13
									
								
								docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								docker-compose.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,13 @@ | ||||
| version: "2.1" | ||||
|  | ||||
| services: | ||||
|   myapp: | ||||
|     image: alpine:3.5 | ||||
|     restart: always | ||||
|     command: "sh -c 'while :; do sleep 1; done'" | ||||
|  | ||||
|   crontab: | ||||
|     build: . | ||||
|     restart: always | ||||
|     volumes: | ||||
|      - "/var/run/docker.sock:/var/run/docker.sock" | ||||
							
								
								
									
										164
									
								
								docker-entrypoint
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										164
									
								
								docker-entrypoint
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,164 @@ | ||||
| #!/usr/bin/env bash | ||||
| set -e | ||||
|  | ||||
| # for local testing only | ||||
| #HOME_DIR=. | ||||
|  | ||||
| CONFIG=${HOME_DIR}/config.json | ||||
| DOCKER_SOCK=/var/run/docker.sock | ||||
| CRONTAB_FILE=${HOME_DIR}/docker | ||||
|  | ||||
| make_image_cmd() { | ||||
|     DOCKERARGS=$(echo ${TMP_JSON} | jq -r .dockerargs) | ||||
|     if [ "${DOCKERARGS}" == "null" ]; then DOCKERARGS=; fi | ||||
|     IMAGE=$(echo ${TMP_JSON} | jq -r .image) | ||||
|     TMP_COMMAND=$(echo ${TMP_JSON} | jq -r .command) | ||||
|     echo "docker run ${DOCKERARGS} ${IMAGE} ${TMP_COMMAND}" | ||||
| } | ||||
|  | ||||
| make_container_cmd() { | ||||
|     DOCKERARGS=$(echo ${TMP_JSON} | jq -r .dockerargs) | ||||
|     if [ "${DOCKERARGS}" == "null" ]; then DOCKERARGS=; fi | ||||
|     PROJECT=$(echo ${TMP_JSON} | jq -r .project) | ||||
|     CONTAINER=$(echo ${TMP_JSON} | jq -r .container) | ||||
|     TMP_COMMAND=$(echo ${TMP_JSON} | jq -r .command) | ||||
|  | ||||
|     COMMAND_ARR=() | ||||
|  | ||||
|     if [ "${PROJECT}" != "null" ]; then | ||||
|  | ||||
|         # create bash script to detect all running containers | ||||
|         SCRIPT_NAME=$(cat /proc/sys/kernel/random/uuid) | ||||
| cat << EOF > ${HOME_DIR}/${SCRIPT_NAME} | ||||
| !#/bin/bash | ||||
| set -e | ||||
|  | ||||
| CONTAINERS=\$(curl --no-buffer -s -XGET --unix-socket ${DOCKER_SOCK} http://localhost/containers/json | jq -r .[].Names[0] | sed 's@/@@')" | ||||
| for CONTAINER_NAME in \$CONTAINERS; do | ||||
|     if [[ "\${CONTAINER_NAME}" =~ ^${PROJECT}_${CONTAINER}.+ ]]; then | ||||
|         docker exec ${DOCKERARGS} \${CONTAINER_NAME} ${TMP_COMMAND} | ||||
|     fi | ||||
| done | ||||
| EOF | ||||
|         echo "sh ${HOME_DIR}/${SCRIPT_NAME}" | ||||
|     else | ||||
|         echo "docker exec ${DOCKERARGS} ${CONTAINER} ${TMP_COMMAND}" | ||||
|     fi | ||||
| } | ||||
|  | ||||
| make_cmd() { | ||||
|     IMAGE=$(echo ${TMP_JSON} | jq -r .image) | ||||
|     CONTAINER=$(echo ${TMP_JSON} | jq -r .container) | ||||
|     if [ "${IMAGE}" != "null" ]; then | ||||
|         make_image_cmd | ||||
|     elif [ "${CONTAINER}" != "null" ]; then | ||||
|         make_container_cmd | ||||
|     else | ||||
|         echo "echo 'Error making docker command, image or container param missing.'" | ||||
|     fi | ||||
| } | ||||
|  | ||||
| parse_schedule() { | ||||
| 	case $1 in | ||||
|         "@yearly") | ||||
|             echo "0 0 0 1 1 *" | ||||
|             ;; | ||||
|         "@annually") | ||||
|             echo "0 0 0 1 1 *" | ||||
|             ;; | ||||
|         "@monthly") | ||||
|             echo "0 0 0 1 * *" | ||||
|             ;; | ||||
|         "@weekly") | ||||
|             echo "0 0 0 * * 0" | ||||
|             ;; | ||||
|         "@daily") | ||||
|             echo "0 0 0 * * *" | ||||
|             ;; | ||||
|         "@midnight") | ||||
|             echo "0 0 0 * * *" | ||||
|             ;; | ||||
|         "@hourly") | ||||
|             echo "0 0 * * * *" | ||||
|             ;; | ||||
|         "@every") | ||||
|             TIME=$2 | ||||
|             TOTAL=0 | ||||
|  | ||||
|             M=$(echo $TIME | grep -o '[0-9]\+m') | ||||
|             H=$(echo $TIME | grep -o '[0-9]\+h') | ||||
|             D=$(echo $TIME | grep -o '[0-9]\+d') | ||||
|  | ||||
|             if [ -n "${M}" ]; then | ||||
|                 TOTAL=$(($TOTAL + ${M::-1})) | ||||
|             fi | ||||
|             if [ -n "${H}" ]; then | ||||
|                 TOTAL=$(($TOTAL + ${H::-1} * 60)) | ||||
|             fi | ||||
|             if [ -n "${D}" ]; then | ||||
|                 TOTAL=$(($TOTAL + ${D::-1} * 60 * 24)) | ||||
|             fi | ||||
|  | ||||
|             echo "*/${TOTAL} * * * * *" | ||||
|             ;; | ||||
|         *) | ||||
|             echo "${@}" | ||||
|             ;; | ||||
|     esac | ||||
| } | ||||
|  | ||||
| function build_crontab() { | ||||
|     rm -rf ${CRONTAB_FILE} | ||||
|     while read i ; do | ||||
|         #echo "parse $(jq .[$i] ${CONFIG})" | ||||
|  | ||||
|         SCHEDULE=$(jq -r .[$i].schedule ${CONFIG} | sed 's/\*/\\*/g') | ||||
|         if [ "${SCHEDULE}" == "null" ]; then | ||||
|             echo "Schedule Missing: $(jq -r .[$i].schedule ${CONFIG})" | ||||
|             continue | ||||
|         fi | ||||
|         SCHEDULE=$(parse_schedule ${SCHEDULE} | sed 's/\\//g') | ||||
|  | ||||
|         if [ "$(jq -r .[$i].command ${CONFIG})" == "null" ]; then | ||||
|             echo "Command Missing: $(jq -r .[$i].command ${CONFIG})" | ||||
|             continue | ||||
|         fi | ||||
|  | ||||
|         TMP_JSON=$(jq -c .[$i] ${CONFIG}) | ||||
|         COMMAND=$(make_cmd) | ||||
|         if [ "$(jq -r .[$i].trigger ${CONFIG})" != "null" ]; then | ||||
|             while read j ; do | ||||
|                 echo "trigger parse $(jq .[$i].trigger[$j] ${CONFIG})" | ||||
|                 if [ "$(jq .[$i].trigger[$j].command ${CONFIG})" == "null" ]; then | ||||
|                     echo "Command Missing: $(jq -r .[$i].trigger[$j].command ${CONFIG})" | ||||
|                     continue | ||||
|                 fi | ||||
|                 TMP_JSON=$(jq -c .[$i].trigger[$j] ${CONFIG}) | ||||
|                 COMMAND="$COMMAND && $(make_cmd)" | ||||
|             done < <(jq -r '.['$i'].trigger|keys[]' ${CONFIG}) | ||||
|         fi | ||||
|  | ||||
|         NAME=$(jq -r .[$i].name ${CONFIG}) | ||||
|         COMMENT=$(jq -r .[$i].comment ${CONFIG}) | ||||
|         if [ "${NAME}" != "null" ] && [ "${COMMENT}" != "null" ]; then | ||||
|             echo "# ${NAME}: ${COMMENT}" >> ${CRONTAB_FILE} | ||||
|         elif [ "${COMMENT}" != "null" ]; then | ||||
|             echo "# ${COMMENT}" >> ${CRONTAB_FILE} | ||||
|         fi | ||||
|  | ||||
|         echo "${SCHEDULE} ${COMMAND}" >> ${CRONTAB_FILE} | ||||
|     done < <(jq -r '.|keys[]' ${CONFIG}) | ||||
|  | ||||
|     echo "crontab generation complete" | ||||
|     cat ${CRONTAB_FILE} | ||||
| } | ||||
|  | ||||
| # Used to pass json to functions - total hack, I know | ||||
| TMP_JSON= | ||||
|  | ||||
| if [ "$1" = "crond" ] && [ -f ${CONFIG} ]; then | ||||
|     build_crontab | ||||
| fi | ||||
|  | ||||
| echo "$@" | ||||
| exec "$@" | ||||
		Reference in New Issue
	
	Block a user