65 Commits

Author SHA1 Message Date
176cb2e29c fix: move back to root user to debug permissions more completely. 2025-01-05 11:05:58 -08:00
799ccc8edc fix: permissions for /opt/crontab. 2025-01-05 10:58:49 -08:00
18880befdb fix: LOG_DIR is not used anywhere, remove it. 2025-01-05 10:52:44 -08:00
1a540df103 fix: make /var/log/crontab writeable for everyone. 2025-01-05 10:46:14 -08:00
a87292bd6c chore: casing. 2024-12-31 10:53:17 -08:00
5172ed0b21 fix: use docker user. 2024-12-31 10:48:22 -08:00
a72e84783e chore: update pre-commit. 2024-12-31 10:47:59 -08:00
5aeeb19efb fix: missed id. 2023-06-25 19:39:09 -07:00
9c8b32cd22 chore: add discord notification to cleanup. 2023-06-25 19:37:31 -07:00
c3a1963b9c feat: cleanup old images on the 15th of the month. 2023-06-25 19:07:53 -07:00
c7a835217c chore: update and upgrade require the cache. 2023-06-24 14:39:30 -07:00
ac49cae228 chore: remove arm64. 2023-06-24 14:36:48 -07:00
4cfb3021b8 fix: typo. 2023-06-24 14:34:09 -07:00
e3bfdebe5d chore: move build to the repo rather than the dockerfiles repo. 2023-06-24 14:31:13 -07:00
ccaf6059e7 chore: remove forked build.yml. 2023-06-24 14:20:32 -07:00
2b46bb2949 chore: remove FUNDING.yml 2023-05-21 11:44:58 -07:00
899ec9c46e fix: allow multiple networks. 2023-05-21 11:43:46 -07:00
2b364b93a7 fix: fixing @random since it was broken and removing @every since it never worked. 2023-03-05 12:02:18 -08:00
022399e16a feat: adding TEST_MODE. 2023-03-04 18:31:55 -08:00
035cd4a906 feat: Major Updates
- removed support for `projects` as the feature was very incomplete and it served little purpose
- added support for common settings between jobs using `~~shared-settings` as a key in the config
- cleaned up some items that have long bugged me
  - better reuse of code
  - better variable naming
  - improved flow and readability
  - formatting to the logs
2023-02-18 18:42:20 -08:00
76b524fd17 chore: merge from private git repo. 2022-08-07 08:14:21 -07:00
c530324db3 Merge pull request #2 from SimplicityGuy/additional-fixes
fix: additional fixes
2022-07-16 11:28:48 -07:00
9da424985d fix: moving the top level key name to the "name" property. 2022-07-16 11:24:32 -07:00
31d0b732b2 chore: cleaning up echos. 2022-07-16 10:53:34 -07:00
e69b276707 chore: nicer formatting. 2022-07-16 10:45:41 -07:00
08bbceff95 fix: cleaning script creation. 2022-07-16 10:43:25 -07:00
0643e9ceae chore: moving line to be consistent. 2022-07-16 10:40:02 -07:00
9316c9f5c2 fix: address jq parsing errors when some properties are not set. 2022-07-16 10:22:43 -07:00
a523ad7d74 fix: reset COMMENT. 2022-07-16 09:49:17 -07:00
36ef24f15f chore: omit additional items. 2022-07-16 09:48:40 -07:00
c4da20ce83 fix: addressing issue with parsing json. 2022-07-16 09:29:04 -07:00
8f1d9200e0 chore: tabs to spaces. 2022-07-16 09:21:04 -07:00
2b1c20967d Merge pull request #1 from SimplicityGuy/fixes
fix: shellcheck fixes
2022-07-16 09:00:59 -07:00
1d844f8318 chore: missed one spot using previously set env var. 2022-07-16 08:58:33 -07:00
7f29f0621e chore: standardizing on env vars. 2022-07-16 08:51:38 -07:00
f0c30632bd chore: standardize on {}. 2022-07-16 08:43:14 -07:00
84265413ee chore: adding error handling. 2022-07-16 08:38:01 -07:00
c685687367 chore: rename function to better describe what it does. 2022-07-16 08:34:43 -07:00
dc18a5199d chore: reorganization. 2022-07-16 08:33:41 -07:00
99aa59ddb4 chore: whitespace and dead code cleanup. 2022-07-16 08:31:20 -07:00
f18275eb0d fix: simplifying logic. 2022-07-16 08:28:20 -07:00
882fb137f2 fix: SC1001, SC2018, SC2019 shellcheck fixes. 2022-07-16 08:25:09 -07:00
6d92c31545 fix: SC2005 and SC2046 shellcheck fixes. 2022-07-16 08:22:51 -07:00
dcd3e429f6 fix: SC2166 shellcheck fixes. 2022-07-16 08:17:58 -07:00
96e699c4d7 fix: SC2162 shellcheck fixes. 2022-07-16 08:17:17 -07:00
4d945526fe fix: SC2236 shellcheck fixes. 2022-07-16 08:16:22 -07:00
8c8e19c1c9 fix: SC2006 shellcheck fixes. 2022-07-16 08:15:18 -07:00
82875aba6e fix: SC2004 shellcheck fixes. 2022-07-16 08:14:30 -07:00
9701554b3c fix: SC2086 shellcheck fixes. 2022-07-16 08:13:11 -07:00
653dcb4091 chore: update alpine image. 2022-07-15 21:40:15 -07:00
be97512ec2 ci: kiss 2020-12-26 18:18:02 -07:00
89cf941fea ci: only allow supproted by docker 2020-12-26 18:13:22 -07:00
5611ac5014 ci: try with all platforms 2020-12-26 17:59:12 -07:00
b29a6bb3b3 ci: remove platform list 2020-12-26 17:55:52 -07:00
c2331fc9e7 Merge pull request #40 from chiqomar/master
Adding python conversion for TOML and YAML
2020-12-26 17:45:46 -07:00
88bed2bc4a Fixing entrypoint dealing with JSON mapping 2020-12-23 13:22:15 -05:00
0ef54cc06b Updating README, adding JSON mapping support for consistency 2020-12-23 13:08:05 -05:00
d9b4cecea0 ci: remove unsupported 2020-12-22 16:33:24 -07:00
e55dba16d7 ci: build all platforms 2020-12-22 15:21:41 -07:00
18e9954d74 Using RQ to change toml and yaml to JSON, removing all python stuff 2020-12-21 19:19:52 -05:00
b9bbeacae1 Merge branch 'main' of github.com:willfarrell/docker-crontab 2020-12-08 16:50:33 -05:00
200cd1313f Adding gitignore 2020-01-09 12:46:37 -05:00
5013ecbc3f Adding vscode to gitignore 2019-09-13 16:25:07 -04:00
4f9146b8f8 using tini, functions for most code in entrypoint 2019-09-03 13:15:31 -04:00
4a0d85f51a Adding python conversion for TOML and YAML; slightly more verbose syntax for dockerargs 2019-08-15 10:52:20 -04:00
17 changed files with 745 additions and 420 deletions

