Support for custom shells

It is now possible to override and explicitly specify the shell you
would like used when executing cron tasks. This is accomplished with the
NEXTCLOUD_EXEC_SHELL environment variable, which defaults to bash. You
can also override and customize the arguments provided to that shell
executable via NEXTCLOUD_EXEC_SHELL_ARGS, which defaults to "-c".

See documentation for more detail and examples.

Fixes #6
This commit is contained in:
Robert Dailey 2020-08-28 14:52:50 -05:00
parent 08ec92f509
commit faa77b77c8
7 changed files with 123 additions and 17 deletions

View file

@ -6,6 +6,8 @@ ENV NEXTCLOUD_EXEC_USER=www-data
ENV NEXTCLOUD_CONTAINER_NAME=
ENV NEXTCLOUD_PROJECT_NAME=
ENV NEXTCLOUD_CRON_MINUTE_INTERVAL=15
ENV NEXTCLOUD_EXEC_SHELL=bash
ENV NEXTCLOUD_EXEC_SHELL_ARGS=-c
COPY scripts/*.sh /
COPY scripts/cron-scripts /cron-scripts

View file

@ -77,6 +77,17 @@ how this all works.
in the tasks being executed using the Nextcloud container's running user. Specifically, the
`--user` option will *not* be provided to the `docker exec` command.
* `NEXTCLOUD_EXEC_SHELL`<br>
Allows specifying a custom shell program that will be used to execute cron tasks inside the
Nextcloud container. This shell program *must* exist inside the Nextcloud container itself
(validation happens on start up to ensure this). The default value if not specified is `bash`.
* `NEXTCLOUD_EXEC_SHELL_ARGS`<br>
Allows custom arguments to be passed to the shell program specified by `NEXTCLOUD_EXEC_SHELL`. See
the detailed documentation provided later on in this document for more information. At minimum,
the arguments passed to your shell program must allow for the execution of a series of string
commands. The default value if not specified is `-c`.
* `DEBUG`<br>
Enables more verbose logging in core scripts. Useful only for development. To get more verbose
output in your own custom cron scripts, use `set -x` in the actual script.
@ -103,15 +114,15 @@ in addition to the default `cron.php` task. To add your custom tasks, follow the
1. Write a shell script that runs the commands that will be part of your task. This shell script
must have the `.sh` extension. An example of the contents of such a script is below. As an
optional security measure, do not make this shell script executable. The contents of the file are
piped into `bash`, so the executable bit is not used.
piped into `sh`, so the executable bit and shebang line are not used or required.
```sh
#!/usr/bin/env bash
php -f /var/www/html/cron.php
```
2. Mount this shell script inside the `/cron-scripts` directory. Here's an example if you're using
`docker-compose.yml`:
2. Mount this shell script inside the `/cron-scripts` directory. You may also choose to *replace*
this directory, but bear in mind that you will not be running the built-in cron tasks in that
case. Here's an example if you're using `docker-compose.yml`:
```yml
services:
@ -139,11 +150,42 @@ services:
As an optional safety measure, mount the directory or files as read-only (via the `:ro` at the end).
The container should not modify the files, but it doesn't hurt to be explicitly strict.
### Customizing the Shell
By default, all cron task scripts in the `/cron-scripts` directory are executed with `bash`.
However, not all Nextcloud containers have `bash`. In this case, you may want to override it with a
shell like `sh`. You can accomplish this (as well as customizing the arguments passed to the shell)
with `NEXTCLOUD_EXEC_SHELL` and `NEXTCLOUD_EXEC_SHELL_ARGS`.
The shell args are used when passing the contents of script files to the shell executable inside the
Nextcloud container. Customizing the args might be necessary depending on the shell program you
choose, or you may want to leverage options for debugging purposes (See "Debugging" section for
examples).
> **NOTE**<br>
> The arguments that are passed to the shell program must, at least, allow the execution of a string
> of commands. See the documentation on your chosen shell for which arguments these should be.
Here is an example of how you would override `bash` for `sh` using `docker-compose.yml` (again,
greatly simplified for example purposes; this is not a complete YAML):
```yml
services:
cron:
image: rcdailey/nextcloud-cronjob
environment:
- NEXTCLOUD_EXEC_SHELL=sh
- NEXTCLOUD_EXEC_SHELL_ARGS=-c
```
Note that `-c` is the default for `NEXTCLOUD_EXEC_SHELL_ARGS`, so it isn't necessary to specify it
above. However, it is explicitly specified for example purposes.
### Notes
* All cron task shell scripts run at the same interval defined by `NEXTCLOUD_CRON_MINUTE_INTERVAL`.
* Modification of your own shell scripts on the host do not require that you restart/recreate the
container.
container (only when volume mappings change in the YAML file).
## Debugging
@ -161,3 +203,17 @@ Started crond
> Running Script: run_cron_php.sh
> Done
```
You can leverage `NEXTCLOUD_EXEC_SHELL_ARGS` to get more verbose output from your scripts. For
example, for `bash` you can specify `-x` for debug mode. So you could use this in your YAML:
```yml
services:
cron:
image: rcdailey/nextcloud-cronjob
environment:
- NEXTCLOUD_EXEC_SHELL_ARGS=-xc
```
Note the addition of `-x` in the arguments. This will provide line-by-line output for each cron task
shell script executed.

View file

@ -1,2 +1 @@
#!/usr/bin/env bash
php -f /var/www/html/cron.php

View file

@ -2,16 +2,12 @@
set -e
[[ ! -z "$DEBUG" ]] && set -x
source /nextcloud-exec.sh
echo "-------------------------------------------------------------"
echo " Executing Cron Tasks: $(date)"
echo "-------------------------------------------------------------"
# If a user must be specified when executing the task, set up that option here.
# You may also leave NEXTCLOUD_EXEC_USER blank, in which case it will not be used.
if [[ -n "$NEXTCLOUD_EXEC_USER" ]]; then
exec_user="--user $NEXTCLOUD_EXEC_USER"
fi
# Obtain the ID of the container. We do this each iteration since the Nextcloud container may be
# recreated while the cron container is still running. We will need to check for a new container ID
# each time.
@ -23,13 +19,17 @@ fi
echo "> Nextcloud Container ID: ${containerId}"
run_scripts_in_dir() {
cd "$1"
for script in *.sh; do
echo "> Running Script: $script"
nextcloud_exec "$containerId" "$(< $script)"
done
}
# Loop through all shell scripts and execute the contents of those scripts in the Nextcloud
# container. It's done this way so that the user may mount more scripts to be executed in addition
# to the default ones.
cd /cron-scripts
for script in *.sh; do
echo "> Running Script: $script"
docker exec $exec_user -i "$containerId" bash < $script
done
run_scripts_in_dir /cron-scripts
echo "> Done"

View file

@ -2,6 +2,8 @@
set -e
[[ ! -z "$DEBUG" ]] && set -x
source /nextcloud-exec.sh
if [[ -z "$NEXTCLOUD_CONTAINER_NAME" ]]; then
echo "NEXTCLOUD_CONTAINER_NAME is a required variable"
exit 1
@ -24,6 +26,15 @@ else
echo "Found Nextcloud container with ID $containerId"
fi
# Verify the chosen shell exists in the Nextcloud container
if ! nextcloud_exec_no_shell "$containerId" sh -c "command -v \"$NEXTCLOUD_EXEC_SHELL\"" >/dev/null 2>&1
then
echo "ERROR: Shell \"$NEXTCLOUD_EXEC_SHELL\" does not exist in the Nextcloud container"
exit 1
else
echo "Chosen shell \"$NEXTCLOUD_EXEC_SHELL\" was found in the Nextcloud container"
fi
echo "*/$NEXTCLOUD_CRON_MINUTE_INTERVAL * * * * /cron-tasks.sh" \
> /var/spool/cron/crontabs/root

