Wifiv2 / entrypoint.sh
Mbonea's picture
Add runtime log filters for startup and migrations
2324ed0
Raw
History Blame Contribute Delete
12.9 kB
#!/bin/bash
set -e
DATA_ROOT="${DATA_ROOT:-/data/wifiplatform}"
DATA_DIR="${DB_DATA_DIR:-$DATA_ROOT/mysql}"
SOURCE_DATA_ROOT="${SOURCE_DATA_ROOT:-}"
LOG_DIR="$DATA_ROOT/logs"
PAYMENT_STORAGE_DIR="$DATA_ROOT/storage/payment"
QUARANTINE_DIR="$DATA_ROOT/quarantine/mysql-table-artifacts"
INIT_FLAG="$DATA_ROOT/.db_initialized"
RESTORE_SQL_PATH="${RESTORE_SQL_PATH:-}"
DB_DIR="$DATA_DIR/${DB_NAME:-wifiplatform}"
MYSQL_SYSTEM_DIR="$DATA_DIR/mysql"
DEFAULT_DISABLED_LOG_TYPES="env,migrate,migration"
normalize_log_type() {
printf '%s' "$1" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9]+/-/g; s/^-+//; s/-+$//'
}
log_type_list_contains() {
local list="$1"
local needle
needle="$(normalize_log_type "$2")"
[ -n "$needle" ] || return 1
IFS=',' read -ra items <<< "$list"
for item in "${items[@]}"; do
item="$(normalize_log_type "$item")"
if [ "$item" = "$needle" ] || [ "$item" = "all" ]; then
return 0
fi
done
return 1
}
log_type_enabled() {
local type="$1"
local disabled="${LOG_DISABLED_TYPES:-$DEFAULT_DISABLED_LOG_TYPES}"
local enabled="${LOG_ENABLED_TYPES:-}"
if log_type_list_contains "$enabled" "all" || log_type_list_contains "$enabled" "$type"; then
return 0
fi
if log_type_list_contains "$disabled" "all" || log_type_list_contains "$disabled" "$type"; then
return 1
fi
return 0
}
repair_blocking_table_artifacts() {
[ -d "$DB_DIR" ] || return 0
local quarantine_root="$QUARANTINE_DIR/$(date '+%Y%m%d%H%M%S')"
local found=0
local candidate=""
for table in \
clients \
client_staff \
devices \
device_payments \
device_status_log \
wifi_plans \
access_tokens \
payments \
sessions \
payouts \
balance_ledger \
omada_alerts \
sms_log \
password_resets \
system_config \
device_portal_settings \
device_portal_payment_options \
token_compensations \
token_deauth_events \
client_notification_preferences \
client_payment_credentials
do
for candidate in "$DB_DIR/$table" "$DB_DIR/$table".*; do
[ -e "$candidate" ] || continue
[ -d "$candidate" ] || continue
mkdir -p "$quarantine_root"
echo "[entrypoint] Found blocking MariaDB directory artifact: $(basename "$candidate")"
mv "$candidate" "$quarantine_root/$(basename "$candidate")"
found=1
done
done
if [ "$found" -eq 1 ]; then
echo "[entrypoint] Moved blocking MariaDB artifacts to $quarantine_root"
fi
}
dump_mariadb_diagnostics() {
echo "[entrypoint][diag] MariaDB version:"
mysqld --version 2>&1 || true
echo "[entrypoint][diag] Data directory listing:"
ls -la "$DATA_DIR" 2>&1 || true
echo "[entrypoint][diag] Runtime socket directory listing:"
ls -la /run/mysqld 2>&1 || true
echo "[entrypoint][diag] Recent MariaDB log output:"
tail -n 120 "$LOG_DIR/mariadb.log" 2>/dev/null || true
}
stop_mariadb_pid() {
local pid="${1:-}"
[ -n "$pid" ] || return 0
if kill -0 "$pid" 2>/dev/null; then
echo "[entrypoint] Stopping MariaDB process $pid..."
kill "$pid" 2>/dev/null || true
for i in $(seq 1 15); do
if ! kill -0 "$pid" 2>/dev/null; then
break
fi
sleep 1
done
if kill -0 "$pid" 2>/dev/null; then
echo "[entrypoint] MariaDB process $pid did not stop; forcing termination..."
kill -9 "$pid" 2>/dev/null || true
fi
fi
rm -f /run/mysqld/mysqld.sock /run/mysqld/mysqld.pid
}
clone_source_data_root_if_requested() {
[ -n "$SOURCE_DATA_ROOT" ] || return 0
local source_mysql="$SOURCE_DATA_ROOT/mysql"
echo "[entrypoint] SOURCE_DATA_ROOT=$SOURCE_DATA_ROOT"
echo "[entrypoint] Clone-on-boot requested from $source_mysql to $DATA_DIR"
if [ "$source_mysql" = "$DATA_DIR" ]; then
echo "[entrypoint] ERROR: SOURCE_DATA_ROOT and DATA_ROOT resolve to the same mysql directory."
exit 1
fi
if [ ! -d "$source_mysql/mysql" ]; then
echo "[entrypoint] ERROR: source MariaDB system tables not found at $source_mysql/mysql"
exit 1
fi
if [ -d "$MYSQL_SYSTEM_DIR" ]; then
echo "[entrypoint] Target MariaDB system tables already exist at $MYSQL_SYSTEM_DIR; skipping clone."
return 0
fi
if [ -e "$DATA_DIR" ]; then
echo "[entrypoint] ERROR: target data dir exists but has no MariaDB system tables: $DATA_DIR"
echo "[entrypoint] Use a fresh DATA_ROOT for clone-on-boot."
exit 1
fi
mkdir -p "$DATA_ROOT"
echo "[entrypoint] Copying MariaDB files from source data root..."
cp -a "$source_mysql" "$DATA_DIR"
if [ -d "$SOURCE_DATA_ROOT/storage" ] && [ ! -d "$DATA_ROOT/storage" ]; then
echo "[entrypoint] Copying storage assets from source data root..."
cp -a "$SOURCE_DATA_ROOT/storage" "$DATA_ROOT/storage"
fi
touch "$INIT_FLAG"
echo "[entrypoint] Clone-on-boot completed; first-run DB initialization will be skipped."
}
echo "===== Application Startup at $(date '+%F %T') ====="
echo "[entrypoint] Starting WiFi Platform..."
echo "[entrypoint] Running as: $(id)"
if log_type_enabled "env"; then
echo "[entrypoint] Environment variables:"
printenv | sort | while IFS= read -r line; do
key="${line%%=*}"
case "$key" in
*TOKEN*|*SECRET*|*PASS*|*PASSWORD*|*KEY*)
echo "[env] $key=[redacted]"
;;
*)
echo "[env] $line"
;;
esac
done
else
echo "[entrypoint] Environment variable logging disabled (enable with LOG_ENABLED_TYPES=env)."
fi
echo "[entrypoint] DATA_ROOT=$DATA_ROOT"
echo "[entrypoint] DATA_DIR=$DATA_DIR"
echo "[entrypoint] DB_NAME=${DB_NAME:-wifiplatform}"
# ── Ensure persistent dirs exist (writable on HF /data volume) ──
clone_source_data_root_if_requested
mkdir -p "$DATA_DIR" "$LOG_DIR" "$PAYMENT_STORAGE_DIR" "$QUARANTINE_DIR"
repair_blocking_table_artifacts
# ── First-run: initialize + load schema ──
if [ ! -f "$INIT_FLAG" ]; then
echo "[entrypoint] First run β€” initializing MariaDB data directory..."
echo "[entrypoint] MariaDB binaries:"
command -v mysql_install_db || true
command -v mysqld || true
command -v mysql || true
mysqld --version || true
if [ -d "$MYSQL_SYSTEM_DIR" ]; then
echo "[entrypoint] Existing MariaDB system tables found at $MYSQL_SYSTEM_DIR; skipping mysql_install_db."
echo "[entrypoint] This usually means a previous bootstrap partially completed before $INIT_FLAG was written."
else
# Initialize WITHOUT --user=mysql (we ARE the user)
echo "[entrypoint] Running mysql_install_db..."
mysql_install_db \
--datadir="$DATA_DIR" \
--auth-root-authentication-method=normal \
--skip-test-db > "$LOG_DIR/mysql_install_db.log" 2>&1 || {
echo "[entrypoint] ERROR: mysql_install_db failed. Output:"
cat "$LOG_DIR/mysql_install_db.log" 2>/dev/null || true
exit 1
}
echo "[entrypoint] mysql_install_db output:"
cat "$LOG_DIR/mysql_install_db.log" 2>/dev/null || true
fi
# Start MariaDB temporarily β€” use mysqld directly, not mysqld_safe
# mysqld_safe tries to chown things internally
echo "[entrypoint] Starting temporary MariaDB for setup..."
mysqld \
--datadir="$DATA_DIR" \
--socket=/run/mysqld/mysqld.sock \
--skip-networking \
--log-error="$LOG_DIR/mariadb.log" &
MYSQL_PID=$!
# Wait for MariaDB to be ready
echo "[entrypoint] Waiting for MariaDB to start..."
for i in $(seq 1 30); do
if mysqladmin ping --socket=/run/mysqld/mysqld.sock --silent 2>/dev/null; then
echo "[entrypoint] MariaDB is up (attempt $i)."
break
fi
if [ "$i" -eq 30 ]; then
echo "[entrypoint] ERROR: MariaDB failed to start. Log output:"
cat "$LOG_DIR/mariadb.log" 2>/dev/null || true
exit 1
fi
sleep 1
done
echo "[entrypoint] Probing MariaDB root socket access..."
if ! mysql -u root --socket=/run/mysqld/mysqld.sock -e "SELECT CURRENT_USER(), USER(), @@version, @@datadir;" 2>&1; then
echo "[entrypoint] ERROR: root socket login failed before bootstrap SQL."
dump_mariadb_diagnostics
echo "[entrypoint][diag] Restarting temporary MariaDB with --skip-grant-tables to inspect and repair auth rows..."
stop_mariadb_pid "$MYSQL_PID"
wait "$MYSQL_PID" 2>/dev/null || true
mysqld \
--datadir="$DATA_DIR" \
--socket=/run/mysqld/mysqld.sock \
--skip-networking \
--skip-grant-tables \
--log-error="$LOG_DIR/mariadb-skip-grants.log" &
MYSQL_PID=$!
for i in $(seq 1 30); do
if mysqladmin --socket=/run/mysqld/mysqld.sock ping --silent 2>/dev/null; then
echo "[entrypoint][diag] MariaDB skip-grants instance is up (attempt $i)."
break
fi
if [ "$i" -eq 30 ]; then
echo "[entrypoint][diag] ERROR: skip-grants instance failed to start."
cat "$LOG_DIR/mariadb-skip-grants.log" 2>/dev/null || true
stop_mariadb_pid "$MYSQL_PID"
exit 1
fi
sleep 1
done
echo "[entrypoint][diag] mysql.global_priv rows:"
mysql --socket=/run/mysqld/mysqld.sock -N -e "SELECT Host, User, Priv FROM mysql.global_priv;" 2>&1 || true
echo "[entrypoint][diag] mysql.user rows:"
mysql --socket=/run/mysqld/mysqld.sock -N -e "SELECT Host, User, plugin, authentication_string FROM mysql.user;" 2>&1 || true
echo "[entrypoint][repair] Resetting root@localhost to passwordless mysql_native_password for bootstrap..."
mysql --socket=/run/mysqld/mysqld.sock <<SQL
UPDATE mysql.global_priv
SET Priv = JSON_SET(
JSON_REMOVE(COALESCE(Priv, JSON_OBJECT()), '$.auth_or'),
'$.plugin',
'mysql_native_password',
'$.authentication_string',
''
)
WHERE Host = 'localhost' AND User = 'root';
FLUSH PRIVILEGES;
SQL
stop_mariadb_pid "$MYSQL_PID"
wait "$MYSQL_PID" 2>/dev/null || true
echo "[entrypoint][repair] Restarting MariaDB after root auth repair..."
mysqld \
--datadir="$DATA_DIR" \
--socket=/run/mysqld/mysqld.sock \
--skip-networking \
--log-error="$LOG_DIR/mariadb.log" &
MYSQL_PID=$!
for i in $(seq 1 30); do
if mysqladmin ping --socket=/run/mysqld/mysqld.sock --silent 2>/dev/null; then
echo "[entrypoint][repair] MariaDB is back up (attempt $i)."
break
fi
if [ "$i" -eq 30 ]; then
echo "[entrypoint][repair] ERROR: MariaDB failed to restart after auth repair."
dump_mariadb_diagnostics
exit 1
fi
sleep 1
done
echo "[entrypoint][repair] Re-probing root socket access..."
mysql -u root --socket=/run/mysqld/mysqld.sock -e "SELECT CURRENT_USER(), USER(), @@version, @@datadir;" 2>&1 || {
echo "[entrypoint][repair] ERROR: root socket login still failed after auth repair."
dump_mariadb_diagnostics
exit 1
}
fi
echo "[entrypoint] Creating database and user..."
mysql -u root --socket=/run/mysqld/mysqld.sock <<SQL
CREATE DATABASE IF NOT EXISTS \`${DB_NAME:-wifiplatform}\`
CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER IF NOT EXISTS '${DB_USER:-wifiuser}'@'localhost'
IDENTIFIED BY '${DB_PASS:-wifipass}';
GRANT ALL PRIVILEGES ON \`${DB_NAME:-wifiplatform}\`.*
TO '${DB_USER:-wifiuser}'@'localhost';
FLUSH PRIVILEGES;
SQL
APP_TABLE_COUNT="$(mysql -u root --socket=/run/mysqld/mysqld.sock -N -B -e "SELECT COUNT(*) FROM information_schema.TABLES WHERE TABLE_SCHEMA = '${DB_NAME:-wifiplatform}' AND TABLE_TYPE = 'BASE TABLE';" 2>/dev/null || echo 0)"
APP_TABLE_COUNT="${APP_TABLE_COUNT:-0}"
if [ "$APP_TABLE_COUNT" -gt 0 ]; then
echo "[entrypoint] Database ${DB_NAME:-wifiplatform} already has $APP_TABLE_COUNT tables; skipping schema/restore load."
elif [ -n "$RESTORE_SQL_PATH" ] && [ -f "$RESTORE_SQL_PATH" ]; then
echo "[entrypoint] Restoring database from $RESTORE_SQL_PATH..."
mysql -u root --socket=/run/mysqld/mysqld.sock "${DB_NAME:-wifiplatform}" < "$RESTORE_SQL_PATH"
else
echo "[entrypoint] Loading schema..."
mysql -u root --socket=/run/mysqld/mysqld.sock "${DB_NAME:-wifiplatform}" < /app/schema.sql
fi
# Stop the temporary instance gracefully
mysqladmin -u root --socket=/run/mysqld/mysqld.sock shutdown
wait "$MYSQL_PID" 2>/dev/null || true
touch "$INIT_FLAG"
echo "[entrypoint] Database initialized successfully."
fi
# ── Start MariaDB for production ──
echo "[entrypoint] Starting MariaDB..."
mysqld \
--datadir="$DATA_DIR" \
--socket=/run/mysqld/mysqld.sock \
--log-error="$LOG_DIR/mariadb.log" &
echo "[entrypoint] Waiting for MariaDB to be ready..."
for i in $(seq 1 30); do
if mysqladmin ping --socket=/run/mysqld/mysqld.sock --silent 2>/dev/null; then
echo "[entrypoint] MariaDB ready."
break
fi
if [ "$i" -eq 30 ]; then
echo "[entrypoint] ERROR: MariaDB failed to start."
cat "$LOG_DIR/mariadb.log" 2>/dev/null || true
exit 1
fi
sleep 1
done
# ── Start Node app (exec replaces shell β†’ PID 1 β†’ clean signals) ──
echo "[entrypoint] Starting Node.js app..."
exec node src/server.js