15
.github/FUNDING.yml vendored
View File

@ -1,12 +1,3 @@
# These are supported funding model platforms
github: [willfarrell]# Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
github: [SimplicityGuy]
ko_fi: robertwlodarczyk
custom: [paypal.me/RWlodarczyk]

View File

@ -1,48 +1,75 @@
name: build
---
name: crontab
on:
workflow_dispatch:
push:
branches:
- main
tags:
- '*'
pull_request:
branches:
- main
schedule:
- cron: '0 0 * * *'
- cron: '0 1 * * 6'
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.actor }}/crontab
jobs:
multi:
build-crontab:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
-
name: Checkout
uses: actions/checkout@v2
-
name: Set up QEMU
uses: docker/setup-qemu-action@v1
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Login to DockerHub
uses: docker/login-action@v1
- name: Checkout repository.
uses: actions/checkout@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
submodules: true
- if: github.ref == 'refs/heads/main'
name: Conditional(Set tag as `latest`)
run: echo "tag=willfarrell/crontab:latest" >> $GITHUB_ENV
- name: Log in to the GitHub Container Registry.
if: github.event_name != 'pull_request'
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GHCR_TOKEN }}
- if: startsWith(github.ref, 'refs/tags/')
name: Conditional(Set tag as `{version}`)
run: echo "tag=willfarrell/crontab:${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Extract metadata (tags, labels) for Docker.
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=ref,event=branch
type=ref,event=pr
type=schedule,pattern={{date 'YYYYMMDD'}}
-
name: Build and push
uses: docker/build-push-action@v2
- name: Set up QEMU.
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx.
uses: docker/setup-buildx-action@v2
with:
platforms: linux/amd64
- name: Build and push Docker image to GitHub Container Registry.
uses: docker/build-push-action@v4
with:
context: .
file: ./Dockerfile
push: true
tags: |
${{ env.tag }}
platforms: linux/amd64
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
provenance: true
sbom: true
- name: Send notification to Discord.
uses: sarisia/actions-status-discord@v1.12.0
if: always()
with:
webhook: ${{ secrets.DISCORD_WEBHOOK }}

39
.github/workflows/cleanup.yml vendored Normal file
View File