18
scripts/nextcloud-exec.sh Normal file
View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
nextcloud_exec_no_shell() {
containerId="$1"; shift
# If a user must be specified when executing the task, set up that option here.
# You may also leave NEXTCLOUD_EXEC_USER blank, in which case it will not be used.
if [[ -n "$NEXTCLOUD_EXEC_USER" ]]; then
exec_user="--user $NEXTCLOUD_EXEC_USER"
fi
docker exec $exec_user -i "$containerId" "$@"
}
nextcloud_exec() {
containerId="$1"; shift
nextcloud_exec_no_shell "$containerId" "$NEXTCLOUD_EXEC_SHELL" $NEXTCLOUD_EXEC_SHELL_ARGS "$@"
}

View file

@ -0,0 +1,20 @@
version: '3'
services:
app:
image: nextcloud:19-fpm-alpine
cronjob:
build: ../..
depends_on:
- app
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /etc/localtime:/etc/localtime:ro
environment:
- NEXTCLOUD_PROJECT_NAME=shell-test
- NEXTCLOUD_CONTAINER_NAME=app
- NEXTCLOUD_CRON_MINUTE_INTERVAL=1
- NEXTCLOUD_EXEC_SHELL=sh
- NEXTCLOUD_EXEC_SHELL_ARGS=-xc
- DEBUG=1