mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-11 12:09:03 +00:00
188 lines
5.2 KiB
Bash
Executable File
188 lines
5.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -Eeuo pipefail
|
|
|
|
container_name="${CCS_CLIPROXY_CONTAINER:-ccs-cliproxy}"
|
|
lock_file="${CCS_CLIPROXY_LOCK_FILE:-/run/lock/ccs-cliproxy-maintenance.lock}"
|
|
log_file="${CCS_CLIPROXY_UPDATE_LOG:-/var/log/ccs-cliproxy-update.log}"
|
|
|
|
log() {
|
|
printf '[%s] %s\n' "$(date -Is)" "$*" | tee -a "$log_file"
|
|
}
|
|
|
|
exec 9>"$lock_file"
|
|
if ! flock -n 9; then
|
|
log 'Another CLIProxy maintenance operation is active; skipping update check'
|
|
exit 0
|
|
fi
|
|
|
|
if ! docker inspect "$container_name" >/dev/null 2>&1; then
|
|
log "Container $container_name is missing; reconciliation must restore it before updating"
|
|
exit 1
|
|
fi
|
|
|
|
if [ "$(docker inspect --format '{{.State.Running}}' "$container_name")" != 'true' ]; then
|
|
log "Container $container_name is not running; reconciliation must restore it before updating"
|
|
exit 1
|
|
fi
|
|
|
|
status_output="$(docker exec "$container_name" ccs cliproxy --version --backend plus --verbose 2>&1)" || {
|
|
log "Unable to inspect the installed CLIProxy version: $status_output"
|
|
exit 1
|
|
}
|
|
|
|
if grep -qi 'Could not fetch' <<<"$status_output"; then
|
|
log "Unable to check the latest CLIProxy version: $(grep -im1 'Could not fetch' <<<"$status_output" | xargs)"
|
|
exit 1
|
|
fi
|
|
|
|
if ! grep -qi 'update available' <<<"$status_output"; then
|
|
exit 0
|
|
fi
|
|
|
|
log 'CLIProxy Plus update available; staging verified replacement while the current proxy stays online'
|
|
|
|
if ! docker exec -i "$container_name" sh -s <<'CONTAINER_UPDATE'
|
|
set -eu
|
|
|
|
live_dir='/root/.ccs/cliproxy/bin/plus'
|
|
live_binary="$live_dir/cli-proxy-api-plus"
|
|
live_version="$live_dir/.version"
|
|
stage_root="$(mktemp -d /root/.ccs/cliproxy/.host-update.XXXXXX)"
|
|
stage_ccs_dir="$stage_root/ccs"
|
|
stage_dir="$stage_ccs_dir/cliproxy/bin/plus"
|
|
stage_binary="$stage_dir/cli-proxy-api-plus"
|
|
stage_version="$stage_dir/.version"
|
|
backup_binary="$stage_root/previous-binary"
|
|
backup_version="$stage_root/previous-version"
|
|
install_lock_target='/root/.ccs/cliproxy/bin/.install-lifecycle-plus'
|
|
install_lock_dir="$install_lock_target.lock"
|
|
install_lock_stale_seconds=600
|
|
install_lock_owned=0
|
|
maintenance_started=0
|
|
|
|
supervisorctl_cmd() {
|
|
supervisorctl -c /etc/supervisord.conf "$@"
|
|
}
|
|
|
|
cleanup() {
|
|
rm -rf -- "$stage_root"
|
|
}
|
|
|
|
remove_stale_install_lock() {
|
|
lock_mtime="$(
|
|
stat -c %Y "$install_lock_dir" 2>/dev/null || stat -f %m "$install_lock_dir" 2>/dev/null
|
|
)" || return 0
|
|
current_time="$(date +%s)"
|
|
lock_age=$((current_time - lock_mtime))
|
|
if [ "$lock_age" -gt "$install_lock_stale_seconds" ]; then
|
|
rmdir "$install_lock_dir" 2>/dev/null || true
|
|
fi
|
|
}
|
|
|
|
acquire_install_lock() {
|
|
mkdir -p "$install_lock_target"
|
|
attempts=0
|
|
while ! mkdir "$install_lock_dir" 2>/dev/null; do
|
|
attempts=$((attempts + 1))
|
|
if [ "$attempts" -ge 240 ]; then
|
|
printf '[X] Timed out waiting for CLIProxy install lifecycle lock\n' >&2
|
|
return 1
|
|
fi
|
|
remove_stale_install_lock
|
|
sleep 0.25
|
|
done
|
|
install_lock_owned=1
|
|
touch "$install_lock_dir"
|
|
}
|
|
|
|
release_install_lock() {
|
|
if [ "$install_lock_owned" -eq 1 ]; then
|
|
rmdir "$install_lock_dir" 2>/dev/null || true
|
|
install_lock_owned=0
|
|
fi
|
|
}
|
|
|
|
wait_for_proxy() {
|
|
attempts=0
|
|
while [ "$attempts" -lt 30 ]; do
|
|
if curl -fsS --max-time 2 http://127.0.0.1:8317/ >/dev/null; then
|
|
return 0
|
|
fi
|
|
attempts=$((attempts + 1))
|
|
sleep 2
|
|
done
|
|
return 1
|
|
}
|
|
|
|
rollback() {
|
|
rollback_ok=1
|
|
supervisorctl_cmd stop cliproxy >/dev/null 2>&1 || true
|
|
if [ -f "$backup_binary" ]; then
|
|
install -m 0755 "$backup_binary" "$live_binary.rollback" && \
|
|
mv -f "$live_binary.rollback" "$live_binary" || rollback_ok=0
|
|
else
|
|
rollback_ok=0
|
|
fi
|
|
if [ -f "$backup_version" ]; then
|
|
install -m 0644 "$backup_version" "$live_version.rollback" && \
|
|
mv -f "$live_version.rollback" "$live_version" || rollback_ok=0
|
|
else
|
|
rollback_ok=0
|
|
fi
|
|
supervisorctl_cmd start cliproxy >/dev/null || rollback_ok=0
|
|
wait_for_proxy || rollback_ok=0
|
|
[ "$rollback_ok" -eq 1 ]
|
|
}
|
|
|
|
on_exit() {
|
|
rc=$?
|
|
trap - EXIT INT TERM HUP
|
|
rollback_failed=0
|
|
if [ "$rc" -ne 0 ] && [ "$maintenance_started" -eq 1 ]; then
|
|
if ! rollback; then
|
|
rollback_failed=1
|
|
printf '[X] CLIProxy rollback failed; recovery files preserved at %s\n' "$stage_root" >&2
|
|
fi
|
|
fi
|
|
release_install_lock
|
|
if [ "$rollback_failed" -eq 0 ]; then
|
|
cleanup
|
|
else
|
|
rc=70
|
|
fi
|
|
exit "$rc"
|
|
}
|
|
|
|
trap on_exit EXIT
|
|
trap 'exit 130' INT
|
|
trap 'exit 143' TERM
|
|
trap 'exit 129' HUP
|
|
|
|
mkdir -p "$stage_ccs_dir"
|
|
CCS_DIR="$stage_ccs_dir" ccs cliproxy --latest --backend plus
|
|
test -x "$stage_binary"
|
|
test -s "$stage_version"
|
|
"$stage_binary" --version >/dev/null
|
|
|
|
acquire_install_lock
|
|
cp -p "$live_binary" "$backup_binary"
|
|
cp -p "$live_version" "$backup_version"
|
|
|
|
maintenance_started=1
|
|
supervisorctl_cmd stop cliproxy >/dev/null
|
|
mv -f "$stage_binary" "$live_binary"
|
|
mv -f "$stage_version" "$live_version"
|
|
chmod 0755 "$live_binary"
|
|
supervisorctl_cmd start cliproxy >/dev/null
|
|
wait_for_proxy
|
|
maintenance_started=0
|
|
CONTAINER_UPDATE
|
|
then
|
|
log 'CLIProxy Plus update failed; rollback was attempted and reconciliation will verify service health'
|
|
exit 1
|
|
fi
|
|
|
|
installed_output="$(docker exec "$container_name" ccs cliproxy --version --backend plus 2>&1)"
|
|
log "CLIProxy Plus update completed and passed health verification: $(grep -m1 'Version:' <<<"$installed_output" | xargs)"
|