From faa77b77c8a7cb36cf7b829d530149a894424c83 Mon Sep 17 00:00:00 2001 From: Robert Dailey Date: Fri, 28 Aug 2020 14:52:50 -0500 Subject: [PATCH] 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 --- Dockerfile | 2 + README.md | 66 +++++++++++++++++++++++++--- scripts/cron-scripts/run-cron-php.sh | 1 - scripts/cron-tasks.sh | 22 +++++----- scripts/entrypoint.sh | 11 +++++ scripts/nextcloud-exec.sh | 18 ++++++++ test/shell-test/docker-compose.yml | 20 +++++++++ 7 files changed, 123 insertions(+), 17 deletions(-) create mode 100644 scripts/nextcloud-exec.sh create mode 100644 test/shell-test/docker-compose.yml diff --git a/Dockerfile b/Dockerfile index cea19f3..87a1883 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 diff --git a/README.md b/README.md index e5169ff..f791104 100644 --- a/README.md +++ b/README.md @@ -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`
+ 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`
+ 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`
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**
+> 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. diff --git a/scripts/cron-scripts/run-cron-php.sh b/scripts/cron-scripts/run-cron-php.sh index 7cd1f8e..ffd5169 100644 --- a/scripts/cron-scripts/run-cron-php.sh +++ b/scripts/cron-scripts/run-cron-php.sh @@ -1,2 +1 @@ -#!/usr/bin/env bash php -f /var/www/html/cron.php diff --git a/scripts/cron-tasks.sh b/scripts/cron-tasks.sh index e4c886b..597f675 100755 --- a/scripts/cron-tasks.sh +++ b/scripts/cron-tasks.sh @@ -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" diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh index 6351879..1bf14ca 100755 --- a/scripts/entrypoint.sh +++ b/scripts/entrypoint.sh @@ -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 diff --git a/scripts/nextcloud-exec.sh b/scripts/nextcloud-exec.sh new file mode 100644 index 0000000..a6d978f --- /dev/null +++ b/scripts/nextcloud-exec.sh @@ -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 "$@" +} diff --git a/test/shell-test/docker-compose.yml b/test/shell-test/docker-compose.yml new file mode 100644 index 0000000..d2508b2 --- /dev/null +++ b/test/shell-test/docker-compose.yml @@ -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