From 0adfe041730f2e4e057015d19f2ca4e0a772edff Mon Sep 17 00:00:00 2001 From: Gmer4Lfe Date: Fri, 8 May 2026 19:12:00 -0400 Subject: [PATCH] feat(docker): add docker_update.sh for daily container image pulls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pulls the latest image for each container in DAILY_RESTART_CONTAINERS before docker_daily_restart.sh runs. Containers stay running during the pull — no extra downtime. Mirrors the weekly update pattern exactly. Toggle: DAILY_CONTAINER_UPDATES=true/false in master.conf. Pull failures are non-fatal — the daily restart proceeds regardless. Co-Authored-By: Claude Sonnet 4.6 --- Docker_Essentials/docker_update.sh | 171 +++++++++++++++++++++++++++++ master.conf | 6 + 2 files changed, 177 insertions(+) create mode 100644 Docker_Essentials/docker_update.sh diff --git a/Docker_Essentials/docker_update.sh b/Docker_Essentials/docker_update.sh new file mode 100644 index 0000000..00289fb --- /dev/null +++ b/Docker_Essentials/docker_update.sh @@ -0,0 +1,171 @@ +#!/bin/bash +# ============================================================================================== +# ================================= Docker Update ============================================== +# ============================================================================================== +# Pulls the latest image for each container in HOST*_DAILY_RESTART_CONTAINERS. +# Called by daily_sync_maintenance.sh before docker_daily_restart.sh — containers stay +# running during the pull, so there is no extra downtime. +# +# ── WHY SAME LIST AS DAILY RESTART ─────────────────────────────────────────────────────────── +# Containers that restart daily (auth stack: Authelia, NPM, Mariadb, Redis, etc.) are +# exactly the containers that benefit from staying current. Reusing DAILY_RESTART_CONTAINERS +# means no second list to maintain — add/remove a container once and both update + restart +# reflect the change automatically. +# +# ── WHAT THIS DOES ──────────────────────────────────────────────────────────────────────────── +# docker pull — fetches the latest digest from the registry +# Old vs new image ID comparison — distinguishes "updated" from "already current" +# Containers keep running — pull does not affect the live container +# docker_daily_restart.sh runs after — containers restart on the fresh image +# +# ── TOGGLE ──────────────────────────────────────────────────────────────────────────────────── +# DAILY_CONTAINER_UPDATES=false in master.conf — skips all pulls, exits cleanly +# docker_daily_restart.sh still runs regardless — update and restart are independent +# +# ── CONFIGURATION ───────────────────────────────────────────────────────────────────────────── +# master.conf: DAILY_CONTAINER_UPDATES — enable/disable (default: true) +# master_host*.conf: HOST*_DAILY_RESTART_CONTAINERS — containers to update +# +# ── USAGE ───────────────────────────────────────────────────────────────────────────────────── +# docker_update.sh — normal run +# docker_update.sh --dry-run — show what would be pulled +# docker_update.sh --log — verbose output +# docker_update.sh --status — show config and exit +# ============================================================================================== + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +source "$SCRIPT_DIR/../load_config.sh" + +parse_args "$@" + +# ============================================================================================== +# ━━━ Setup ━━━ +# ============================================================================================== +if [[ "$EUID" -ne 0 ]]; then + error "Must be run as root" + exit 1 +fi + +if ! command -v docker &>/dev/null; then + error "Docker command not found" + exit 1 +fi + +detect_hosts + +if [[ "${DAILY_CONTAINER_UPDATES:-true}" != "true" ]]; then + log "DAILY_CONTAINER_UPDATES=false — skipping container updates" + exit 0 +fi + +if [[ ${#DAILY_RESTART_CONTAINERS[@]} -eq 0 ]]; then + warn "DAILY_RESTART_CONTAINERS is empty for $MY_ID — nothing to update" + warn "Check HOST${MY_ID#HOST}_DAILY_RESTART_CONTAINERS in master_host*.conf" + exit 0 +fi + +# ============================================================================================== +# ━━━ Status ━━━ +# ============================================================================================== +if [[ "$SHOW_STATUS" == true ]]; then + echo "" + echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━" + echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)" + echo "$ICON_CONTAINERS Containers: ${DAILY_RESTART_CONTAINERS[*]}" + echo "$ICON_GEAR Enabled: ${DAILY_CONTAINER_UPDATES:-true}" + echo "$ICON_GEAR Dry Run: $DRY_RUN" + echo "━━━━━━━━━━━━━━━━━━━━━━━" + exit 0 +fi + +[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no images will be pulled" + +# ============================================================================================== +# ━━━ Pull Updates ━━━ +# ============================================================================================== +echo "" +echo "━━━ $ICON_CONTAINERS Docker Update — $(date '+%Y-%m-%d %H:%M:%S') ━━━" +echo "$ICON_CONTAINERS Containers: ${DAILY_RESTART_CONTAINERS[*]}" +echo "" + +START=$(date +%s) +UPDATED=() +UP_TO_DATE=() +FAILED=() +SKIPPED=() + +for container in "${DAILY_RESTART_CONTAINERS[@]}"; do + [[ -z "$container" ]] && continue + echo "━━━ $ICON_CONTAINERS $container ━━━" + + if ! docker inspect "$container" &>/dev/null; then + warn "$container — not found, skipping" + SKIPPED+=("$container") + echo "" + continue + fi + + IMAGE=$(docker inspect --format='{{.Config.Image}}' "$container" 2>/dev/null) + if [[ -z "$IMAGE" ]]; then + warn "$container — could not determine image, skipping" + SKIPPED+=("$container") + echo "" + continue + fi + + log "$container — image: $IMAGE" + + if [[ "$DRY_RUN" == true ]]; then + warn "DRY RUN — would pull: $IMAGE" + UPDATED+=("$container") + echo "" + continue + fi + + # Capture image ID before pull to detect whether an update landed + OLD_ID=$(docker image inspect "$IMAGE" --format='{{.Id}}' 2>/dev/null || echo "") + + echo "$ICON_SYNC Pulling $IMAGE..." + if docker pull "$IMAGE" 2>&1 | grep -E "^(Status:|Digest:|Error|error)" | sed 's/^/ /'; then + NEW_ID=$(docker image inspect "$IMAGE" --format='{{.Id}}' 2>/dev/null || echo "") + + if [[ -n "$OLD_ID" ]] && [[ "$OLD_ID" != "$NEW_ID" ]]; then + echo "$ICON_DONE $container — updated ✅" + UPDATED+=("$container") + else + log "$container — already up to date" + UP_TO_DATE+=("$container") + fi + else + warn "$container — pull failed ($IMAGE)" + FAILED+=("$container") + fi + + echo "" +done + +END=$(date +%s) + +# ============================================================================================== +# ━━━ Summary ━━━ +# ============================================================================================== +echo "━━━━━ $ICON_SUMMARY DOCKER UPDATE SUMMARY ━━━━━" +echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)" +echo "$ICON_TIME Duration: $(format_duration $(( END - START )))" +[[ ${#UPDATED[@]} -gt 0 ]] && echo "$ICON_DONE Updated: ${UPDATED[*]}" +[[ ${#UP_TO_DATE[@]} -gt 0 ]] && echo "$ICON_RUNNING Up to date: ${UP_TO_DATE[*]}" +[[ ${#SKIPPED[@]} -gt 0 ]] && echo "$ICON_WARN Skipped: ${SKIPPED[*]}" +[[ ${#FAILED[@]} -gt 0 ]] && echo "$ICON_ERROR Failed: ${FAILED[*]}" + +if [[ "$DRY_RUN" == true ]]; then + warn "DRY RUN — no images pulled" +elif [[ ${#FAILED[@]} -eq 0 ]]; then + log "$ICON_DONE Status: done ✅ — ${#UPDATED[@]} updated, ${#UP_TO_DATE[@]} current" +else + warn "Status: ${#FAILED[@]} pull(s) failed — restart will proceed with existing images" +fi +echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" + +# Pull failures are non-fatal — restart proceeds regardless +exit 0 diff --git a/master.conf b/master.conf index 7a7e3e1..25b19d9 100644 --- a/master.conf +++ b/master.conf @@ -269,9 +269,15 @@ #"Media/lidarr_cleanup.sh" # remove orphaned music files — enable when ready #"Media/sonarr_cleanup.sh" # remove orphaned TV files — enable when ready #"Media/radarr_cleanup.sh" # remove orphaned movie files — enable when ready + "Docker_Essentials/docker_update.sh" # pull container image updates before restart "Docker_Essentials/docker_daily_restart.sh" # daily container restarts — runs last ) +# Pull latest images for DAILY_RESTART_CONTAINERS before the daily restart. +# Containers keep running during pull — no extra downtime. +# Set false to skip updates while still running the daily restart. + DAILY_CONTAINER_UPDATES=true + # Media shares synced daily by daily_sync_maintenance.sh. # Defined per-host in master_host*.conf — HOST1_DAILY_SYNC_SHARES and HOST2_DAILY_SYNC_SHARES. # Each server syncs only the shares it owns — direction is automatic.