@ -0,0 +1,39 @@
---
name: cleanup
on:
schedule:
- cron: '0 0 15 * *'
env:
IMAGE_NAME: ${{ github.actor }}/docker-crontab
jobs:
cleanup-docker-crontab:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Delete Docker images older than a month.
id: cleanup-images
uses: snok/container-retention-policy@v2
with:
account-type: personal
cut-off: One month ago UTC
keep-at-least: 4
skip-tags: latest
image-names: ${{ env.IMAGE_NAME }}
token: ${{ secrets.GHCR_TOKEN }}
- name: Send notification to Discord.
uses: sarisia/actions-status-discord@v1.12.0
if: always()
with:
title: ${{ env.IMAGE_NAME }}
description: |
succeded cleanup : ${{ steps.cleanup-images.outputs.deleted }}
failed cleanup : ${{ steps.cleanup-images.outputs.failed }}
webhook: ${{ secrets.DISCORD_WEBHOOK }}

6
.gitignore vendored
View File

@ -1,4 +1,10 @@
.idea
*.iml
.vscode
.DS_Store
config.json
config.working.json
jobs/
projects/

32
.pre-commit-config.yaml Normal file
View File

@ -0,0 +1,32 @@
---
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: cef0300fd0fc4d2a87a85fa2093c6b283ea36f4b # frozen: v5.0.0
hooks:
- id: check-added-large-files
- id: check-executables-have-shebangs
- id: check-merge-conflict
- id: check-shebang-scripts-are-executable
- id: check-yaml
- id: detect-aws-credentials
- id: detect-private-key
- id: end-of-file-fixer
- id: mixed-line-ending
- id: trailing-whitespace
- repo: https://github.com/python-jsonschema/check-jsonschema
rev: cb3c2be894b151dff143b1baf6acbd55f2b7faed # frozen: 0.30.0
hooks:
- id: check-github-workflows
- repo: https://github.com/executablebooks/mdformat
rev: e20b1ac5acb8aba0b49d3a9109c6e6b58684ee83 # frozen: 0.7.21
hooks:
- id: mdformat
additional_dependencies:
- mdformat-gfm
- repo: https://github.com/hadolint/hadolint
rev: c3dc18df7a501f02a560a2cc7ba3c69a85ca01d3 # frozen: v2.13.1-beta
hooks:
- id: hadolint

View File

