1
0
mirror of git://f0xx.org/ac/ac-scripts synced 2026-07-29 07:59:00 +03:00
Files
ac-scripts/full_deployment.sh
2026-07-04 11:31:34 +02:00

239 lines
8.8 KiB
Bash
Executable File

#!/bin/sh
# full_deployment.sh — Full backend cluster deployment for Android Cast
#
# Usage:
# ./ac-scripts/full_deployment.sh [OPTIONS]
#
# Options:
# --phase baseline|db|app|sfu|all (default: all)
# --host cast01|cast02|cast03|all (default: all)
# --dry-run Print commands without executing
# --no-color Disable color output
# -h|--help
#
# This script:
# 1. Pulls latest ac/* repos on each node (git pull + submodule update)
# 2. Runs populate_lab_setup.sh phases on each node via SSH
# 3. Runs Janus SFU deployment on cast02 (phase sfu)
# 4. Reloads nginx on cast01
# 5. Runs load-schemas.sh to apply any new SQL migrations
#
# Prerequisites:
# - SSH passwordless access to cast01, cast02, cast03
# - /mnt/repos/ac checkout on each node
#
set -eu
# ── Helpers ──────────────────────────────────────────────────────────
RED='\033[0;31m'; GRN='\033[0;32m'; YEL='\033[1;33m'; BLU='\033[0;34m'; RST='\033[0m'
if [ "${NO_COLOR:-}" = "1" ] || [ "${1:-}" = "--no-color" ]; then
RED=''; GRN=''; YEL=''; BLU=''; RST=''
fi
log() { printf "%b[%s]%b %s\n" "$BLU" "$(date '+%H:%M:%S')" "$RST" "$*"; }
ok() { printf "%b[OK]%b %s\n" "$GRN" "$RST" "$*"; }
warn() { printf "%b[WARN]%b %s\n" "$YEL" "$RST" "$*"; }
fail() { printf "%b[FAIL]%b %s\n" "$RED" "$RST" "$*" >&2; exit 1; }
DRY=0
PHASE="all"
TARGET_HOST="all"
while [ $# -gt 0 ]; do
case "$1" in
--dry-run) DRY=1 ;;
--no-color) : ;;
--phase) shift; PHASE="${1:?phase value required}" ;;
--host) shift; TARGET_HOST="${1:?host value required}" ;;
-h|--help)
sed -n '2,30p' "$0" | sed 's/^# //'
exit 0 ;;
*) warn "Unknown option: $1" ;;
esac
shift
done
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
DEPLOY_DIR="$(cd "${SCRIPT_DIR}/../ac-deploy/sim/cluster0" && pwd)"
NODES="cast01 cast02 cast03"
rsh() {
local host="$1"; shift
if [ "$DRY" = "1" ]; then
log "[DRY] ssh $host $*"
return 0
fi
ssh "$host" "$@"
}
on_host() {
local host="$1"
[ "$TARGET_HOST" = "all" ] || [ "$TARGET_HOST" = "$host" ]
}
# ── Phase helpers ────────────────────────────────────────────────────
phase_sync() {
local host="$1"
log "$host: pulling latest ac/* repos..."
rsh "$host" "cd /mnt/repos/ac && git fetch origin && git checkout next && git pull && git submodule update --init --recursive 2>&1 | tail -5"
# Sync critical cluster files from git repo to NFS share (scripts + nginx templates only)
rsh "$host" "
SRC=/mnt/repos/ac/ac-deploy/sim/cluster0
DST=/shared/cluster
[ -d \"\$SRC\" ] || exit 0
for f in nginx/apps-port80.conf nginx/url-shortener-port80.conf \
scripts/load-schemas.sh scripts/configure-nginx.sh \
scripts/deploy-ac-workspace.sh; do
[ -f \"\$SRC/\$f\" ] && sudo cp \"\$SRC/\$f\" \"\$DST/\$f\" 2>/dev/null || true
done
sudo cp -r \"\$SRC/lab-seeds/backend/sql/migrations/\" \"\$DST/lab-seeds/backend/sql/migrations/\" 2>/dev/null || true
"
ok "$host: sync done"
}
phase_baseline() {
local host="$1"
log "$host: running baseline..."
rsh "$host" "cd /mnt/repos/ac/ac-deploy/sim/cluster0 && sudo sh populate_lab_setup.sh --phase baseline 2>&1 | tail -10"
ok "$host: baseline done"
}
phase_db() {
local host="$1"
log "$host: running DB setup..."
rsh "$host" "cd /mnt/repos/ac/ac-deploy/sim/cluster0 && sudo sh populate_lab_setup.sh --phase db 2>&1 | tail -10"
ok "$host: DB done"
}
phase_workspace_bump() {
# Bump ac-workspace submodule SHAs from the monorepo clone so the deployment
# workspace (/var/www/ac/workspace) gets the latest code on next pull.
local host="$1"
rsh "$host" "
WS=/var/www/ac/workspace
MONOREPO=/mnt/repos/ac
[ -d \"\$WS/.git\" ] || exit 0
changed=0
for sub in \$(sudo -u nginx git -C \"\$WS\" submodule status 2>/dev/null | awk '{print \$2}'); do
local_sha=\$(git -C \"\$MONOREPO/\$sub\" rev-parse HEAD 2>/dev/null) || continue
ws_sha=\$(sudo -u nginx git -C \"\$WS/\$sub\" rev-parse HEAD 2>/dev/null) || continue
[ \"\$local_sha\" = \"\$ws_sha\" ] && continue
sudo -u nginx git -C \"\$WS/\$sub\" fetch origin 2>/dev/null || true
sudo -u nginx git -C \"\$WS/\$sub\" checkout \"\$local_sha\" 2>/dev/null || true
sudo -u nginx git -C \"\$WS\" add \"\$sub\" 2>/dev/null || true
changed=1
done
[ \"\$changed\" = '1' ] && sudo -u nginx git -C \"\$WS\" commit -m 'auto-bump submodules from monorepo' 2>/dev/null || true
sudo -u nginx git -C \"\$WS\" push origin next 2>/dev/null || true
" 2>/dev/null || true
}
phase_app() {
local host="$1"
log "$host: running app deploy..."
phase_workspace_bump "$host"
rsh "$host" "cd /mnt/repos/ac/ac-deploy/sim/cluster0 && sudo sh populate_lab_setup.sh --phase app 2>&1 | tail -10"
ok "$host: app done"
}
phase_sfu() {
log "cast02: deploying Janus SFU..."
# Install Janus if not present
rsh cast02 "command -v janus >/dev/null 2>&1 || sudo apk add --no-cache janus-gateway janus-gateway-openrc"
# Ensure config files exist (idempotent cp)
rsh cast02 "
[ -f /etc/janus/janus.jcfg ] || sudo cp /etc/janus/janus.jcfg.sample /etc/janus/janus.jcfg
[ -f /etc/janus/janus.transport.http.jcfg ] || sudo cp /etc/janus/janus.transport.http.jcfg.sample /etc/janus/janus.transport.http.jcfg
[ -f /etc/janus/janus.transport.websockets.jcfg ] || sudo cp /etc/janus/janus.transport.websockets.jcfg.sample /etc/janus/janus.transport.websockets.jcfg
[ -f /etc/janus/janus.plugin.videoroom.jcfg ] || sudo cp /etc/janus/janus.plugin.videoroom.jcfg.sample /etc/janus/janus.plugin.videoroom.jcfg
"
# Enable and (re)start service
rsh cast02 "sudo rc-update add janus-gateway default 2>/dev/null; sudo rc-service janus-gateway restart 2>&1 | tail -3"
# Verify Janus is responding
rsh cast02 "curl -sf http://localhost:8088/janus/info | python3 -c 'import sys,json; d=json.load(sys.stdin); print(\"Janus\",d[\"version_string\"],\"OK\")'" \
&& ok "cast02: Janus SFU is running" \
|| warn "cast02: Janus health check failed (may still be starting)"
# Deploy SFU signaling service to cast01 workspace
log "cast01: deploying SFU signaling service..."
rsh cast01 "
sudo mkdir -p /var/www/ac/workspace/ac-ms-sfu-signaling
if [ -d /mnt/repos/ac/ac-ms-sfu-signaling ]; then
sudo rsync -a --delete /mnt/repos/ac/ac-ms-sfu-signaling/ /var/www/ac/workspace/ac-ms-sfu-signaling/
sudo chown -R nginx:nginx /var/www/ac/workspace/ac-ms-sfu-signaling/ 2>/dev/null || true
fi
"
# Apply SFU schema migrations via load-schemas
log "cast01: applying SFU schema migrations..."
rsh cast01 "
cd /mnt/repos/ac/ac-deploy/sim/cluster0
sudo sh scripts/load-schemas.sh 2>&1 | tail -5
" || warn "Schema migration failed (may already be applied)"
# Reload nginx with Janus proxy config
log "cast01: reloading nginx..."
rsh cast01 "sudo /usr/sbin/nginx -t && sudo rc-service nginx reload 2>&1 | tail -2"
ok "cast01: nginx reloaded with Janus proxy"
}
phase_nginx_reload() {
log "cast01: reloading nginx..."
rsh cast01 "sudo /usr/sbin/nginx -t && sudo rc-service nginx reload 2>&1 | tail -2"
ok "cast01: nginx reloaded"
}
phase_verify() {
log "Running cluster verification..."
rsh cast01 "cd /mnt/repos/ac/ac-deploy/sim/cluster0 && sh scripts/verify-cluster.sh 2>&1 | tail -20" || warn "verify-cluster returned non-zero"
}
# ── Main ─────────────────────────────────────────────────────────────
log "=== Android Cast Full Deployment ==="
log "Phase: $PHASE | Hosts: $TARGET_HOST | Dry: $DRY"
echo ""
case "$PHASE" in
baseline)
for h in $NODES; do
on_host "$h" && { phase_sync "$h"; phase_baseline "$h"; }
done ;;
db)
for h in $NODES; do
on_host "$h" && { phase_sync "$h"; phase_db "$h"; }
done ;;
app)
for h in $NODES; do
on_host "$h" && { phase_sync "$h"; phase_app "$h"; }
done
on_host cast01 && phase_nginx_reload ;;
sfu)
phase_sync cast02
phase_sync cast01
phase_sfu ;;
all)
# 1. Sync all nodes
for h in $NODES; do
on_host "$h" && phase_sync "$h"
done
# 2. Baseline on all
for h in $NODES; do
on_host "$h" && phase_baseline "$h"
done
# 3. DB on cast01 (primary + replicas follow)
on_host cast01 && phase_db cast01
# 4. App on all nodes
for h in $NODES; do
on_host "$h" && phase_app "$h"
done
# 5. SFU
phase_sfu
# 6. Verify
on_host cast01 && phase_verify ;;
*)
fail "Unknown phase: $PHASE (use: baseline|db|app|sfu|all)" ;;
esac
echo ""
ok "=== Deployment complete (phase=$PHASE) ==="