231
update.sh
231
update.sh
@@ -1,6 +1,15 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# ==========================================
|
||||
# 📦🐳 LXC Updater (TM) - update.sh
|
||||
# - Logs par date : /var/log/lxc-updater-TM/YYYY-MM-DD/
|
||||
# - Spinner sur chaque action
|
||||
# - Mise à jour OS (apt/apk) + Docker (compose ou run)
|
||||
# - Docker Compose : pull + up -d --remove-orphans (recreate safe)
|
||||
# - Docker Run : pull + recreate auto (si jq dispo) sinon restart
|
||||
# ==========================================
|
||||
|
||||
MODE="${1:-unknown}"
|
||||
|
||||
# 📁 Logs par date
|
||||
@@ -80,7 +89,9 @@ run_cmd() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# 🔎 APT : nombre de paquets upgradables
|
||||
# ------------------------------
|
||||
# 📦 Mise à jour OS
|
||||
# ------------------------------
|
||||
apt_upgradable_count() {
|
||||
LC_ALL=C apt-get -s upgrade 2>/dev/null | grep -c '^Inst ' || true
|
||||
}
|
||||
@@ -112,7 +123,13 @@ update_apk() {
|
||||
log "✅ Mise à jour APK terminée"
|
||||
}
|
||||
|
||||
# Compose: détecter dirs via labels + vérifier compose file
|
||||
# ------------------------------
|
||||
# 🐳 Mise à jour Docker
|
||||
# ------------------------------
|
||||
docker_image_id() {
|
||||
docker image inspect -f '{{.Id}}' "$1" 2>/dev/null || true
|
||||
}
|
||||
|
||||
compose_dirs_from_labels() {
|
||||
docker ps -q | while read -r cid; do
|
||||
docker inspect -f '{{ index .Config.Labels "com.docker.compose.project.working_dir" }}' "$cid" 2>/dev/null || true
|
||||
@@ -130,7 +147,6 @@ update_compose_dir() {
|
||||
|
||||
if docker compose version >/dev/null 2>&1; then
|
||||
run_cmd "Téléchargement des images (Compose)" bash -c "cd '$dir' && docker compose pull"
|
||||
# 🔁 Recréation SAFE (volumes conservés)
|
||||
run_cmd "Redéploiement des services (Compose)" bash -c "cd '$dir' && docker compose up -d --remove-orphans"
|
||||
return 0
|
||||
fi
|
||||
@@ -145,43 +161,189 @@ update_compose_dir() {
|
||||
return 2
|
||||
}
|
||||
|
||||
update_docker_compose() {
|
||||
log "🐳 Mise à jour Docker (Compose)"
|
||||
recreate_docker_run_container() {
|
||||
local name="$1"
|
||||
local image="$2"
|
||||
|
||||
run_cmd "Vérification Docker" docker ps
|
||||
|
||||
log "🔍 Recherche de projets Docker Compose"
|
||||
mapfile -t dirs < <(compose_dirs_from_labels)
|
||||
|
||||
valid=()
|
||||
for d in "${dirs[@]}"; do
|
||||
if is_compose_dir_valid "$d"; then
|
||||
valid+=("$d")
|
||||
else
|
||||
log "⚠️ Projet ignoré (chemin invalide ou sans compose.yml) : $d"
|
||||
fi
|
||||
done
|
||||
|
||||
if (( ${#valid[@]} == 0 )); then
|
||||
log "⚠️ Aucun projet Compose détecté"
|
||||
# jq obligatoire pour recréer proprement
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
run_cmd "Redémarrage du conteneur : $name" docker restart "$name"
|
||||
log "⚠️ 'jq' absent → redémarrage uniquement (recréation automatique impossible)"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "📁 Projet(s) Compose détecté(s) : ${#valid[@]}"
|
||||
for d in "${valid[@]}"; do
|
||||
local tmp="/tmp/lxc-updater-${name}.json"
|
||||
docker inspect "$name" > "$tmp" 2>>"$LOG_FILE" || true
|
||||
|
||||
local restart_name network_mode user workdir
|
||||
restart_name="$(jq -r '.[0].HostConfig.RestartPolicy.Name // empty' "$tmp")"
|
||||
network_mode="$(jq -r '.[0].HostConfig.NetworkMode // "default"' "$tmp")"
|
||||
user="$(jq -r '.[0].Config.User // empty' "$tmp")"
|
||||
workdir="$(jq -r '.[0].Config.WorkingDir // empty' "$tmp")"
|
||||
|
||||
mapfile -t envs < <(jq -r '.[0].Config.Env[]? // empty' "$tmp")
|
||||
mapfile -t binds < <(jq -r '.[0].HostConfig.Binds[]? // empty' "$tmp")
|
||||
|
||||
# Ports: containerPort/proto = HostIp:HostPort
|
||||
mapfile -t port_rules < <(
|
||||
jq -r '.[0].HostConfig.PortBindings | to_entries[]? | "\(.key)=\(.value[0].HostIp // ""):\(.value[0].HostPort)"' "$tmp"
|
||||
)
|
||||
|
||||
# Réseaux (si default/bridge)
|
||||
mapfile -t nets < <(jq -r '.[0].NetworkSettings.Networks | keys[]? // empty' "$tmp")
|
||||
|
||||
local new_name="${name}__new"
|
||||
docker rm -f "$new_name" >>"$LOG_FILE" 2>&1 || true
|
||||
|
||||
args=(docker create --name "$new_name")
|
||||
|
||||
if [[ -n "$restart_name" && "$restart_name" != "no" ]]; then
|
||||
args+=(--restart "$restart_name")
|
||||
fi
|
||||
[[ -n "$user" ]] && args+=(--user "$user")
|
||||
[[ -n "$workdir" ]] && args+=(--workdir "$workdir")
|
||||
|
||||
for e in "${envs[@]}"; do
|
||||
args+=(-e "$e")
|
||||
done
|
||||
|
||||
# volumes/binds -> données conservées
|
||||
for b in "${binds[@]}"; do
|
||||
args+=(-v "$b")
|
||||
done
|
||||
|
||||
# ports
|
||||
for pr in "${port_rules[@]}"; do
|
||||
cport="${pr%%=*}" # ex: 8080/tcp
|
||||
mapping="${pr#*=}" # ex: 0.0.0.0:8080 ou :8080
|
||||
cportnum="${cport%/*}" # 8080
|
||||
|
||||
if [[ "$mapping" == :* ]]; then
|
||||
hostport="${mapping#:}"
|
||||
args+=(-p "${hostport}:${cportnum}")
|
||||
else
|
||||
hip="${mapping%%:*}"
|
||||
hp="${mapping#*:}"
|
||||
args+=(-p "${hip}:${hp}:${cportnum}")
|
||||
fi
|
||||
done
|
||||
|
||||
# network mode (si spécial)
|
||||
if [[ "$network_mode" != "default" && "$network_mode" != "bridge" ]]; then
|
||||
args+=(--network "$network_mode")
|
||||
fi
|
||||
|
||||
# image
|
||||
args+=("$image")
|
||||
|
||||
run_cmd "Création du nouveau conteneur : $name" "${args[@]}"
|
||||
|
||||
# connecter aux réseaux additionnels si needed
|
||||
if [[ "$network_mode" == "default" || "$network_mode" == "bridge" ]]; then
|
||||
for n in "${nets[@]}"; do
|
||||
[[ "$n" == "bridge" || "$n" == "host" || "$n" == "none" ]] && continue
|
||||
run_cmd "Connexion réseau ($n) : $name" docker network connect "$n" "$new_name"
|
||||
done
|
||||
fi
|
||||
|
||||
# switch
|
||||
run_cmd "Arrêt du conteneur : $name" docker stop "$name"
|
||||
run_cmd "Renommage de l'ancien conteneur" docker rename "$name" "${name}__old"
|
||||
run_cmd "Renommage du nouveau conteneur" docker rename "$new_name" "$name"
|
||||
run_cmd "Démarrage du conteneur : $name" docker start "$name"
|
||||
run_cmd "Suppression de l'ancien conteneur" docker rm "${name}__old"
|
||||
|
||||
log "✅ Conteneur recréé et redémarré : $name (données conservées via volumes/binds)"
|
||||
}
|
||||
|
||||
update_docker() {
|
||||
log "🐳 Mise à jour des conteneurs Docker"
|
||||
|
||||
run_cmd "Vérification Docker" docker ps
|
||||
|
||||
# ---- 1) Compose via labels
|
||||
log "🔍 Recherche de projets Docker Compose"
|
||||
mapfile -t dirs < <(compose_dirs_from_labels)
|
||||
|
||||
valid_compose=()
|
||||
for d in "${dirs[@]}"; do
|
||||
if is_compose_dir_valid "$d"; then
|
||||
valid_compose+=("$d")
|
||||
else
|
||||
log "⚠️ Projet Compose ignoré (chemin invalide ou sans compose.yml) : $d"
|
||||
fi
|
||||
done
|
||||
|
||||
if (( ${#valid_compose[@]} > 0 )); then
|
||||
log "📁 Projet(s) Docker Compose détecté(s) : ${#valid_compose[@]}"
|
||||
for d in "${valid_compose[@]}"; do
|
||||
update_compose_dir "$d"
|
||||
done
|
||||
else
|
||||
log "⚠️ Aucun projet Compose détecté via labels"
|
||||
fi
|
||||
|
||||
# ---- 2) Conteneurs non-Compose = docker run
|
||||
mapfile -t run_containers < <(
|
||||
docker ps -q | while read -r cid; do
|
||||
p="$(docker inspect -f '{{ index .Config.Labels "com.docker.compose.project" }}' "$cid" 2>/dev/null || true)"
|
||||
if [[ -z "$p" || "$p" == "<no value>" ]]; then
|
||||
echo "$cid"
|
||||
fi
|
||||
done
|
||||
)
|
||||
|
||||
if (( ${#run_containers[@]} == 0 )); then
|
||||
log "✅ Aucun conteneur 'docker run' détecté"
|
||||
log "✅ Mise à jour Docker terminée"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log "🧩 Conteneur(s) 'docker run' détecté(s) : ${#run_containers[@]}"
|
||||
|
||||
for cid in "${run_containers[@]}"; do
|
||||
name="$(docker inspect -f '{{.Name}}' "$cid" | sed 's#^/##')"
|
||||
image="$(docker inspect -f '{{.Config.Image}}' "$cid")"
|
||||
|
||||
log "📦 Conteneur : $name"
|
||||
log " 🧱 Image : $image"
|
||||
|
||||
old_id="$(docker_image_id "$image")"
|
||||
run_cmd "Téléchargement des mises à jour : $name" docker pull "$image"
|
||||
new_id="$(docker_image_id "$image")"
|
||||
|
||||
if [[ -n "$old_id" && -n "$new_id" && "$old_id" == "$new_id" ]]; then
|
||||
log "✅ Aucun changement d'image : $name"
|
||||
continue
|
||||
fi
|
||||
|
||||
log "⬆️ Image mise à jour pour : $name"
|
||||
if [[ -n "$old_id" && -n "$new_id" ]]; then
|
||||
log " 🔁 ${old_id:0:20} ➜ ${new_id:0:20}"
|
||||
fi
|
||||
|
||||
recreate_docker_run_container "$name" "$image"
|
||||
done
|
||||
|
||||
log "✅ Mise à jour Docker terminée"
|
||||
}
|
||||
|
||||
# --- Parsing MODE (apt/apk + docker) ---
|
||||
# ------------------------------
|
||||
# 🔎 Mode : base + docker éventuel
|
||||
# Exemples : apt, apk, apt+docker, apk+docker, docker, unknown
|
||||
# ------------------------------
|
||||
BASE_MODE="${MODE%%+docker}"
|
||||
HAS_DOCKER="0"
|
||||
[[ "$MODE" == *"+docker" ]] && HAS_DOCKER="1"
|
||||
|
||||
# ─────────────── Sortie lisible ───────────────
|
||||
# Si MODE == "docker" seul (au cas où), on active docker
|
||||
if [[ "$MODE" == "docker" ]]; then
|
||||
HAS_DOCKER="1"
|
||||
fi
|
||||
|
||||
# ------------------------------
|
||||
# 🖥️ Sortie lisible
|
||||
# ------------------------------
|
||||
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
log "📦 Mise à jour du système"
|
||||
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
@@ -189,18 +351,27 @@ log "━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
case "$BASE_MODE" in
|
||||
apt) update_apt ;;
|
||||
apk) update_apk ;;
|
||||
*) log "⚠️ Impossible de détecter le gestionnaire de paquets (apt/apk)" ;;
|
||||
*)
|
||||
# Si apt/apk non détecté mais apt-get existe, on tente apt
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
update_apt
|
||||
elif command -v apk >/dev/null 2>&1; then
|
||||
update_apk
|
||||
else
|
||||
log "⚠️ Impossible de détecter le gestionnaire de paquets (apt/apk)"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ "$HAS_DOCKER" == "1" ]]; then
|
||||
log ""
|
||||
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
log "🐳 Mise à jour du ou des conteneurs"
|
||||
log "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
||||
update_docker_compose
|
||||
|
||||
if command -v docker >/dev/null 2>&1; then
|
||||
update_docker
|
||||
else
|
||||
log ""
|
||||
log "🐳 Aucun conteneur Docker détecté"
|
||||
log "🐳 Docker non présent"
|
||||
fi
|
||||
|
||||
log ""
|
||||
|
||||
Reference in New Issue
Block a user