diff --git a/.VERSION b/.VERSION index 1eb805bc..c60d416f 100644 --- a/.VERSION +++ b/.VERSION @@ -1 +1 @@ -v6.1.0 \ No newline at end of file +v6.2.0-beta.1 \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a621160d..a685acf6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,7 @@ ## Changelog +### v6.2.0-beta.1 (2022-06-13) +* rewrite maintenance script ([#243 by @agross](https://github.com/buanet/ioBroker.docker/pull/243)) +* optimize container shutdown on SIGTERM ([as requested with #264 by @buzz0r](https://github.com/buanet/ioBroker.docker/pull/264)) ### v6.1.0 (2022-03-01) * v6.1.0-beta.2 (2022-02-11) @@ -8,8 +11,8 @@ * optimize startup script logging * add breaks and optimize maintenance script (fixes [#233](https://github.com/buanet/ioBroker.docker/issues/233)) * v6.1.0-beta.1 (2021-12-23) - * some more corrections in maintenance script ([#232 @agross](https://github.com/buanet/ioBroker.docker/pull/232)) - * add auto confirm parameter to upgrade function in maintenance script ([#229 @thost96](https://github.com/buanet/ioBroker.docker/pull/229)) + * some more corrections in maintenance script ([#232 by @agross](https://github.com/buanet/ioBroker.docker/pull/232)) + * add auto confirm parameter to upgrade function in maintenance script ([#229 by @thost96](https://github.com/buanet/ioBroker.docker/pull/229)) * add alias "m" for maintenance script ### v6.0.0 (2021-12-09) @@ -96,7 +99,7 @@ ### v4.1.0 (2020-01-17) * improved readme.md * v4.0.3-beta (2020-01-06) - * added support to restore backup on startup ([#56 @duffbeer2000](https://github.com/buanet/ioBroker.docker/pull/56)) + * added support to restore backup on startup ([#56 by @duffbeer2000](https://github.com/buanet/ioBroker.docker/pull/56)) * small fixes according to "docker best practices" * v4.0.2-beta (2019-12-10) * ~~added env for activating redis~~ @@ -116,13 +119,13 @@ * v3.1.2-beta (2019-09-03) * using node 10 instead of node 8 * v3.1.1-beta (2019-09-02) - * adding env for setting uid/ gid for iobroker-user ([#33 @mplogas](https://github.com/buanet/ioBroker.docker/pull/33)) + * adding env for setting uid/ gid for iobroker-user ([#33 by @mplogas](https://github.com/buanet/ioBroker.docker/pull/33)) ### v3.1.0 (2019-08-21) * v3.0.3-beta (2019-08-21) * switching base image from "debian:latest" to "debian:stretch" * v3.0.2-beta (2019-06-13) - * using gosu instead of sudo ([#26 @SchumyHao](https://github.com/buanet/ioBroker.docker/pull/26)) + * using gosu instead of sudo ([#26 by @SchumyHao](https://github.com/buanet/ioBroker.docker/pull/26)) * changing output of ioBroker logging * v3.0.1-beta (2019-05-18) * ~~switching back to iobroker-daemon for startup~~ diff --git a/debian/scripts/iobroker_startup.sh b/debian/scripts/iobroker_startup.sh index f8e7122b..64aa9312 100644 --- a/debian/scripts/iobroker_startup.sh +++ b/debian/scripts/iobroker_startup.sh @@ -19,6 +19,8 @@ statesdbtype=$IOB_STATESDB_TYPE usbdevices=$USBDEVICES zwave=$ZWAVE +pkill_timeout=10 # timeout for iobroker shutdown in seconds + # Getting date and time for logging dati=`date '+%Y-%m-%d %H:%M:%S'` @@ -497,8 +499,35 @@ echo "running" > /opt/scripts/.docker_config/.healthcheck shut_down() { echo ' ' echo "Recived termination signal (SIGTERM)." - echo "Shutting down ioBroker..." - pkill -SIGTERM -u iobroker -f iobroker.js-controller + echo -n "Shutting down ioBroker" + + local status timeout + + timeout="$(date --date="now + $pkill_timeout sec" +%s)" + pkill -u iobroker -f iobroker.js-controller + status=$? + if (( status >= 2 )); then # syntax error or fatal error + return 1 + fi + + if (( status == 1 )); then # no processes matched + return + fi + + # pgrep exits with status 1 when there are no matches + while pgrep -u iobroker > /dev/null; (( $? != 1 )); do + if (($(date +%s) > timeout)); then + echo -e '\nTimeout reached. Killing remaining processes...' + pkill --signal SIGKILL -u iobroker + echo 'Done.' + exit + fi + + echo -n '.' + sleep 1 + done + + echo -e '\nDone.' exit } diff --git a/debian/scripts/maintenance.sh b/debian/scripts/maintenance.sh index 8b89fc7c..6b09c968 100644 --- a/debian/scripts/maintenance.sh +++ b/debian/scripts/maintenance.sh @@ -1,234 +1,228 @@ -#!/bin/bash +#!/usr/bin/env bash -############################ -##### default settings ##### -############################ +# bash strict mode +set -euo pipefail -autoconfirm=no # could be set to true by commandline option -killbyname=no # could be set to true by commandline option / undocumented, only for use with backitup restore scripts - -#################################### -##### declaration of functions ##### -#################################### +autoconfirm= # can be set to 'yes' by command line option +killbyname= # can be set to 'yes' by command line option (undocumented, only for use with backitup restore scripts) +healthcheck=/opt/scripts/.docker_config/.healthcheck # path of healthcheck file +pkill_timeout=10 # timeout for stopping iobroker in seconds # display help text display_help() { - echo "This script is build to manage your ioBroker container!" + echo 'This script helps you manage your ioBroker container!' echo '' echo "Usage: maintenance [ COMMAND ] [ OPTION ]" echo " maint [ COMMAND ] [ OPTION ]" echo " m [ COMMAND ] [ OPTION ]" echo '' - echo "COMMANDS" - echo "------------------" - echo " status > reports the current state of maintenance mode" - echo " on > switches mantenance mode ON" - echo " off > switches mantenance mode OFF and shuts down or restarts container" - echo " upgrade > will put container to maintenance mode and upgrade ioBroker" - echo " help > shows this help" + echo 'COMMANDS' + echo '------------------' + echo ' status > reports the current state of maintenance mode' + echo ' on > switches mantenance mode ON' + echo ' off > switches mantenance mode OFF and stops or restarts the container' + echo ' upgrade > puts the container to maintenance mode and upgrades ioBroker' + echo ' help > shows this help' echo '' - echo "OPTIONS" - echo "------------------" - echo " -y|--yes > confirms the used command without asking" - echo " -h|--help > shows this help" + echo 'OPTIONS' + echo '------------------' + echo ' -y|--yes > confirms the used command without asking' + echo ' -h|--help > shows this help' echo '' - exit 0 } -# checking maintenance mode status -check_status() { - if [ $(cat /opt/scripts/.docker_config/.healthcheck) == 'maintenance' ] - then +# check maintenance enabled +maintenance_enabled() { + [[ -f "$healthcheck" && "$(cat "$healthcheck")" == maintenance ]] +} + +# display maintenance status +maintenance_status() { + if maintenance_enabled; then echo 'Maintenance mode is turned ON.' - elif [ $(cat /opt/scripts/.docker_config/.healthcheck) != 'maintenance' ] - then + else echo 'Maintenance mode is turned OFF.' fi } -# turn maintenance mode ON -switch_on() { - if [ $(cat /opt/scripts/.docker_config/.healthcheck) != 'maintenance' ] && [ "$killbyname" == "yes" ] # maintenance mode OFF / killbyname = yes / undocumented, only for use with backitup restore scripts - then +# enable maintenance mode +enable_maintenance() { + if maintenance_enabled; then + echo 'Maintenance mode is already turned ON.' + return + fi + + if [[ "$killbyname" == yes ]]; then + # undocumented option, only for use with backitup restore scripts echo 'This command will activate maintenance mode and stop js-controller.' echo 'Activating maintenance mode...' - echo "maintenance" > /opt/scripts/.docker_config/.healthcheck - sleep 1 - echo 'Done.' - echo 'Stopping ioBroker...' - pkill -u iobroker -f iobroker.js-controller + echo 'maintenance' > "$healthcheck" sleep 1 echo 'Done.' - exit 0 - elif [ $(cat /opt/scripts/.docker_config/.healthcheck) != 'maintenance' ] && [ "$autoconfirm" == "no" ] # maintenance mode OFF / autoconfirm = no - then - echo 'You are now going to stop ioBroker and activating maintenance mode for this container.' - read -p 'Do you want to continue [yes/no]? ' A - if [ "$A" == "y" ] || [ "$A" == "Y" ] || [ "$A" == "yes" ] - then - echo 'Activating maintenance mode...' - echo "maintenance" > /opt/scripts/.docker_config/.healthcheck - sleep 1 - echo 'Done.' - echo 'Stopping ioBroker...' - pkill -u iobroker - sleep 1 - echo 'Done.' - exit 0 + echo -n 'Stopping ioBroker...' + stop_iob + return + fi + + echo 'You are now going to stop ioBroker and activate maintenance mode for this container.' + + if [[ "$autoconfirm" != yes ]]; then + local reply + + read -rp 'Do you want to continue [yes/no]? ' reply + if [[ "$reply" == y || "$reply" == Y || "$reply" == yes ]]; then + : # continue else - exit 0 + return 1 fi - elif [ $(cat /opt/scripts/.docker_config/.healthcheck) != 'maintenance' ] && [ "$autoconfirm" == "yes" ] # maintenance mode OFF / autoconfirm = yes - then - echo 'You are now going to stop ioBroker and activating maintenance mode for this container.' - echo 'This command was already confirmed by -y or --yes option.' - echo 'Activating maintenance mode...' - echo "maintenance" > /opt/scripts/.docker_config/.healthcheck - sleep 1 - echo 'Done.' - echo 'Stopping ioBroker...' - pkill -u iobroker - sleep 1 - echo 'Done.' - exit 0 else - echo 'Maintenance mode is already turned ON.' + echo 'This command was already confirmed by the -y or --yes option.' fi + + echo 'Activating maintenance mode...' + echo 'maintenance' > "$healthcheck" + sleep 1 + echo 'Done.' + echo -n 'Stopping ioBroker...' + stop_iob } -# turn maintenance mode OFF -switch_off() { - if [ $(cat /opt/scripts/.docker_config/.healthcheck) == 'maintenance' ] && [ "$autoconfirm" == "no" ] # maintenance mode ON / autoconfirm = no - then - echo 'You are now going to deactivate maintenance mode for this container.' - echo 'Depending on the restart policy, your container will be stopped or restarted immediately.' - read -p 'Do you want to continue [yes/no]? ' A - if [ "$A" == "y" ] || [ "$A" == "Y" ] || [ "$A" == "yes" ] - then - echo 'Deactivating maintenance mode and forcing container to stop or restart...' - echo "stopping" > /opt/scripts/.docker_config/.healthcheck - pkill -u root - echo 'Done.' - exit 0 +# disable maintenance mode +disable_maintenance() { + if ! maintenance_enabled; then + echo 'Maintenance mode is already turned OFF.' + return + fi + + echo 'You are now going to deactivate maintenance mode for this container.' + echo 'Depending on the restart policy, your container will be stopped or restarted immediately.' + + if [[ "$autoconfirm" != yes ]]; then + local reply + + read -rp 'Do you want to continue [yes/no]? ' reply + if [[ "$reply" == y || "$reply" == Y || "$reply" == yes ]]; then + : # continue else - exit 0 + return 1 fi - elif [ $(cat /opt/scripts/.docker_config/.healthcheck) == 'maintenance' ] && [ "$autoconfirm" == "yes" ] # maintenance mode ON / autoconfirm = yes - then - echo 'You are now going to deactivate maintenance mode for this container.' - echo 'Depending on the restart policy, your container will be stopped or restarted immediately.' - echo 'This command was already confirmed by -y or --yes option.' - echo 'Deactivating maintenance mode and forcing container to stop or restart...' - echo "stopping" > /opt/scripts/.docker_config/.healthcheck - pkill -u root - echo 'Done.' - exit 0 else - echo 'Maintenance mode is already turned OFF.' + echo 'This command was already confirmed by the -y or --yes option.' fi + + echo 'Deactivating maintenance mode and forcing container to stop or restart...' + echo 'stopping' > "$healthcheck" + pkill -u root + echo 'Done.' } # upgrade js-controller -upgrade() { +upgrade_jscontroller() { echo 'You are now going to upgrade your js-controller.' echo 'As this will change data in /opt/iobroker, make sure you have a backup!' - echo 'During the upgrade process the container will automatically switch into maintenance mode and stop ioBroker.' - echo 'Depending of the restart policy, your container will be stopped or restarted automatically after the upgrade.' - - if [ "$autoconfirm" == "no" ] - then - read -p 'Do you want to continue [yes/no]? ' A - if [ "$A" == "y" ] || [ "$A" == "Y" ] || [ "$A" == "yes" ] - then - : # Continue. + echo 'During the upgrade process, the container will automatically switch into maintenance mode and stop ioBroker.' + echo 'Depending on the restart policy, your container will be stopped or restarted automatically after the upgrade.' + + if [[ "$autoconfirm" != yes ]]; then + local reply + + read -rp 'Do you want to continue [yes/no]? ' reply + if [[ "$reply" == y || "$reply" == Y || "$reply" == yes ]]; then + : # continue else - exit 0 + return 1 fi - elif [ "$autoconfirm" == "yes" ] - then - echo 'This command was already confirmed by -y or --yes option.' + else + echo 'This command was already confirmed by the -y or --yes option.' fi - if [ $(cat /opt/scripts/.docker_config/.healthcheck) != 'maintenance' ] - then - echo 'Activating maintenance mode...' - echo "maintenance" > /opt/scripts/.docker_config/.healthcheck - sleep 1 - echo 'Done.' - echo 'Stopping ioBroker...' - pkill -u iobroker - sleep 5 - echo 'Done.' + + if ! maintenance_enabled > /dev/null; then + autoconfirm=yes + enable_maintenance fi + echo 'Upgrading js-controller...' iobroker update sleep 1 iobroker upgrade self sleep 1 echo 'Done.' + echo 'Container will be stopped or restarted in 5 seconds...' sleep 5 - echo "stopping" > /opt/scripts/.docker_config/.healthcheck + echo 'stopping' > "$healthcheck" pkill -u root - exit 0 } -######################################## -##### parsing commands and options ##### -######################################## +# stop iobroker and wait until all processes stopped or pkill_timeout is reached +stop_iob() { + local status timeout -# reading all arguments and putting them in reverse -reverse= -for i in "$@"; do - reverse="$i $reverse" -done + timeout="$(date --date="now + $pkill_timeout sec" +%s)" + pkill -u iobroker -f iobroker.js-controller + status=$? + if (( status >= 2 )); then # syntax error or fatal error + return 1 + fi -# checking the arguments -for i in $reverse; do - case $i in + if (( status == 1 )); then # no processes matched + return + fi + + # pgrep exits with status 1 when there are no matches + while pgrep -u iobroker > /dev/null; (( $? != 1 )); do + if (($(date +%s) > timeout)); then + echo -e '\nTimeout reached. Killing remaining processes...' + pgrep --list-full -u iobroker + pkill --signal SIGKILL -u iobroker + echo 'Done.' + return + fi + + echo -n '.' + sleep 1 + done + + echo -e '\nDone.' +} + +# parsing commands and options + +# default command to run unless another was given +run=(display_help) + +for arg in "$@"; do + case $arg in help|-h|--help) - display_help # calling function to display help text - break + run=(display_help) ;; status) - check_status # calling function to check maintenance mode status - break + run=(maintenance_status) ;; on) - switch_on # calling function to switch maintenance mode on - break + run=(enable_maintenance) ;; off) - switch_off # calling function to switch maintenance mode off - break + run=(disable_maintenance) ;; upgrade) - upgrade # calling function to upgrade js-controller - break + run=(upgrade_jscontroller) ;; -y|--yes) - autoconfirm=yes # setting autoconfrm option to "yes" - shift + autoconfirm=yes ;; -kbn|--killbyname) - killbyname=yes # setting killbyname option to "yes" - shift + killbyname=yes ;; - -a=*|--argument=*) # dummy exaple for parsing option with value - ARGUMENT="${i#*=}" - shift - ;; - --) # End of all options. - shift - break - ;; - -?*|?*) - echo 'WARN: Unknown parameter. Please try again or see help (-h|--help).' + --) break ;; - *) # Default case: No more options, so break out of the loop. - break + *) + >&2 echo "Unknown parameter: $arg" + >&2 echo 'Please try again or see help (help|-h|--help).' + exit 1 ;; esac done -exit 0 +"${run[@]}" \ No newline at end of file