forked from wangbo/easyai
82 lines
3.2 KiB
Bash
Executable File
82 lines
3.2 KiB
Bash
Executable File
#!/bin/bash
|
|
set -euo pipefail
|
|
PROJECT_ROOT="${1:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
|
|
cd "$PROJECT_ROOT"
|
|
. ./scripts/init-security-env.sh
|
|
[ "$(read_env_value .env INFRASTRUCTURE_AUTO_UPDATE_ENABLED)" != false ] || exit 0
|
|
state="$PROJECT_ROOT/data/infrastructure-update"
|
|
mkdir -p "$state"
|
|
lock="$state/lock"
|
|
if ! mkdir "$lock" 2>/dev/null; then
|
|
pid="$(cat "$lock/pid" 2>/dev/null || true)"
|
|
if [[ "$pid" =~ ^[0-9]+$ ]] && ! kill -0 "$pid" 2>/dev/null; then
|
|
rm -f "$lock/pid"
|
|
rmdir "$lock" 2>/dev/null || exit 0
|
|
mkdir "$lock" 2>/dev/null || exit 0
|
|
else
|
|
exit 0
|
|
fi
|
|
fi
|
|
printf '%s\n' "$$" > "$lock/pid"
|
|
trap 'rm -f "$lock/pid"; rmdir "$lock" 2>/dev/null || true' EXIT
|
|
exec > "$state/last-run.log" 2>&1
|
|
printf '%s 检查 Watchtower 和 updater 更新\n' "$(date '+%Y-%m-%d %H:%M:%S')"
|
|
registry_config="$(read_env_value .env DOCKER_CONFIG_FILE)"
|
|
docker_options=()
|
|
if [ -n "$registry_config" ] && [ -f "$registry_config" ]; then
|
|
docker_options=(--config "$(dirname "$registry_config")")
|
|
fi
|
|
if docker "${docker_options[@]}" compose version >/dev/null 2>&1; then
|
|
compose() { docker "${docker_options[@]}" compose "$@"; }
|
|
else
|
|
compose() {
|
|
if [ -n "$registry_config" ] && [ -f "$registry_config" ]; then
|
|
DOCKER_CONFIG="$(dirname "$registry_config")" docker-compose "$@"
|
|
else
|
|
docker-compose "$@"
|
|
fi
|
|
}
|
|
fi
|
|
available="$(compose config --services)"
|
|
printf '%s\n' "$available" | grep -qx watchtower || { echo '未配置 Watchtower'; exit 1; }
|
|
maintain_service() {
|
|
local service="$1" container_id status attempt
|
|
echo "检查 $service"
|
|
# A failed service must not block the other update channel.
|
|
compose pull "$service" || return 1
|
|
compose up -d --no-deps "$service" || return 1
|
|
for ((attempt=0; attempt<12; attempt++)); do
|
|
container_id="$(compose ps -q "$service")" || return 1
|
|
if [ -n "$container_id" ]; then
|
|
status="$(docker inspect -f '{{.State.Running}} {{if .State.Health}}{{.State.Health.Status}}{{else}}none{{end}}' "$container_id")" || return 1
|
|
if [ "$status" = 'true healthy' ]; then
|
|
echo "$service 已恢复可用"
|
|
return 0
|
|
fi
|
|
if [ "$status" = 'true none' ]; then
|
|
if [ "$service" = watchtower ]; then
|
|
if docker exec "$container_id" /watchtower --health-check; then return 0; fi
|
|
else
|
|
if docker exec "$container_id" node -e "require('http').get('http://127.0.0.1:3015/health',r=>{r.resume();process.exit(r.statusCode===200?0:1)}).on('error',()=>process.exit(1))"; then return 0; fi
|
|
fi
|
|
fi
|
|
fi
|
|
sleep 5
|
|
done
|
|
echo "$service 暂未恢复可用" >&2
|
|
return 1
|
|
}
|
|
failed=0
|
|
for service in watchtower easyai-updater; do
|
|
# config --services respects active profiles: legacy deployments stay legacy.
|
|
if ! printf '%s\n' "$available" | grep -qx "$service"; then
|
|
echo "$service 未启用,跳过维护"
|
|
continue
|
|
fi
|
|
if ! maintain_service "$service"; then
|
|
echo "$service 本次维护未成功,下次定时任务将继续检查" >&2
|
|
failed=1
|
|
fi
|
|
done
|
|
exit "$failed"
|