@ -1,12 +1,59 @@
FROM library/docker:stable
#hadolint ignore=DL3007
FROM alpine:latest AS builder
LABEL org.opencontainers.image.title="crontab builder" \
org.opencontainers.image.description="crontab builder" \
org.opencontainers.image.authors="robert@simplicityguy.com" \
org.opencontainers.image.source="https://github.com/SimplicityGuy/alertmanager-discord/blob/main/Dockerfile" \
org.opencontainers.image.licenses="MIT" \
org.opencontainers.image.created="$(date +'%Y-%m-%d')" \
org.opencontainers.image.base.name="docker.io/library/alpine"
ENV RQ_VERSION=1.0.2
WORKDIR /usr/bin/rq/
#hadolint ignore=DL3018
RUN apk update --quiet && \
apk upgrade --quiet && \
apk add --quiet --no-cache \
upx && \
rm /var/cache/apk/* && \
wget --quiet https://github.com/dflemstr/rq/releases/download/v${RQ_VERSION}/rq-v${RQ_VERSION}-x86_64-unknown-linux-musl.tar.gz && \
tar -xvf rq-v${RQ_VERSION}-x86_64-unknown-linux-musl.tar.gz && \
upx --brute rq
#hadolint ignore=DL3007
FROM docker:latest AS release
LABEL org.opencontainers.image.title="crontab" \
org.opencontainers.image.description="A docker job scheduler (aka crontab for docker)." \
org.opencontainers.image.authors="robert@simplicityguy.com" \
org.opencontainers.image.source="https://github.com/SimplicityGuy/docker-crontab/blob/main/Dockerfile" \
org.opencontainers.image.licenses="MIT" \
org.opencontainers.image.created="$(date +'%Y-%m-%d')" \
org.opencontainers.image.base.name="docker.io/library/docker"
ENV HOME_DIR=/opt/crontab
RUN apk add --no-cache --virtual .run-deps gettext bash jq \
&& mkdir -p ${HOME_DIR}/jobs ${HOME_DIR}/projects \
&& adduser -S docker -D
COPY docker-entrypoint /
ENTRYPOINT ["/docker-entrypoint"]
#hadolint ignore=DL3018
RUN apk update --quiet && \
apk upgrade --quiet && \
apk add --quiet --no-cache \
bash \
coreutils \
curl \
gettext \
jq \
tini \
wget && \
rm /var/cache/apk/* && \
rm -rf /etc/periodic /etc/crontabs/root && \
mkdir -p ${HOME_DIR}/jobs
COPY --from=builder /usr/bin/rq/rq /usr/local/bin
COPY entrypoint.sh /
ENTRYPOINT ["/sbin/tini", "--", "/entrypoint.sh"]
HEALTHCHECK --interval=5s --timeout=3s \
CMD ps aux | grep '[c]rond' || exit 1

View File

@ -1,57 +1,59 @@
# docker-crontab
# crontab
![crontab](https://github.com/SimplicityGuy/docker-crontab/actions/workflows/build.yml/badge.svg) ![License: MIT](https://img.shields.io/github/license/SimplicityGuy/docker-crontab) [![pre-commit](https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit)](https://github.com/pre-commit/pre-commit)
A simple wrapper over `docker` to all complex cron job to be run in other containers.
## Supported tags and Dockerfile links
- [`latest` (*Dockerfile*)](https://github.com/willfarrell/docker-crontab/blob/master/Dockerfile)
- [`0.6.0` (*Dockerfile*)](https://github.com/willfarrell/docker-crontab/blob/0.6.0/Dockerfile)
![](https://img.shields.io/docker/pulls/willfarrell/crontab "Total docker pulls") [![](https://images.microbadger.com/badges/image/willfarrell/crontab.svg)](http://microbadger.com/images/willfarrell/crontab "Get your own image badge on microbadger.com")
## Why?
Yes, I'm aware of [mcuadros/ofelia](https://github.com/mcuadros/ofelia) (~10MB), it was the main inspiration for this project.
Yes, I'm aware of [mcuadros/ofelia](https://github.com/mcuadros/ofelia) (>250MB when this was created), it was the main inspiration for this project.
A great project, don't get me wrong. It was just missing certain key enterprise features I felt were required to support where docker is heading.
## 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`.
- Ability to share settings between cron jobs using `~~shared-settings` as a key.
## Config file
The config file can be specified 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)
## Config.json
- `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.
- `schedule`: Crontab schedule syntax as described in https://en.wikipedia.org/wiki/Cron. Examples: `@hourly`, `@every 1h30m`, `* * * * *`. Required.
- `command`: Command to be run on in crontab container or 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. Optional.
- `container`: Full container name. Ignored if `image` is included. Optional.
- `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`
- `onstart`: Run the command on `crontab` container start, set to `true`. Optional, defaults to falsey.
- `trigger`: Array of docker-crontab subset objects. Sub-set includes: `image`, `container`, `command`, `dockerargs`
- `onstart`: Run the command on `crontab` container start, set to `true`. Optional, defaults to false.
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
[{
{
"logrotate": {
"schedule":"@every 5m",
"command":"/usr/sbin/logrotate /etc/logrotate.conf"
},{
},
"cert-regen": {
"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",
"dockerargs":"--it --env-file /opt/crontab/env/letsencrypt.env",
"volumes":["webapp_nginx_tls_cert:/etc/ssl", "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
}]
}
}
```
## How to use
@ -71,27 +73,26 @@ docker run -d \
### Use with docker-compose
1. Figure out which network name used for your docker-compose containers
* use `docker network ls` to see existing networks
* if your `docker-compose.yml` is in `my_dir` directory, you probably has network `my_dir_default`
* otherwise [read the docker-compose docs](https://docs.docker.com/compose/networking/)
2. Add `dockerargs` to your docker-crontab `config.json`
* use `--network NETWORK_NAME` to connect new container into docker-compose network
* use `--rm --name NAME` to use named container
* e.g. `"dockerargs": "--network my_dir_default --rm --name my-best-cron-job"`
- use `docker network ls` to see existing networks
- if your `docker-compose.yml` is in `my_dir` directory, you probably has network `my_dir_default`
- otherwise [read the docker-compose docs](https://docs.docker.com/compose/networking/)
1. Add `dockerargs` to your docker-crontab `config.json`
- use `--network NETWORK_NAME` to connect new container into docker-compose network
- use `--name NAME` to use named container
- e.g. `"dockerargs": "--it"`
### Dockerfile
```Dockerfile
FROM willfarrell/crontab
FROM registry.gitlab.com/simplicityguy/docker/crontab
COPY config.json ${HOME_DIR}/
```
### Logrotate Dockerfile
```Dockerfile
FROM willfarrell/crontab
FROM registry.gitlab.com/simplicityguy/docker/crontab
RUN apk add --no-cache logrotate
RUN echo "*/5 * * * * /usr/sbin/logrotate /etc/logrotate.conf" >> /etc/crontabs/logrotate
@ -99,17 +100,3 @@ COPY logrotate.conf /etc/logrotate.conf
CMD ["crond", "-f"]
```
### Logging - In Dev
All `stdout` is captured, formatted, and saved to `/var/log/crontab/jobs.log`. Set `LOG_FILE` to `/dev/null` to disable logging.
example: `e6ced859-1563-493b-b1b1-5a190b29e938 2017-06-18T01:27:10+0000 [info] Start Cronjob **map-a-vol** map a volume`
grok: `CRONTABLOG %{DATA:request_id} %{TIMESTAMP_ISO8601:timestamp} \[%{LOGLEVEL:severity}\] %{GREEDYDATA:message}`
## TODO
- [ ] Have ability to auto regenerate crontab on file change (signal HUP?)
- [ ] Run commands on host machine (w/ --privileged?)
- [ ] Write tests
- [ ] Setup TravisCI

View 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
}
]

View 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
}
}

View 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

View File

@ -0,0 +1,49 @@
# 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"

View 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

View File

@ -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
}]

View File

@ -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"

View File

@ -1,244 +0,0 @@
#!/usr/bin/env bash
set -e
if [ -z "$DOCKER_HOST" -a "$DOCKER_PORT_2375_TCP" ]; then
export DOCKER_HOST='tcp://docker:2375'
fi
# for local testing only
#HOME_DIR=.
if [ "${LOG_FILE}" == "" ]; then
LOG_DIR=/var/log/crontab
LOG_FILE=${LOG_DIR}/jobs.log
mkdir -p ${LOG_DIR}
touch ${LOG_FILE}
fi
CONFIG=${HOME_DIR}/config.json
DOCKER_SOCK=/var/run/docker.sock
CRONTAB_FILE=/etc/crontabs/docker
# Ensure dir exist - in case of volume mapping
mkdir -p ${HOME_DIR}/jobs ${HOME_DIR}/projects
ensure_docker_socket_accessible() {
if ! grep -q "^docker:" /etc/group; then
# Ensure 'docker' user has permissions for docker socket (without changing permissions)
DOCKER_GID=$(stat -c '%g' ${DOCKER_SOCK})
if [ "${DOCKER_GID}" != "0" ]; then
if ! grep -qE "^[^:]+:[^:]+:${DOCKER_GID}:" /etc/group; then
# No group with such gid exists - create group docker
addgroup -g ${DOCKER_GID} docker
adduser docker docker
else
# Group with such gid exists - add user "docker" to this group
DOCKER_GROUP_NAME=`getent group "${DOCKER_GID}" | awk -F':' '{{ print $1 }}'`
adduser docker $DOCKER_GROUP_NAME
fi
else
# Docker socket belongs to "root" group - add user "docker" to this group
adduser docker root
fi
fi
}
slugify() {
echo "$@" | iconv -t ascii | sed -r s/[~\^]+//g | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | tr A-Z a-z
}
make_image_cmd() {
DOCKERARGS=$(echo ${1} | jq -r .dockerargs)
if [ "${DOCKERARGS}" == "null" ]; then DOCKERARGS=; fi
IMAGE=$(echo ${1} | jq -r .image | envsubst)
TMP_COMMAND=$(echo ${1} | jq -r .command)
echo "docker run ${DOCKERARGS} ${IMAGE} ${TMP_COMMAND}"
}
make_container_cmd() {
DOCKERARGS=$(echo ${1} | jq -r .dockerargs)
if [ "${DOCKERARGS}" == "null" ]; then DOCKERARGS=; fi
SCRIPT_NAME=$(echo ${1} | jq -r .name)
SCRIPT_NAME=$(slugify $SCRIPT_NAME)
PROJECT=$(echo ${1} | jq -r .project)
CONTAINER=$(echo ${1} | jq -r .container | envsubst)
TMP_COMMAND=$(echo ${1} | jq -r .command)
if [ "${PROJECT}" != "null" ]; then
# create bash script to detect all running containers
if [ "${SCRIPT_NAME}" == "null" ]; then
SCRIPT_NAME=$(cat /proc/sys/kernel/random/uuid)
fi
cat << EOF > ${HOME_DIR}/projects/${SCRIPT_NAME}.sh
#!/usr/bin/env bash
set -e
CONTAINERS=\$(docker ps --format '{{.Names}}' | grep -E "^${PROJECT}_${CONTAINER}.[0-9]+")
for CONTAINER_NAME in \$CONTAINERS; do
docker exec ${DOCKERARGS} \${CONTAINER_NAME} ${TMP_COMMAND}
done
EOF
echo "/bin/bash ${HOME_DIR}/projects/${SCRIPT_NAME}.sh"
else
echo "docker exec ${DOCKERARGS} ${CONTAINER} ${TMP_COMMAND}"
fi
}
#make_host_cmd() {
# HOST_BINARY=$(echo ${1} | jq -r .host)
# TMP_COMMAND=$(echo ${1} | jq -r .command)
# echo "${HOST_BINARY} ${TMP_COMMAND}"
#}
make_cmd() {
if [ "$(echo ${1} | jq -r .image)" != "null" ]; then
make_image_cmd "$1"
elif [ "$(echo ${1} | jq -r .container)" != "null" ]; then
make_container_cmd "$1"
#elif [ "$(echo ${1} | jq -r .host)" != "null" ]; then
# make_host_cmd "$1"
else
echo ${1} | jq -r .command
fi
}
parse_schedule() {
case $1 in
"@yearly")
echo "0 0 1 1 *"
;;
"@annually")
echo "0 0 1 1 *"
;;
"@monthly")
echo "0 0 1 * *"
;;
"@weekly")
echo "0 0 * * 0"
;;
"@daily")
echo "0 0 * * *"
;;
"@midnight")
echo "0 0 * * *"
;;
"@hourly")
echo "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}
ONSTART=()
while read i ; do
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
COMMENT=$(jq -r .[$i].comment ${CONFIG})
if [ "${COMMENT}" != "null" ]; then
echo "# ${COMMENT}" >> ${CRONTAB_FILE}
fi
SCRIPT_NAME=$(jq -r .[$i].name ${CONFIG})
SCRIPT_NAME=$(slugify $SCRIPT_NAME)
if [ "${SCRIPT_NAME}" == "null" ]; then
SCRIPT_NAME=$(cat /proc/sys/kernel/random/uuid)
fi
COMMAND="/bin/bash ${HOME_DIR}/jobs/${SCRIPT_NAME}.sh"
cat << EOF > ${HOME_DIR}/jobs/${SCRIPT_NAME}.sh
#!/usr/bin/env bash
set -e
# TODO find workaround
# [error] write /dev/stdout: broken pipe <- when using docker commands
#UUID=\$(cat /proc/sys/kernel/random/uuid)
#exec > >(read message; echo "\${UUID} \$(date -Iseconds) [info] \$message" | tee -a ${LOG_FILE} )
#exec 2> >(read message; echo "\${UUID} \$(date -Iseconds) [error] \$message" | tee -a ${LOG_FILE} >&2)
echo "Start Cronjob **${SCRIPT_NAME}** ${COMMENT}"
$(make_cmd "$(jq -c .[$i] ${CONFIG})")
EOF
if [ "$(jq -r .[$i].trigger ${CONFIG})" != "null" ]; then
while read j ; do
if [ "$(jq .[$i].trigger[$j].command ${CONFIG})" == "null" ]; then
echo "Command Missing: $(jq -r .[$i].trigger[$j].command ${CONFIG})"
continue
fi
#TRIGGER_COMMAND=$(make_cmd "$(jq -c .[$i].trigger[$j] ${CONFIG})")
echo "$(make_cmd "$(jq -c .[$i].trigger[$j] ${CONFIG})")" >> ${HOME_DIR}/jobs/${SCRIPT_NAME}.sh
#COMMAND="${COMMAND} && ${TRIGGER_COMMAND}"
done < <(jq -r '.['$i'].trigger|keys[]' ${CONFIG})
fi
echo "echo \"End Cronjob **${SCRIPT_NAME}** ${COMMENT}\"" >> ${HOME_DIR}/jobs/${SCRIPT_NAME}.sh
echo "${SCHEDULE} ${COMMAND}" >> ${CRONTAB_FILE}
if [ "$(jq -r .[$i].onstart ${CONFIG})" == "true" ]; then
ONSTART+=("${COMMAND}")
fi
done < <(jq -r '.|keys[]' ${CONFIG})
echo "##### crontab generation complete #####"
cat ${CRONTAB_FILE}
echo "##### run commands with onstart #####"
for COMMAND in "${ONSTART[@]}"; do
echo "${COMMAND}"
${COMMAND} &
done
}
ensure_docker_socket_accessible
if [ "$1" = "crond" ]; then
if [ -f ${CONFIG} ]; then
build_crontab
else
echo "Unable to find ${HOME_DIR}/config.json"
fi
fi
echo "$@"
exec "$@"

263
entrypoint.sh Executable file
View File

@ -0,0 +1,263 @@
#!/bin/bash
set -e
DOCKER_SOCK=/var/run/docker.sock
CRONTAB_FILE=/etc/crontabs/docker
if [ -z "${HOME_DIR}" ] && [ -n "${TEST_MODE}" ]; then
HOME_DIR=/tmp/crontab-docker-testing
CRONTAB_FILE=${HOME_DIR}/test
elif [ -z "${HOME_DIR}" ]; then
echo "HOME_DIR not set."
exit 1
fi
# Ensure dir exist - in case of volume mapping.
mkdir -p "${HOME_DIR}"/jobs
if [ -z "${DOCKER_HOST}" ] && [ -a "${DOCKER_PORT_2375_TCP}" ]; then
export DOCKER_HOST="tcp://docker:2375"
fi
normalize_config() {
JSON_CONFIG={}
if [ -f "${HOME_DIR}/config.json" ]; then
JSON_CONFIG="$(cat "${HOME_DIR}"/config.json)"
elif [ -f "${HOME_DIR}/config.toml" ]; then
JSON_CONFIG="$(rq -t <<< "$(cat "${HOME_DIR}"/config.toml)")"
elif [ -f "${HOME_DIR}/config.yml" ]; then
JSON_CONFIG="$(rq -y <<< "$(cat "${HOME_DIR}"/config.yml)")"
elif [ -f "${HOME_DIR}/config.yaml" ]; then
JSON_CONFIG="$(rq -y <<< "$(cat "${HOME_DIR}"/config.yaml)")"
fi
jq -S -r '."~~shared-settings" as $shared | del(."~~shared-settings") | to_entries | map_values(.value + { name: .key } + $shared)' <<< "${JSON_CONFIG}" > "${HOME_DIR}"/config.working.json
}
ensure_docker_socket_accessible() {
if ! grep -q "^docker:" /etc/group; then
# Ensure 'docker' user has permissions for docker socket (without changing permissions).
DOCKER_GID=$(stat -c '%g' ${DOCKER_SOCK})
if [ "${DOCKER_GID}" != "0" ]; then
if ! grep -qE "^[^:]+:[^:]+:${DOCKER_GID}:" /etc/group; then
# No group with such gid exists - create group 'docker'.
addgroup -g "${DOCKER_GID}" docker
adduser docker docker
else
# Group with such gid exists - add user 'docker' to this group.
DOCKER_GROUP_NAME=$(getent group "${DOCKER_GID}" | awk -F':' '{{ print $1 }}')
adduser docker "${DOCKER_GROUP_NAME}"
fi
else
# Docker socket belongs to 'root' group - add user 'docker' to this group.
adduser docker root
fi
fi
}
slugify() {
echo "${@}" | iconv -t ascii | sed -r s/[~^]+//g | sed -r s/[^a-zA-Z0-9]+/-/g | sed -r s/^-+\|-+$//g | tr '[:upper:]' '[:lower:]'
}
make_image_cmd() {
DOCKERARGS=$(echo "${1}" | jq -r .dockerargs)
ENVIRONMENT=$(echo "${1}" | jq -r 'select(.environment != null) | .environment | map("--env " + .) | join(" ")')
EXPOSE=$(echo "${1}" | jq -r 'select(.expose != null) | .expose | map("--expose " + .) | join(" ")' )
NAME=$(echo "${1}" | jq -r 'select(.name != null) | .name')
NETWORKS=$(echo "${1}" | jq -r 'select(.networks != null) | .networks | map("--network " + .) | join(" ")')
PORTS=$(echo "${1}" | jq -r 'select(.ports != null) | .ports | map("--publish " + .) | join(" ")')
VOLUMES=$(echo "${1}" | jq -r 'select(.volumes != null) | .volumes | map("--volume " + .) | join(" ")')
if [ "${DOCKERARGS}" == "null" ]; then DOCKERARGS=; fi
DOCKERARGS+=" "
if [ -n "${ENVIRONMENT}" ]; then DOCKERARGS+="${ENVIRONMENT} "; fi
if [ -n "${EXPOSE}" ]; then DOCKERARGS+="${EXPOSE} "; fi
if [ -n "${NAME}" ]; then DOCKERARGS+="--name ${NAME} "; fi
if [ -n "${NETWORKS}" ]; then DOCKERARGS+="${NETWORKS} "; fi
if [ -n "${PORTS}" ]; then DOCKERARGS+="${PORTS} "; fi
if [ -n "${VOLUMES}" ]; then DOCKERARGS+="${VOLUMES} "; fi
IMAGE=$(echo "${1}" | jq -r .image | envsubst)
if [ "${IMAGE}" == "null" ]; then return; fi
COMMAND=$(echo "${1}" | jq -r .command)
echo "docker run ${DOCKERARGS} ${IMAGE} ${COMMAND}"
}
make_container_cmd() {
DOCKERARGS=$(echo "${1}" | jq -r .dockerargs)
if [ "${DOCKERARGS}" == "null" ]; then DOCKERARGS=; fi
CONTAINER=$(echo "${1}" | jq -r .container | envsubst)
if [ "${CONTAINER}" == "null" ]; then return; fi
COMMAND=$(echo "${1}" | jq -r .command )
if [ "${COMMAND}" == "null" ]; then return; fi
echo "docker exec ${DOCKERARGS} ${CONTAINER} ${COMMAND}"
}
make_cmd() {
if [ "$(echo "${1}" | jq -r .image)" != "null" ]; then
make_image_cmd "${1}"
elif [ "$(echo "${1}" | jq -r .container)" != "null" ]; then
make_container_cmd "${1}"
else
echo "${1}" | jq -r .command
fi
}
parse_schedule() {
IFS=" "
read -a params <<< "$@"
case ${params[0]} in
"@yearly" | "@annually")
echo "0 0 1 1 *"
;;
"@monthly")
echo "0 0 1 * *"
;;
"@weekly")
echo "0 0 * * 0"
;;
"@daily")
echo "0 0 * * *"
;;
"@midnight")
echo "0 0 * * *"
;;
"@hourly")
echo "0 * * * *"
;;
"@random")
M="*"
H="*"
D="*"
for when in "${params[@]:1}"
do
case $when in
"@m")
M=$(shuf -i 0-59 -n 1)
;;
"@h")
H=$(shuf -i 0-23 -n 1)
;;
"@d")
D=$(shuf -i 0-6 -n 1)
;;
esac
done
echo "${M} ${H} * * ${D}"
;;
*)
echo "${params[@]}"
;;
esac
}
function build_crontab() {
rm -rf "${CRONTAB_FILE}"
ONSTART=()
while read -r i ; do
KEY=$(jq -r .["$i"] "${CONFIG}")
SCHEDULE=$(echo "${KEY}" | jq -r '.schedule' | sed 's/\*/\\*/g')
if [ "${SCHEDULE}" == "null" ]; then
echo "'schedule' missing: '${KEY}"
continue
fi
SCHEDULE=$(parse_schedule "${SCHEDULE}" | sed 's/\\//g')
COMMAND=$(echo "${KEY}" | jq -r '.command')
if [ "${COMMAND}" == "null" ]; then
echo "'command' missing: '${KEY}'"
continue
fi
COMMENT=$(echo "${KEY}" | jq -r '.comment')
SCRIPT_NAME=$(echo "${KEY}" | jq -r '.name')
SCRIPT_NAME=$(slugify "${SCRIPT_NAME}")
if [ "${SCRIPT_NAME}" == "null" ]; then
SCRIPT_NAME=$(cat /proc/sys/kernel/random/uuid)
fi
CRON_COMMAND=$(make_cmd "${KEY}")
SCRIPT_PATH="${HOME_DIR}/jobs/${SCRIPT_NAME}.sh"
touch "${SCRIPT_PATH}"
chmod +x "${SCRIPT_PATH}"
{
echo "#\!/usr/bin/env bash"
echo "set -e"
echo ""
echo "echo \"start cron job __${SCRIPT_NAME}__\""
echo "${CRON_COMMAND}"
} > "${SCRIPT_PATH}"
TRIGGER=$(echo "${KEY}" | jq -r '.trigger')
if [ "${TRIGGER}" != "null" ]; then
while read -r j ; do
TRIGGER_KEY=$(echo "${KEY}" | jq -r .trigger["$j"])
TRIGGER_COMMAND=$(echo "${TRIGGER_KEY}" | jq -r '.command')
if [ "${TRIGGER_COMMAND}" == "null" ]; then
continue
fi
make_cmd "${TRIGGER_KEY}" >> "${SCRIPT_PATH}"
done < <(echo "${KEY}" | jq -r '.trigger | keys[]')
fi
echo "echo \"end cron job __${SCRIPT_NAME}__\"" >> "${SCRIPT_PATH}"
if [ "${COMMENT}" != "null" ]; then
echo "# ${COMMENT}" >> "${CRONTAB_FILE}"
fi
echo "${SCHEDULE} ${SCRIPT_PATH}" >> "${CRONTAB_FILE}"
ONSTART_COMMAND=$(echo "${KEY}" | jq -r '.onstart')
if [ "${ONSTART_COMMAND}" == "true" ]; then
ONSTART+=("${SCRIPT_PATH}")
fi
done < <(jq -r '. | keys[]' "${CONFIG}")
printf "##### crontab generated #####\n"
cat "${CRONTAB_FILE}"
printf "##### run commands with onstart #####\n"
for ONSTART_COMMAND in "${ONSTART[@]}"; do
printf "%s\n" "${ONSTART_COMMAND}"
${ONSTART_COMMAND} &
done
printf "##### cron running #####\n"
}
start_app() {
normalize_config
export CONFIG=${HOME_DIR}/config.working.json
if [ ! -f "${CONFIG}" ]; then
printf "missing generated %s. exiting.\n" "${CONFIG}"
exit 1
fi
if [ "${1}" == "crond" ]; then
build_crontab
fi
printf "%s\n" "${@}"
exec "${@}"
}
if [ -z "${TEST_MODE}" ]; then
ensure_docker_socket_accessible
fi
printf "✨ starting crontab container ✨\n"
start_app "${@}"

View File

@ -1,20 +0,0 @@
#!/usr/bin/env bash
set -e
# This file is for testing the logging of docker output #8
LOG_FILE=./jobs.log
touch ${LOG_FILE}
UUID="xxxxxxxxxxxxxxxxx"
exec > >(read message; echo "${UUID} $(date) [info] $message" | tee -a ${LOG_FILE} )
exec 2> >(read message; echo "${UUID} $(date) [error] $message" | tee -a ${LOG_FILE} >&2)
echo "Start"
docker run alpine sh -c 'while :; do echo "ping"; sleep 1; done'
# [error] write /dev/stdout: broken pipe
# --log-driver syslog <- errors
# --log-driver none <- errors
echo "End"