#!/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