From 8e444a688f4175086e3e1b606a507b376ef76a4c Mon Sep 17 00:00:00 2001 From: mag37 Date: Sun, 11 May 2025 20:50:09 +0200 Subject: [PATCH] Update rework (#178) * first iteration rewriting the update logic * formatting fixes * Added an option to have compose up only target the specific container. Used with either -F flag, config variable or label. * Skipping update check on non-compose containers unless option is set * Versionbump Added new info and upped the version number. --- README.md | 21 ++++++------ default.config | 5 +-- dockcheck.sh | 88 +++++++++++++++++++++++++++++++++++--------------- 3 files changed, 77 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index e5ace87..8e8efae 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,15 @@ ___ ## :bell: Changelog +- **v0.6.4**: Restructured the update process - first pulls all updates, then recreates all containers. + - Added logic to skip update check on non-compose containers (unless `-r` option). + - Added option `-F` to revert to `compose up -d ` targeting specific container and not the stack. + - Also added corresponding label and config-option. + - Added markdown formatting to `notify_ntfy-sh.sh` template. - **v0.6.3**: Some fixes and changes: - Stops when a container recreation (compose up -d) fails, also `up`s the whole stack now. - `-M`, Markdown format url-releasenotes in notification (requires template rework, look at gotify!) - - Added [addons/DSM/README.md](./addons/DSM/README.md) added for more info Synology DSM info. + - Added [addons/DSM/README.md](./addons/DSM/README.md) for more info Synology DSM info. - Permission checks - graceful exit if no docker permissions + checking if root for pkg-manager. - **v0.6.2**: Style and colour changes, prometheus hotfix, new options: - `-u`, Allow auto self update of dockcheck.sh @@ -33,11 +38,6 @@ ___ - xargs/pipefail, removed `-set -e` bash option for now. - unbound variables fixed (hopefully) - dependency installer from pkgmanager rewritten -- **v0.6.0**: Refactored a lot of code, cleaner logic and syntax, safer variables. - - Safer bash options with `set -euo pipefail`, `shopt -s nullglob` and `failglob`. - - Added a `default.conf` for user settings - persistent through updates. - - Added `notify_slack.sh` template for slack curl api. - ___ @@ -54,7 +54,8 @@ Options: -c D Exports metrics as prom file for the prometheus node_exporter. Provide the collector textfile directory. -d N Only update to new images that are N+ days old. Lists too recent with +prefix and age. 2xSlower. -e X Exclude containers, separated by comma. --f Force stack restart after update. Caution: restarts once for every updated container within stack. +-f Force stop+start stack after update. Caution: restarts once for every updated container within stack. +-F Only compose up the specific container, not the whole compose stack (useful for master-compose structure). -h Print this Help. -i Inform - send a preconfigured notification. -I Prints custom releasenote urls alongside each container with updates (requires urls.list). @@ -204,11 +205,13 @@ See [discussion here](https://github.com/mag37/dockcheck/discussions/145). Optionally add labels to compose-files. Currently these are the usable labels: ``` labels: - mag37.dockcheck.restart-stack: true mag37.dockcheck.update: true + mag37.dockcheck.only-specific-container: true + mag37.dockcheck.restart-stack: true ``` -- `mag37.dockcheck.restart-stack: true` works instead of the `-f` option, forcing stop+restart on the whole compose-stack (Caution: Will restart on every updated container within stack). - `mag37.dockcheck.update: true` will when used with the `-l` option only update containers with this label and skip the rest. Will still list updates as usual. +- `mag37.dockcheck.only-specific-container: true` works instead of the `-F` option, specifying the updated container when doing compose up, like `docker compose up -d homer`. +- `mag37.dockcheck.restart-stack: true` works instead of the `-f` option, forcing stop+restart on the whole compose-stack (Caution: Will restart on every updated container within stack). ## :roller_coaster: Workaround for non **amd64** / **arm64** `regctl` provides binaries for amd64/arm64, to use on other architecture you could try this workaround. diff --git a/default.config b/default.config index 0a69d00..406ccbf 100644 --- a/default.config +++ b/default.config @@ -17,8 +17,9 @@ #DaysOld="5" # Only update to new images that are N+ days old. Lists too recent with +prefix and age. 2xSlower. #Stopped="-a" # Include stopped containers in the check. (Logic: docker ps -a). #OnlyLabel=true # Only update if label is set. See readme. -#ForceRestartStacks=true # Force stack restart after update. Caution. +#ForceRestartStacks=true # Force stop+start stack after update. Caution: restarts once for every updated container within stack. #DRunUp=true # Allow updating images for docker run, wont update the container. #MonoMode=true # Monochrome mode, no printf colour codes and hides progress bar. #PrintReleaseURL=true # Prints custom releasenote urls alongside each container with updates (requires urls.list)` -#PrintMarkdownURL=true # Prints custom releasenote urls as markdown +#PrintMarkdownURL=true # Prints custom releasenote urls as markdown +#OnlySpecific=true # Only compose up the specific container, not the whole compose. (useful for master-compose structure). diff --git a/dockcheck.sh b/dockcheck.sh index 1a2aa72..9e7de0b 100755 --- a/dockcheck.sh +++ b/dockcheck.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash -VERSION="v0.6.3" -### ChangeNotes: Permission checks, now compose up on whole stack, -M markdown option added. +VERSION="v0.6.4" +### ChangeNotes: Restructured update process - first pulls all images, then recreates all containers. Added -F option. Github="https://github.com/mag37/dockcheck" RawUrl="https://raw.githubusercontent.com/mag37/dockcheck/main/dockcheck.sh" @@ -34,7 +34,8 @@ Help() { echo "-c Exports metrics as prom file for the prometheus node_exporter. Provide the collector textfile directory." echo "-d N Only update to new images that are N+ days old. Lists too recent with +prefix and age. 2xSlower." echo "-e X Exclude containers, separated by comma." - echo "-f Force stack restart after update. Caution: restarts once for every updated container within stack." + echo "-f Force stop+start stack after update. Caution: restarts once for every updated container within stack." + echo "-F Only compose up the specific container, not the whole compose stack (useful for master-compose structure)." echo "-h Print this Help." echo "-i Inform - send a preconfigured notification." echo "-I Prints custom releasenote urls alongside each container with updates (requires urls.list)." @@ -72,6 +73,8 @@ Stopped=${Stopped:=""} CollectorTextFileDirectory=${CollectorTextFileDirectory:-} Exclude=${Exclude:-} DaysOld=${DaysOld:-} +OnlySpecific=false +SpecificContainer=${SpecificContainer:=""} Excludes=() GotUpdates=() NoUpdates=() @@ -88,13 +91,14 @@ c_blue="\033[0;34m" c_teal="\033[0;36m" c_reset="\033[0m" -while getopts "ayfhiIlmMnprsuvc:e:d:t:x:" options; do +while getopts "ayfFhiIlmMnprsuvc:e:d:t:x:" options; do case "${options}" in a|y) AutoMode=true ;; c) CollectorTextFileDirectory="${OPTARG}" ;; d) DaysOld=${OPTARG} ;; e) Exclude=${OPTARG} ;; f) ForceRestartStacks=true ;; + F) OnlySpecific=true ;; i) Notify=true ;; I) PrintReleaseURL=true ;; l) OnlyLabel=true ;; @@ -195,9 +199,6 @@ choosecontainers() { done fi done - printf "\n%bUpdating container(s):%b\n" "$c_blue" "$c_reset" - printf "%s\n" "${SelectedUpdates[@]}" - printf "\n" } datecheck() { @@ -385,6 +386,15 @@ check_image() { fi done + # Skipping non-compose containers unless option is set + ContLabels=$(docker inspect "$i" --format '{{json .Config.Labels}}') + ContPath=$($jqbin -r '."com.docker.compose.project.working_dir"' <<< "$ContLabels") + [[ "$ContPath" == "null" ]] && ContPath="" + if [[ -z "$ContPath" ]] && [[ "$DRunUp" == false ]]; then + printf "%s\n" "NoUpdates !$i - not checked, no compose file" + return + fi + local NoUpdates GotUpdates GotErrors ImageId=$(docker inspect "$i" --format='{{.Image}}') RepoUrl=$(docker inspect "$i" --format='{{.Config.Image}}') @@ -409,7 +419,7 @@ check_image() { # Make required functions and variables available to subprocesses export -f check_image datecheck export Excludes_string="${Excludes[*]:-}" # Can only export scalar variables -export t_out regbin RepoUrl DaysOld +export t_out regbin RepoUrl DaysOld DRunUp jqbin # Check for POSIX xargs with -P option, fallback without async if (echo "test" | xargs -P 2 >/dev/null 2>&1) && [[ "$MaxAsync" != 0 ]]; then @@ -483,10 +493,41 @@ if [[ -n "${GotUpdates:-}" ]]; then SelectedUpdates=( "${GotUpdates[@]}" ) fi if [[ "$DontUpdate" == false ]]; then + printf "\n%bUpdating container(s):%b\n" "$c_blue" "$c_reset" + printf "%s\n" "${SelectedUpdates[@]}" + NumberofUpdates="${#SelectedUpdates[@]}" + CurrentQue=0 - for i in "${SelectedUpdates[@]}" - do + for i in "${SelectedUpdates[@]}"; do + ((CurrentQue+=1)) + printf "\n%bNow updating (%s/%s): %b%s%b\n" "$c_teal" "$CurrentQue" "$NumberofUpdates" "$c_blue" "$i" "$c_reset" + ContLabels=$(docker inspect "$i" --format '{{json .Config.Labels}}') + ContImage=$(docker inspect "$i" --format='{{.Config.Image}}') + ContPath=$($jqbin -r '."com.docker.compose.project.working_dir"' <<< "$ContLabels") + [[ "$ContPath" == "null" ]] && ContPath="" + ContUpdateLabel=$($jqbin -r '."mag37.dockcheck.update"' <<< "$ContLabels") + [[ "$ContUpdateLabel" == "null" ]] && ContUpdateLabel="" + # Checking if Label Only -option is set, and if container got the label + [[ "$OnlyLabel" == true ]] && { [[ "$ContUpdateLabel" != true ]] && { echo "No update label, skipping."; continue; } } + + # Checking if compose-values are empty - hence started with docker run + if [[ -z "$ContPath" ]]; then + if [[ "$DRunUp" == true ]]; then + docker pull "$ContImage" + printf "%s\n" "$i got a new image downloaded, rebuild manually with preferred 'docker run'-parameters" + else + printf "\n%b%s%b has no compose labels, probably started with docker run - %bskipping%b\n\n" "$c_yellow" "$i" "$c_reset" "$c_yellow" "$c_reset" + fi + continue + fi + + docker pull "$ContImage" || { printf "\n%bDocker error, exiting!%b\n" "$c_red" "$c_reset" ; exit 1; } + done + printf "\n%bDone pulling updates. %bRecreating updated containers.%b\n" "$c_green" "$c_blue" "$c_reset" + + CurrentQue=0 + for i in "${SelectedUpdates[@]}"; do ((CurrentQue+=1)) unset CompleteConfs # Extract labels and metadata @@ -504,17 +545,12 @@ if [[ -n "${GotUpdates:-}" ]]; then [[ "$ContUpdateLabel" == "null" ]] && ContUpdateLabel="" ContRestartStack=$($jqbin -r '."mag37.dockcheck.restart-stack"' <<< "$ContLabels") [[ "$ContRestartStack" == "null" ]] && ContRestartStack="" + ContOnlySpecific=$($jqbin -r '."mag37.dockcheck.only-specific-container"' <<< "$ContLabels") + [[ "$ContRestartStack" == "null" ]] && ContRestartStack="" # Checking if compose-values are empty - hence started with docker run - if [[ -z "$ContPath" ]]; then - if [[ "$DRunUp" == true ]]; then - docker pull "$ContImage" - printf "%s\n" "$i got a new image downloaded, rebuild manually with preferred 'docker run'-parameters" - else - printf "\n%b%s%b has no compose labels, probably started with docker run - %bskipping%b\n\n" "$c_yellow" "$i" "$c_reset" "$c_yellow" "$c_reset" - fi - continue - fi + [[ -z "$ContPath" ]] && continue + # cd to the compose-file directory to account for people who use relative volumes cd "$ContPath" || { printf "\n%bPath error - skipping%b %s" "$c_red" "$c_reset" "$i"; continue; } ## Reformatting path + multi compose @@ -523,22 +559,22 @@ if [[ -n "${GotUpdates:-}" ]]; then else CompleteConfs=$(for conf in ${ContConfigFile//,/ }; do printf -- "-f %s/%s " "$ContPath" "$conf"; done) fi - printf "\n%bNow updating (%s/%s): %b%s%b\n" "$c_teal" "$CurrentQue" "$NumberofUpdates" "$c_blue" "$i" "$c_reset" - # Checking if Label Only -option is set, and if container got the label - [[ "$OnlyLabel" == true ]] && { [[ "$ContUpdateLabel" != true ]] && { echo "No update label, skipping."; continue; } } - docker pull "$ContImage" || { printf "\n%bDocker error, exiting!%b\n" "$c_red" "$c_reset" ; exit 1; } # Check if the container got an environment file set and reformat it ContEnvs="" if [[ -n "$ContEnv" ]]; then ContEnvs=$(for env in ${ContEnv//,/ }; do printf -- "--env-file %s " "$env"; done); fi + # Set variable when compose up should only target the specific container, not the stack + if [[ $OnlySpecific == true ]] || [[ $ContOnlySpecific == true ]]; then SpecificContainer="$ContName"; fi + + printf "\n%bNow recreating (%s/%s): %b%s%b\n" "$c_teal" "$CurrentQue" "$NumberofUpdates" "$c_blue" "$i" "$c_reset" # Check if the whole stack should be restarted if [[ "$ContRestartStack" == true ]] || [[ "$ForceRestartStacks" == true ]]; then - ${DockerBin} ${CompleteConfs} stop; ${DockerBin} ${CompleteConfs} ${ContEnvs} up -d + ${DockerBin} ${CompleteConfs} stop; ${DockerBin} ${CompleteConfs} ${ContEnvs} up -d || { printf "\n%bDocker error, exiting!%b\n" "$c_red" "$c_reset" ; exit 1; } else - ${DockerBin} ${CompleteConfs} ${ContEnvs} up -d || { printf "\n%bDocker error, exiting!%b\n" "$c_red" "$c_reset" ; exit 1; } + ${DockerBin} ${CompleteConfs} ${ContEnvs} up -d ${SpecificContainer} || { printf "\n%bDocker error, exiting!%b\n" "$c_red" "$c_reset" ; exit 1; } fi done if [[ "$AutoPrune" == false ]] && [[ "$AutoMode" == false ]]; then printf "\n"; read -rep "Would you like to prune dangling images? y/[n]: " AutoPrune; fi - if [[ "$AutoPrune" == true ]] || [[ "$AutoPrune" =~ [yY] ]]; then docker image prune -f; fi + if [[ "$AutoPrune" == true ]] || [[ "$AutoPrune" =~ [yY] ]]; then printf "\n Auto pruning.."; docker image prune -f; fi printf "\n%bAll done!%b\n" "$c_green" "$c_reset" else printf "\nNo updates installed, exiting.\n"