ASP.NET Data Protection ciphertext is non-deterministic and bound to the source key ring, so encrypted secret columns (ExternalSystemDefinitions .AuthConfiguration, SmtpConfigurations.Credentials, DatabaseConnection Definitions.ConnectionString) cannot be replayed from a static SQL dump — the app would fail to decrypt them. dump_seed.py now emits those columns as NULL; reseed.sh adds a post-seed stage that recreates the values through the ScadaLink CLI so the EF value converter re-encrypts against the target cluster's key ring.
153 lines
5.0 KiB
Bash
Executable File
153 lines
5.0 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Full reseed of the ScadaLink test cluster.
|
|
#
|
|
# Tears down infra + app containers, drops the MSSQL volume, brings
|
|
# everything back, lets EF Core migrations create the schema, replays
|
|
# infra/mssql/seed-config.sql for templates/scripts/data-connections, and
|
|
# re-seeds sites via docker/seed-sites.sh.
|
|
#
|
|
# Usage:
|
|
# infra/reseed.sh Full reseed (default seed file)
|
|
# infra/reseed.sh --seed PATH Replay a different seed SQL
|
|
# infra/reseed.sh --skip-teardown Replay seed against running stack
|
|
#
|
|
# Prerequisites:
|
|
# - Docker / OrbStack running
|
|
# - Python 3 with pymssql (used by infra/tools/mssql_tool.py + dump_seed.py)
|
|
# - Built scadalink:latest image (docker/build.sh — deploy.sh runs it)
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
SEED_FILE="$SCRIPT_DIR/mssql/seed-config.sql"
|
|
SKIP_TEARDOWN=false
|
|
MGMT_URL="http://localhost:9000"
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--seed)
|
|
SEED_FILE="$2"
|
|
shift 2
|
|
;;
|
|
--skip-teardown)
|
|
SKIP_TEARDOWN=true
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
sed -n '2,16p' "$0" | sed 's/^# \{0,1\}//'
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "Unknown option: $1" >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if [ ! -f "$SEED_FILE" ]; then
|
|
echo "Seed file not found: $SEED_FILE" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "=== ScadaLink Reseed ==="
|
|
echo "Seed file: $SEED_FILE"
|
|
echo ""
|
|
|
|
if ! $SKIP_TEARDOWN; then
|
|
echo "--- Stage 1/6: tear down application containers ---"
|
|
"$PROJECT_ROOT/docker/teardown.sh"
|
|
|
|
echo ""
|
|
echo "--- Stage 2/6: wipe site SQLite state ---"
|
|
shopt -s nullglob
|
|
for d in "$PROJECT_ROOT"/docker/site-*/data; do
|
|
rm -rf "$d"/*
|
|
echo " cleared $d"
|
|
done
|
|
shopt -u nullglob
|
|
|
|
echo ""
|
|
echo "--- Stage 3/6: tear down infra (drops MSSQL volume) ---"
|
|
(cd "$SCRIPT_DIR" && docker compose down -v)
|
|
|
|
echo ""
|
|
echo "--- Stage 4/6: bring infra back up ---"
|
|
(cd "$SCRIPT_DIR" && docker compose up -d)
|
|
|
|
echo " Waiting for MSSQL to accept connections..."
|
|
until docker exec scadalink-mssql /opt/mssql-tools18/bin/sqlcmd \
|
|
-S localhost -U sa -P 'ScadaLink_Dev1#' -C -Q "SELECT 1" >/dev/null 2>&1; do
|
|
sleep 2
|
|
done
|
|
echo " MSSQL ready."
|
|
|
|
echo " Waiting for setup.sql to create ScadaLinkConfig..."
|
|
until docker exec scadalink-mssql /opt/mssql-tools18/bin/sqlcmd \
|
|
-S localhost -U sa -P 'ScadaLink_Dev1#' -C \
|
|
-Q "IF DB_ID('ScadaLinkConfig') IS NULL THROW 50000, 'not ready', 1;" \
|
|
>/dev/null 2>&1; do
|
|
sleep 2
|
|
done
|
|
echo " ScadaLinkConfig present."
|
|
|
|
echo ""
|
|
echo "--- Stage 5/6: deploy central + site nodes ---"
|
|
"$PROJECT_ROOT/docker/deploy.sh"
|
|
fi
|
|
|
|
echo ""
|
|
echo "--- Stage 6a/6: wait for central cluster /health/ready ---"
|
|
until curl -fs "$MGMT_URL/health/ready" >/dev/null 2>&1; do
|
|
sleep 2
|
|
done
|
|
echo " Central cluster ready (EF Core migrations applied)."
|
|
|
|
echo ""
|
|
echo "--- Stage 6b/6: seed sites (CLI) ---"
|
|
# Sites must exist before the design seed: DataConnections.SiteId FKs to Sites.
|
|
"$PROJECT_ROOT/docker/seed-sites.sh"
|
|
|
|
echo ""
|
|
echo "--- Stage 6c/6: replay seed SQL ---"
|
|
docker exec -i scadalink-mssql /opt/mssql-tools18/bin/sqlcmd \
|
|
-S localhost -U sa -P 'ScadaLink_Dev1#' -C -d ScadaLinkConfig -b < "$SEED_FILE"
|
|
echo " Seed replayed."
|
|
|
|
echo ""
|
|
echo "--- Stage 6d/6: restore encrypted secret config (CLI) ---"
|
|
# Configuration that lives in encrypted secret columns cannot be replayed from
|
|
# raw SQL: ASP.NET Data Protection ciphertext is non-deterministic and bound to
|
|
# the source key ring. Create/restore it through the app so the EF value
|
|
# converter encrypts against this cluster's key ring.
|
|
CLI="dotnet run --project $PROJECT_ROOT/src/ScadaLink.CLI --"
|
|
AUTH="--username multi-role --password password"
|
|
|
|
# ExternalSystemDefinitions Id 1 ("Test REST API") is inserted by the seed with
|
|
# a fixed identity but a NULL AuthConfiguration; set the API key here.
|
|
$CLI --url "$MGMT_URL" $AUTH external-system update \
|
|
--id 1 \
|
|
--name "Test REST API" \
|
|
--endpoint-url "http://scadalink-restapi:5200" \
|
|
--auth-type ApiKey \
|
|
--auth-config "scadalink-test-key-1"
|
|
echo " External-system auth config restored (encrypted)."
|
|
|
|
# The "Machine Data DB" database connection is referenced by name from the
|
|
# seeded TestDatabaseQuery script. It is not in seed-config.sql (its
|
|
# ConnectionString is an encrypted secret column); create it through the app.
|
|
$CLI --url "$MGMT_URL" $AUTH db-connection create \
|
|
--name "Machine Data DB" \
|
|
--connection-string "Server=scadalink-mssql,1433;Database=ScadaLinkMachineData;User Id=scadalink_app;Password=ScadaLink_Dev1#;TrustServerCertificate=true" \
|
|
|| echo " (Machine Data DB connection may already exist)"
|
|
echo " Database connection created (encrypted)."
|
|
|
|
echo ""
|
|
echo "=== Reseed complete ==="
|
|
echo ""
|
|
echo "Verify:"
|
|
echo " $PROJECT_ROOT/src/ScadaLink.CLI/bin/Debug/net*/ScadaLink.CLI --url $MGMT_URL --username multi-role --password password template list"
|
|
echo ""
|
|
echo "To refresh the seed file from the current DB state:"
|
|
echo " python3 $SCRIPT_DIR/tools/dump_seed.py --output $SEED_FILE"
|