mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 21:35:42 +00:00
Keep eagle logs following through deployments (#8689)
This commit is contained in:
+102
-25
@@ -4,41 +4,118 @@
|
||||
# Reads the active instance from /opt/eagle0/.active-instance (set by deploy-blue-green.sh).
|
||||
#
|
||||
# Usage:
|
||||
# eagle-logs # Tail logs (follow mode)
|
||||
# eagle-logs -n 100 # Show last 100 lines and follow
|
||||
# eagle-logs --no-follow -n 50 # Show last 50 lines without following
|
||||
# eagle-logs # Show existing logs and keep following
|
||||
# eagle-logs -n 100 # Show the last 100 lines and keep following
|
||||
# eagle-logs --no-follow -n 50 # Show the last 50 lines and exit
|
||||
#
|
||||
# To create an alias, add to ~/.bashrc:
|
||||
# alias eagle-logs='/opt/eagle0/scripts/eagle-logs.sh'
|
||||
#
|
||||
|
||||
set -euo pipefail
|
||||
set -uo pipefail
|
||||
|
||||
APP_DIR="${APP_DIR:-/opt/eagle0}"
|
||||
ACTIVE_FILE="${APP_DIR}/.active-instance"
|
||||
RETRY_INTERVAL_SECONDS="${EAGLE_LOGS_RETRY_INTERVAL_SECONDS:-2}"
|
||||
|
||||
# Read active instance from file, with fallback
|
||||
if [ -f "$ACTIVE_FILE" ]; then
|
||||
ACTIVE=$(cat "$ACTIVE_FILE")
|
||||
else
|
||||
# Fallback: check which container is actually running
|
||||
if docker inspect --format='{{.State.Running}}' eagle-blue 2>/dev/null | grep -q true; then
|
||||
ACTIVE="eagle-blue"
|
||||
elif docker inspect --format='{{.State.Running}}' eagle-green 2>/dev/null | grep -q true; then
|
||||
ACTIVE="eagle-green"
|
||||
else
|
||||
ACTIVE=""
|
||||
trap 'exit 130' INT
|
||||
trap 'exit 143' TERM
|
||||
|
||||
FOLLOW=true
|
||||
DOCKER_LOG_ARGS=()
|
||||
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--no-follow)
|
||||
FOLLOW=false
|
||||
;;
|
||||
-f | --follow)
|
||||
FOLLOW=true
|
||||
;;
|
||||
*)
|
||||
DOCKER_LOG_ARGS+=("$arg")
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
container_is_running() {
|
||||
[ "$(docker inspect --format='{{.State.Running}}' "$1" 2>/dev/null)" = "true" ]
|
||||
}
|
||||
|
||||
resolve_active_instance() {
|
||||
local recorded_active=""
|
||||
|
||||
if [ -f "$ACTIVE_FILE" ]; then
|
||||
IFS= read -r recorded_active < "$ACTIVE_FILE" || true
|
||||
fi
|
||||
|
||||
if [ -n "$recorded_active" ] && container_is_running "$recorded_active"; then
|
||||
printf '%s\n' "$recorded_active"
|
||||
elif container_is_running eagle-blue; then
|
||||
printf '%s\n' "eagle-blue"
|
||||
elif container_is_running eagle-green; then
|
||||
printf '%s\n' "eagle-green"
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
print_disconnect_marker() {
|
||||
local stopped_instance=${1:-unknown}
|
||||
|
||||
printf '\n' >&2
|
||||
printf '%s\n' '================================================================================' >&2
|
||||
printf '%s\n' '==================== EAGLE LOG STREAM DISCONNECTED ============================' >&2
|
||||
printf ' Server unavailable at %s (last instance: %s)\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$stopped_instance" >&2
|
||||
printf '%s\n' ' Waiting for the deployment or server restart; log following will resume.' >&2
|
||||
printf '%s\n' '================================================================================' >&2
|
||||
}
|
||||
|
||||
print_resume_marker() {
|
||||
local active_instance=$1
|
||||
|
||||
printf '\n' >&2
|
||||
printf '%s\n' '================================================================================' >&2
|
||||
printf '%s\n' '======================= EAGLE LOG STREAM RESUMED ==============================' >&2
|
||||
printf ' Attached to %s at %s\n' "$active_instance" "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" >&2
|
||||
printf '%s\n' '================================================================================' >&2
|
||||
}
|
||||
|
||||
if [ "$FOLLOW" = false ]; then
|
||||
if ! ACTIVE=$(resolve_active_instance); then
|
||||
echo "Error: No active Eagle instance found" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec docker logs "${DOCKER_LOG_ARGS[@]}" "$ACTIVE"
|
||||
fi
|
||||
|
||||
if [ -z "$ACTIVE" ]; then
|
||||
echo "Error: No active Eagle instance found" >&2
|
||||
exit 1
|
||||
fi
|
||||
ACTIVE=""
|
||||
DISCONNECTED=false
|
||||
RECONNECT_SINCE=""
|
||||
|
||||
# Default to follow mode if no args provided
|
||||
if [ $# -eq 0 ]; then
|
||||
exec docker logs -f "$ACTIVE"
|
||||
else
|
||||
exec docker logs "$@" "$ACTIVE"
|
||||
fi
|
||||
while true; do
|
||||
if NEXT_ACTIVE=$(resolve_active_instance); then
|
||||
if [ "$DISCONNECTED" = true ]; then
|
||||
print_resume_marker "$NEXT_ACTIVE"
|
||||
fi
|
||||
|
||||
ACTIVE="$NEXT_ACTIVE"
|
||||
DISCONNECTED=false
|
||||
|
||||
if [ -n "$RECONNECT_SINCE" ]; then
|
||||
docker logs --follow "${DOCKER_LOG_ARGS[@]}" --since "$RECONNECT_SINCE" "$ACTIVE" || true
|
||||
else
|
||||
docker logs --follow "${DOCKER_LOG_ARGS[@]}" "$ACTIVE" || true
|
||||
fi
|
||||
|
||||
RECONNECT_SINCE=$(date +%s)
|
||||
fi
|
||||
|
||||
if [ "$DISCONNECTED" = false ]; then
|
||||
print_disconnect_marker "$ACTIVE"
|
||||
DISCONNECTED=true
|
||||
fi
|
||||
|
||||
sleep "$RETRY_INTERVAL_SECONDS"
|
||||
done
|
||||
|
||||
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
TEST_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$TEST_DIR"' EXIT
|
||||
|
||||
FAKE_BIN="${TEST_DIR}/bin"
|
||||
APP_DIR="${TEST_DIR}/app"
|
||||
STATE_FILE="${TEST_DIR}/state"
|
||||
CALLS_FILE="${TEST_DIR}/calls"
|
||||
OUTPUT_FILE="${TEST_DIR}/output"
|
||||
mkdir -p "$FAKE_BIN" "$APP_DIR"
|
||||
|
||||
cat > "${FAKE_BIN}/docker" <<'EOF'
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
case "$1" in
|
||||
inspect)
|
||||
container=${@: -1}
|
||||
state=$(cat "$EAGLE_LOGS_TEST_STATE_FILE")
|
||||
if { [ "$state" = "blue" ] && [ "$container" = "eagle-blue" ]; } ||
|
||||
{ [ "$state" = "green" ] && [ "$container" = "eagle-green" ]; }; then
|
||||
echo true
|
||||
else
|
||||
echo false
|
||||
fi
|
||||
;;
|
||||
logs)
|
||||
container=${@: -1}
|
||||
printf '%s\n' "$*" >> "$EAGLE_LOGS_TEST_CALLS_FILE"
|
||||
if [ "$container" = "eagle-blue" ]; then
|
||||
echo "last blue log"
|
||||
echo "eagle-green" > "$EAGLE_LOGS_TEST_ACTIVE_FILE"
|
||||
echo "green" > "$EAGLE_LOGS_TEST_STATE_FILE"
|
||||
else
|
||||
echo "first green log"
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Unexpected docker command: $*" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
chmod +x "${FAKE_BIN}/docker"
|
||||
|
||||
export EAGLE_LOGS_TEST_STATE_FILE="$STATE_FILE"
|
||||
export EAGLE_LOGS_TEST_CALLS_FILE="$CALLS_FILE"
|
||||
export EAGLE_LOGS_TEST_ACTIVE_FILE="${APP_DIR}/.active-instance"
|
||||
|
||||
echo "blue" > "$STATE_FILE"
|
||||
echo "eagle-blue" > "${APP_DIR}/.active-instance"
|
||||
|
||||
PATH="${FAKE_BIN}:$PATH" APP_DIR="$APP_DIR" "${SCRIPT_DIR}/eagle-logs.sh" --no-follow -n 25
|
||||
grep -q '^logs -n 25 eagle-blue$' "$CALLS_FILE"
|
||||
|
||||
: > "$CALLS_FILE"
|
||||
echo "blue" > "$STATE_FILE"
|
||||
echo "eagle-blue" > "${APP_DIR}/.active-instance"
|
||||
PATH="${FAKE_BIN}:$PATH" APP_DIR="$APP_DIR" EAGLE_LOGS_RETRY_INTERVAL_SECONDS=0.01 \
|
||||
"${SCRIPT_DIR}/eagle-logs.sh" -n 10 > "$OUTPUT_FILE" 2>&1 &
|
||||
FOLLOW_PID=$!
|
||||
|
||||
for _ in $(seq 1 100); do
|
||||
if grep -q 'first green log' "$OUTPUT_FILE"; then
|
||||
break
|
||||
fi
|
||||
sleep 0.01
|
||||
done
|
||||
|
||||
kill "$FOLLOW_PID" 2>/dev/null || true
|
||||
wait "$FOLLOW_PID" 2>/dev/null || true
|
||||
|
||||
grep -q 'EAGLE LOG STREAM DISCONNECTED' "$OUTPUT_FILE"
|
||||
grep -q 'EAGLE LOG STREAM RESUMED' "$OUTPUT_FILE"
|
||||
grep -q 'last blue log' "$OUTPUT_FILE"
|
||||
grep -q 'first green log' "$OUTPUT_FILE"
|
||||
grep -q '^logs --follow -n 10 eagle-blue$' "$CALLS_FILE"
|
||||
grep -Eq '^logs --follow -n 10 --since [0-9]+ eagle-green$' "$CALLS_FILE"
|
||||
|
||||
echo "eagle-logs tests passed"
|
||||
Reference in New Issue
Block a user