refactor: rename ScadaLink → ZB.MOM.WW.ScadaBridge (code + projects + namespaces)

Solution + 23 src projects + 26 test projects renamed; folders, csproj,
namespaces, and ScadaLinkDbContext/ScadaBridgeDbContext class updated.
ActorSystem "scadalink" → "scadabridge", Akka seed-node URLs migrated.
SQL roles/logins, LDAP domains, CLI command name, and CLI config dir
(~/.scadalink → ~/.scadabridge) also renamed.

Build green; 5 Host.Tests fail awaiting SQL login rename in next commit.
Pre-existing StaleTagMonitor timing flakes unchanged.

Rename script committed at tools/rename-to-scadabridge.sh.
This commit is contained in:
Joseph Doherty
2026-05-28 09:37:45 -04:00
parent 6d87ee3c3b
commit 7b0b9c7365
1531 changed files with 11180 additions and 11054 deletions
+12 -12
View File
@@ -1,6 +1,6 @@
# ScadaLink Test Infrastructure
# ScadaBridge Test Infrastructure
Local Docker-based test services for ScadaLink development.
Local Docker-based test services for ScadaBridge development.
## Quick Start
@@ -12,9 +12,9 @@ This starts the following services:
| Service | Port | Purpose |
|---------|------|---------|
| OPC UA (Azure IoT OPC PLC) | 50000 (OPC UA), 8080 (web) | Simulated OPC UA server with ScadaLink-style tags |
| OPC UA (Azure IoT OPC PLC) | 50000 (OPC UA), 8080 (web) | Simulated OPC UA server with ScadaBridge-style tags |
| OPC UA 2 (Azure IoT OPC PLC) | 50010 (OPC UA), 8081 (web) | Second OPC UA server instance (same tags, independent state) |
| LDAP (GLAuth) | 3893 | Lightweight LDAP with test users/groups matching ScadaLink roles |
| LDAP (GLAuth) | 3893 | Lightweight LDAP with test users/groups matching ScadaBridge roles |
| MS SQL 2022 | 1433 | Configuration and machine data databases |
| SMTP (Mailpit) | 1025 (SMTP), 8025 (web) | Email capture for notification testing |
| REST API (Flask) | 5200 | External REST API for Gateway and Inbound API testing |
@@ -24,28 +24,28 @@ This starts the following services:
The MS SQL container does not auto-run init scripts. After the first `docker compose up -d`, run:
```bash
docker exec -i scadalink-mssql /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P 'ScadaLink_Dev1#' -C \
docker exec -i scadabridge-mssql /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P 'ScadaBridge_Dev1#' -C \
-i /docker-entrypoint-initdb.d/setup.sql
```
This creates the `ScadaLinkConfig` and `ScadaLinkMachineData` databases and the `scadalink_app` login. Then seed the Machine Data database:
This creates the `ScadaBridgeConfig` and `ScadaBridgeMachineData` databases and the `scadabridge_app` login. Then seed the Machine Data database:
```bash
docker exec -i scadalink-mssql /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P 'ScadaLink_Dev1#' -C \
docker exec -i scadabridge-mssql /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P 'ScadaBridge_Dev1#' -C \
-i /docker-entrypoint-initdb.d/machinedata_seed.sql
```
For the second environment (`docker-env2/`), also apply the env2 database setup:
```bash
docker exec -i scadalink-mssql /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P 'ScadaLink_Dev1#' -C \
docker exec -i scadabridge-mssql /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P 'ScadaBridge_Dev1#' -C \
-i /docker-entrypoint-initdb.d/setup-env2.sql
```
This creates `ScadaLinkConfig2` and `ScadaLinkMachineData2` databases on the same MSSQL instance. The script is also invoked automatically by `docker-env2/deploy.sh` via `docker-env2/init-db.sh`, so manual application here is only needed if you want the databases ready before first env2 deploy.
This creates `ScadaBridgeConfig2` and `ScadaBridgeMachineData2` databases on the same MSSQL instance. The script is also invoked automatically by `docker-env2/deploy.sh` via `docker-env2/init-db.sh`, so manual application here is only needed if you want the databases ready before first env2 deploy.
## Stopping & Teardown
+19 -19
View File
@@ -1,7 +1,7 @@
services:
opcua:
image: mcr.microsoft.com/iotedge/opc-plc:latest
container_name: scadalink-opcua
container_name: scadabridge-opcua
ports:
- "50000:50000"
- "8080:8080"
@@ -17,12 +17,12 @@ services:
--nf=/app/config/nodes.json
--pn=50000
networks:
- scadalink-net
- scadabridge-net
restart: unless-stopped
opcua2:
image: mcr.microsoft.com/iotedge/opc-plc:latest
container_name: scadalink-opcua2
container_name: scadabridge-opcua2
ports:
- "50010:50010"
- "8081:8080"
@@ -38,41 +38,41 @@ services:
--nf=/app/config/nodes.json
--pn=50010
networks:
- scadalink-net
- scadabridge-net
restart: unless-stopped
ldap:
image: glauth/glauth:latest
container_name: scadalink-ldap
container_name: scadabridge-ldap
ports:
- "3893:3893"
volumes:
- ./glauth/config.toml:/app/config/config.cfg:ro
networks:
- scadalink-net
- scadabridge-net
restart: unless-stopped
mssql:
image: mcr.microsoft.com/mssql/server:2022-latest
container_name: scadalink-mssql
container_name: scadabridge-mssql
ports:
- "1433:1433"
environment:
ACCEPT_EULA: "Y"
MSSQL_SA_PASSWORD: "ScadaLink_Dev1#"
MSSQL_SA_PASSWORD: "ScadaBridge_Dev1#"
MSSQL_PID: "Developer"
volumes:
- scadalink-mssql-data:/var/opt/mssql
- scadabridge-mssql-data:/var/opt/mssql
- ./mssql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:ro
- ./mssql/machinedata_seed.sql:/docker-entrypoint-initdb.d/machinedata_seed.sql:ro
- ./mssql/setup-env2.sql:/docker-entrypoint-initdb.d/setup-env2.sql:ro
networks:
- scadalink-net
- scadabridge-net
restart: unless-stopped
smtp:
image: axllent/mailpit:latest
container_name: scadalink-smtp
container_name: scadabridge-smtp
ports:
- "1025:1025"
- "8025:8025"
@@ -81,24 +81,24 @@ services:
MP_SMTP_AUTH_ALLOW_INSECURE: 1
MP_MAX_MESSAGES: 500
networks:
- scadalink-net
- scadabridge-net
restart: unless-stopped
restapi:
build: ./restapi
container_name: scadalink-restapi
container_name: scadabridge-restapi
ports:
- "5200:5200"
environment:
API_NO_AUTH: 0
PORT: 5200
networks:
- scadalink-net
- scadabridge-net
restart: unless-stopped
playwright:
image: mcr.microsoft.com/playwright:v1.58.2-noble
container_name: scadalink-playwright
container_name: scadabridge-playwright
ports:
- "3000:3000"
command: >
@@ -107,13 +107,13 @@ services:
--port 3000
ipc: host
networks:
- scadalink-net
- scadabridge-net
restart: unless-stopped
volumes:
scadalink-mssql-data:
scadabridge-mssql-data:
networks:
scadalink-net:
name: scadalink-net
scadabridge-net:
name: scadabridge-net
external: true
+6 -6
View File
@@ -7,7 +7,7 @@
[backend]
datastore = "config"
baseDN = "dc=scadalink,dc=local"
baseDN = "dc=scadabridge,dc=local"
# ── Groups ──────────────────────────────────────────────────────────
@@ -35,7 +35,7 @@
name = "admin"
givenname = "Admin"
sn = "User"
mail = "admin@scadalink.local"
mail = "admin@scadabridge.local"
uidnumber = 5001
primarygroup = 5501
passsha256 = "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
@@ -47,7 +47,7 @@
name = "designer"
givenname = "Designer"
sn = "User"
mail = "designer@scadalink.local"
mail = "designer@scadabridge.local"
uidnumber = 5002
primarygroup = 5502
passsha256 = "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
@@ -56,7 +56,7 @@
name = "deployer"
givenname = "Deployer"
sn = "User"
mail = "deployer@scadalink.local"
mail = "deployer@scadabridge.local"
uidnumber = 5003
primarygroup = 5503
passsha256 = "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
@@ -65,7 +65,7 @@
name = "site-deployer"
givenname = "Site"
sn = "Deployer"
mail = "site-deployer@scadalink.local"
mail = "site-deployer@scadabridge.local"
uidnumber = 5004
primarygroup = 5504
passsha256 = "5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8"
@@ -74,7 +74,7 @@
name = "multi-role"
givenname = "Multi"
sn = "Role"
mail = "multi-role@scadalink.local"
mail = "multi-role@scadabridge.local"
uidnumber = 5005
primarygroup = 5501
othergroups = [5502, 5503]
+6 -6
View File
@@ -1,13 +1,13 @@
-- ScadaLink Machine Data Database seed script
-- Populates ScadaLinkMachineData with realistic SCADA/MES tables,
-- ScadaBridge Machine Data Database seed script
-- Populates ScadaBridgeMachineData with realistic SCADA/MES tables,
-- sample data, and stored procedures for development and testing.
--
-- Run after setup.sql:
-- docker exec -i scadalink-mssql /opt/mssql-tools18/bin/sqlcmd \
-- -S localhost -U sa -P 'ScadaLink_Dev1#' -C \
-- docker exec -i scadabridge-mssql /opt/mssql-tools18/bin/sqlcmd \
-- -S localhost -U sa -P 'ScadaBridge_Dev1#' -C \
-- -i /docker-entrypoint-initdb.d/machinedata_seed.sql
USE ScadaLinkMachineData;
USE ScadaBridgeMachineData;
GO
-- =========================================================================
@@ -324,7 +324,7 @@ INSERT INTO dbo.AlarmHistory (SiteId, AlarmName, Severity, State, ActivatedAt, A
('SiteB', 'Mixer-001 Vibration', 2, 'Active', DATEADD(HOUR, -2, @now), NULL, NULL, NULL, 'Vibration level elevated');
GO
PRINT 'ScadaLinkMachineData seed complete.';
PRINT 'ScadaBridgeMachineData seed complete.';
PRINT 'Tables: TagHistory, ProductionCounts, EquipmentEvents, BatchRecords, AlarmHistory';
PRINT 'Stored Procedures: usp_GetTagHistory, usp_GetProductionSummary, usp_InsertBatchRecord, usp_CompleteBatch, usp_GetEquipmentEvents, usp_GetActiveAlarms';
GO
+6 -6
View File
@@ -1,5 +1,5 @@
-- ScadaLink design-data seed.
-- Auto-generated by infra/tools/dump_seed.py against ScadaLinkConfig.
-- ScadaBridge design-data seed.
-- Auto-generated by infra/tools/dump_seed.py against ScadaBridgeConfig.
-- Replays the design-time configuration (templates, scripts,
-- data connections, external systems). Idempotent: deletes
-- existing rows in the covered tables before inserting.
@@ -177,15 +177,15 @@ SET IDENTITY_INSERT [SharedScripts] OFF;
-- DataConnections (3 rows)
SET IDENTITY_INSERT [DataConnections] ON;
INSERT INTO [DataConnections] ([Id], [Name], [Protocol], [PrimaryConfiguration], [SiteId], [BackupConfiguration], [FailoverRetryCount]) VALUES (1, N'OPC PLC Simulator', N'OpcUa', N'{"endpointUrl":"opc.tcp://scadalink-opcua:50000","securityMode":"none","autoAcceptUntrustedCerts":true,"sessionTimeoutMs":60000,"operationTimeoutMs":15000,"publishingIntervalMs":1000,"samplingIntervalMs":1000,"queueSize":10,"keepAliveCount":10,"lifetimeCount":30,"maxNotificationsPerPublish":100,"discardOldest":true,"subscriptionPriority":0,"subscriptionDisplayName":"ScadaLink","timestampsToReturn":"source","deadband":null,"userIdentity":null,"heartbeat":null}', 1, NULL, 3);
INSERT INTO [DataConnections] ([Id], [Name], [Protocol], [PrimaryConfiguration], [SiteId], [BackupConfiguration], [FailoverRetryCount]) VALUES (3014, N'OPC PLC Simulator', N'OpcUa', N'{"endpoint":"opc.tcp://scadalink-opcua:50000","securityMode":"None","publishInterval":1000}', 2, NULL, 3);
INSERT INTO [DataConnections] ([Id], [Name], [Protocol], [PrimaryConfiguration], [SiteId], [BackupConfiguration], [FailoverRetryCount]) VALUES (3015, N'OPC PLC Simulator', N'OpcUa', N'{"endpoint":"opc.tcp://scadalink-opcua:50000","securityMode":"None","publishInterval":1000}', 3, NULL, 3);
INSERT INTO [DataConnections] ([Id], [Name], [Protocol], [PrimaryConfiguration], [SiteId], [BackupConfiguration], [FailoverRetryCount]) VALUES (1, N'OPC PLC Simulator', N'OpcUa', N'{"endpointUrl":"opc.tcp://scadabridge-opcua:50000","securityMode":"none","autoAcceptUntrustedCerts":true,"sessionTimeoutMs":60000,"operationTimeoutMs":15000,"publishingIntervalMs":1000,"samplingIntervalMs":1000,"queueSize":10,"keepAliveCount":10,"lifetimeCount":30,"maxNotificationsPerPublish":100,"discardOldest":true,"subscriptionPriority":0,"subscriptionDisplayName":"ScadaBridge","timestampsToReturn":"source","deadband":null,"userIdentity":null,"heartbeat":null}', 1, NULL, 3);
INSERT INTO [DataConnections] ([Id], [Name], [Protocol], [PrimaryConfiguration], [SiteId], [BackupConfiguration], [FailoverRetryCount]) VALUES (3014, N'OPC PLC Simulator', N'OpcUa', N'{"endpoint":"opc.tcp://scadabridge-opcua:50000","securityMode":"None","publishInterval":1000}', 2, NULL, 3);
INSERT INTO [DataConnections] ([Id], [Name], [Protocol], [PrimaryConfiguration], [SiteId], [BackupConfiguration], [FailoverRetryCount]) VALUES (3015, N'OPC PLC Simulator', N'OpcUa', N'{"endpoint":"opc.tcp://scadabridge-opcua:50000","securityMode":"None","publishInterval":1000}', 3, NULL, 3);
SET IDENTITY_INSERT [DataConnections] OFF;
-- ExternalSystemDefinitions (1 rows)
-- NOTE: [AuthConfiguration] is an encrypted secret column — dumped as NULL. Restore via the app (CLI/API) post-seed.
SET IDENTITY_INSERT [ExternalSystemDefinitions] ON;
INSERT INTO [ExternalSystemDefinitions] ([Id], [Name], [EndpointUrl], [AuthType], [AuthConfiguration], [MaxRetries], [RetryDelay]) VALUES (1, N'Test REST API', N'http://scadalink-restapi:5200', N'ApiKey', NULL, 0, '00:00:00.000000');
INSERT INTO [ExternalSystemDefinitions] ([Id], [Name], [EndpointUrl], [AuthType], [AuthConfiguration], [MaxRetries], [RetryDelay]) VALUES (1, N'Test REST API', N'http://scadabridge-restapi:5200', N'ApiKey', NULL, 0, '00:00:00.000000');
SET IDENTITY_INSERT [ExternalSystemDefinitions] OFF;
-- ExternalSystemMethods (1 rows)
+17 -17
View File
@@ -1,31 +1,31 @@
-- ScadaLink env2 database setup
-- Creates env2 logical databases on an existing scadalink-mssql instance.
-- ScadaBridge env2 database setup
-- Creates env2 logical databases on an existing scadabridge-mssql instance.
-- Idempotent: re-runs are no-ops. Assumes setup.sql has already run
-- (i.e. the scadalink_app login already exists).
-- (i.e. the scadabridge_app login already exists).
-- Create env2 databases
IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = 'ScadaLinkConfig2')
CREATE DATABASE ScadaLinkConfig2;
IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = 'ScadaBridgeConfig2')
CREATE DATABASE ScadaBridgeConfig2;
GO
IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = 'ScadaLinkMachineData2')
CREATE DATABASE ScadaLinkMachineData2;
IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = 'ScadaBridgeMachineData2')
CREATE DATABASE ScadaBridgeMachineData2;
GO
-- Grant db_owner on ScadaLinkConfig2
USE ScadaLinkConfig2;
-- Grant db_owner on ScadaBridgeConfig2
USE ScadaBridgeConfig2;
GO
IF NOT EXISTS (SELECT name FROM sys.database_principals WHERE name = 'scadalink_app')
CREATE USER scadalink_app FOR LOGIN scadalink_app;
IF NOT EXISTS (SELECT name FROM sys.database_principals WHERE name = 'scadabridge_app')
CREATE USER scadabridge_app FOR LOGIN scadabridge_app;
GO
ALTER ROLE db_owner ADD MEMBER scadalink_app;
ALTER ROLE db_owner ADD MEMBER scadabridge_app;
GO
-- Grant db_owner on ScadaLinkMachineData2
USE ScadaLinkMachineData2;
-- Grant db_owner on ScadaBridgeMachineData2
USE ScadaBridgeMachineData2;
GO
IF NOT EXISTS (SELECT name FROM sys.database_principals WHERE name = 'scadalink_app')
CREATE USER scadalink_app FOR LOGIN scadalink_app;
IF NOT EXISTS (SELECT name FROM sys.database_principals WHERE name = 'scadabridge_app')
CREATE USER scadabridge_app FOR LOGIN scadabridge_app;
GO
ALTER ROLE db_owner ADD MEMBER scadalink_app;
ALTER ROLE db_owner ADD MEMBER scadabridge_app;
GO
+17 -17
View File
@@ -1,36 +1,36 @@
-- ScadaLink development database setup
-- ScadaBridge development database setup
-- Run against a fresh MS SQL 2022 instance.
-- EF Core migrations handle schema creation; this script only creates
-- the empty databases and the application login/user.
-- Create databases
IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = 'ScadaLinkConfig')
CREATE DATABASE ScadaLinkConfig;
IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = 'ScadaBridgeConfig')
CREATE DATABASE ScadaBridgeConfig;
GO
IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = 'ScadaLinkMachineData')
CREATE DATABASE ScadaLinkMachineData;
IF NOT EXISTS (SELECT name FROM sys.databases WHERE name = 'ScadaBridgeMachineData')
CREATE DATABASE ScadaBridgeMachineData;
GO
-- Create application login
IF NOT EXISTS (SELECT name FROM sys.server_principals WHERE name = 'scadalink_app')
CREATE LOGIN scadalink_app WITH PASSWORD = 'ScadaLink_Dev1#', DEFAULT_DATABASE = ScadaLinkConfig;
IF NOT EXISTS (SELECT name FROM sys.server_principals WHERE name = 'scadabridge_app')
CREATE LOGIN scadabridge_app WITH PASSWORD = 'ScadaBridge_Dev1#', DEFAULT_DATABASE = ScadaBridgeConfig;
GO
-- Grant db_owner on ScadaLinkConfig
USE ScadaLinkConfig;
-- Grant db_owner on ScadaBridgeConfig
USE ScadaBridgeConfig;
GO
IF NOT EXISTS (SELECT name FROM sys.database_principals WHERE name = 'scadalink_app')
CREATE USER scadalink_app FOR LOGIN scadalink_app;
IF NOT EXISTS (SELECT name FROM sys.database_principals WHERE name = 'scadabridge_app')
CREATE USER scadabridge_app FOR LOGIN scadabridge_app;
GO
ALTER ROLE db_owner ADD MEMBER scadalink_app;
ALTER ROLE db_owner ADD MEMBER scadabridge_app;
GO
-- Grant db_owner on ScadaLinkMachineData
USE ScadaLinkMachineData;
-- Grant db_owner on ScadaBridgeMachineData
USE ScadaBridgeMachineData;
GO
IF NOT EXISTS (SELECT name FROM sys.database_principals WHERE name = 'scadalink_app')
CREATE USER scadalink_app FOR LOGIN scadalink_app;
IF NOT EXISTS (SELECT name FROM sys.database_principals WHERE name = 'scadabridge_app')
CREATE USER scadabridge_app FOR LOGIN scadabridge_app;
GO
ALTER ROLE db_owner ADD MEMBER scadalink_app;
ALTER ROLE db_owner ADD MEMBER scadabridge_app;
GO
+1 -1
View File
@@ -1,5 +1,5 @@
{
"Folder": "ScadaLink",
"Folder": "ScadaBridge",
"NodeList": [],
"FolderList": [
{
+17 -17
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Full reseed of the ScadaLink test cluster.
# Full reseed of the ScadaBridge test cluster.
#
# Tears down infra + app containers, drops the MSSQL volume, brings
# everything back, lets EF Core migrations create the schema, replays
@@ -14,7 +14,7 @@
# 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)
# - Built scadabridge:latest image (docker/build.sh — deploy.sh runs it)
set -euo pipefail
@@ -50,7 +50,7 @@ if [ ! -f "$SEED_FILE" ]; then
exit 1
fi
echo "=== ScadaLink Reseed ==="
echo "=== ScadaBridge Reseed ==="
echo "Seed file: $SEED_FILE"
echo ""
@@ -76,20 +76,20 @@ if ! $SKIP_TEARDOWN; then
(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
until docker exec scadabridge-mssql /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P 'ScadaBridge_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;" \
echo " Waiting for setup.sql to create ScadaBridgeConfig..."
until docker exec scadabridge-mssql /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P 'ScadaBridge_Dev1#' -C \
-Q "IF DB_ID('ScadaBridgeConfig') IS NULL THROW 50000, 'not ready', 1;" \
>/dev/null 2>&1; do
sleep 2
done
echo " ScadaLinkConfig present."
echo " ScadaBridgeConfig present."
echo ""
echo "--- Stage 5/6: deploy central + site nodes ---"
@@ -110,8 +110,8 @@ echo "--- Stage 6b/6: seed sites (CLI) ---"
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"
docker exec -i scadabridge-mssql /opt/mssql-tools18/bin/sqlcmd \
-S localhost -U sa -P 'ScadaBridge_Dev1#' -C -d ScadaBridgeConfig -b < "$SEED_FILE"
echo " Seed replayed."
echo ""
@@ -120,7 +120,7 @@ echo "--- Stage 6d/6: restore encrypted secret config (CLI) ---"
# 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 --"
CLI="dotnet run --project $PROJECT_ROOT/src/ZB.MOM.WW.ScadaBridge.CLI --"
AUTH="--username multi-role --password password"
# ExternalSystemDefinitions Id 1 ("Test REST API") is inserted by the seed with
@@ -128,9 +128,9 @@ AUTH="--username multi-role --password password"
$CLI --url "$MGMT_URL" $AUTH external-system update \
--id 1 \
--name "Test REST API" \
--endpoint-url "http://scadalink-restapi:5200" \
--endpoint-url "http://scadabridge-restapi:5200" \
--auth-type ApiKey \
--auth-config "scadalink-test-key-1"
--auth-config "scadabridge-test-key-1"
echo " External-system auth config restored (encrypted)."
# The "Machine Data DB" database connection is referenced by name from the
@@ -138,7 +138,7 @@ echo " External-system auth config restored (encrypted)."
# 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" \
--connection-string "Server=scadabridge-mssql,1433;Database=ScadaBridgeMachineData;User Id=scadabridge_app;Password=ScadaBridge_Dev1#;TrustServerCertificate=true" \
|| echo " (Machine Data DB connection may already exist)"
echo " Database connection created (encrypted)."
@@ -146,7 +146,7 @@ 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 " $PROJECT_ROOT/src/ZB.MOM.WW.ScadaBridge.CLI/bin/Debug/net*/ZB.MOM.WW.ScadaBridge.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"
+2 -2
View File
@@ -1,4 +1,4 @@
"""External REST API test server for ScadaLink test infrastructure."""
"""External REST API test server for ScadaBridge test infrastructure."""
import os
import time
@@ -9,7 +9,7 @@ from flask import Flask, jsonify, request
app = Flask(__name__)
START_TIME = time.time()
API_KEY = "scadalink-test-key-1"
API_KEY = "scadabridge-test-key-1"
NO_AUTH = os.environ.get("API_NO_AUTH", "0") == "1"
+2 -2
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# Tear down ScadaLink test infrastructure.
# Tear down ScadaBridge test infrastructure.
#
# Drops the MSSQL data volume by default, so the ScadaLinkConfig DB
# Drops the MSSQL data volume by default, so the ScadaBridgeConfig DB
# (templates, scripts, data connections, etc.) is wiped. Use
# infra/reseed.sh afterwards to restore the design state from
# infra/mssql/seed-config.sql.
+6 -6
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Dump design tables from ScadaLinkConfig to a replayable SQL seed file.
"""Dump design tables from ScadaBridgeConfig to a replayable SQL seed file.
Usage:
python3 infra/tools/dump_seed.py --output infra/mssql/seed-config.sql
@@ -18,7 +18,7 @@ Encrypted secret columns (see ENCRYPTED_COLUMNS) are emitted as NULL: they
hold ASP.NET Data Protection ciphertext, which is non-deterministic and bound
to the source key ring, so a raw SQL dump can never replay a valid value.
Re-populate them through the application after the seed runs (infra/reseed.sh
does this via the ScadaLink CLI).
does this via the ScadaBridge CLI).
"""
import argparse
@@ -31,8 +31,8 @@ import pymssql
DEFAULT_HOST = "localhost"
DEFAULT_PORT = 1433
DEFAULT_USER = "sa"
DEFAULT_PASSWORD = "ScadaLink_Dev1#"
DEFAULT_DATABASE = "ScadaLinkConfig"
DEFAULT_PASSWORD = "ScadaBridge_Dev1#"
DEFAULT_DATABASE = "ScadaBridgeConfig"
INSERT_ORDER = [
"TemplateFolders",
@@ -52,7 +52,7 @@ INSERT_ORDER = [
IDENTITY_TABLES = set(INSERT_ORDER)
# (table, column) pairs encrypted at rest via ASP.NET Data Protection
# (EncryptedStringConverter in ScadaLink.ConfigurationDatabase). Ciphertext is
# (EncryptedStringConverter in ZB.MOM.WW.ScadaBridge.ConfigurationDatabase). Ciphertext is
# non-deterministic and key-ring-bound, so it cannot be replayed from a static
# SQL dump — the application would fail to decrypt it on read. These columns
# are dumped as NULL; re-seed their values through the app (CLI / API) so the
@@ -118,7 +118,7 @@ def dump(args):
cursor = conn.cursor()
out = []
out.append("-- ScadaLink design-data seed.")
out.append("-- ScadaBridge design-data seed.")
out.append("-- Auto-generated by infra/tools/dump_seed.py against " + args.database + ".")
out.append("-- Replays the design-time configuration (templates, scripts,")
out.append("-- data connections, external systems). Idempotent: deletes")
+5 -5
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""LDAP client tool for ScadaLink test infrastructure."""
"""LDAP client tool for ScadaBridge test infrastructure."""
import argparse
import sys
@@ -9,10 +9,10 @@ from ldap3 import Server, Connection, NONE, SUBTREE, SIMPLE
DEFAULT_HOST = "localhost"
DEFAULT_PORT = 3893
DEFAULT_BASE_DN = "dc=scadalink,dc=local"
DEFAULT_BASE_DN = "dc=scadabridge,dc=local"
# GLAuth places users under ou=<PrimaryGroupName>,ou=users,dc=...
# The admin user (primarygroup SCADA-Admins) needs search capabilities in config.
DEFAULT_BIND_DN = "cn=admin,ou=SCADA-Admins,ou=users,dc=scadalink,dc=local"
DEFAULT_BIND_DN = "cn=admin,ou=SCADA-Admins,ou=users,dc=scadabridge,dc=local"
DEFAULT_BIND_PASSWORD = "password"
@@ -48,7 +48,7 @@ def cmd_check(args):
def cmd_bind(args):
"""Test user authentication via bind.
GLAuth DN format: cn=<user>,ou=<PrimaryGroup>,ou=users,dc=scadalink,dc=local
GLAuth DN format: cn=<user>,ou=<PrimaryGroup>,ou=users,dc=scadabridge,dc=local
Since we don't know the user's primary group upfront, we search for the user first
to discover the full DN, then rebind with that DN.
"""
@@ -188,7 +188,7 @@ def cmd_groups(args):
def main():
parser = argparse.ArgumentParser(description="LDAP client tool for ScadaLink test infrastructure")
parser = argparse.ArgumentParser(description="LDAP client tool for ScadaBridge test infrastructure")
parser.add_argument("--host", default=DEFAULT_HOST, help=f"LDAP host (default: {DEFAULT_HOST})")
parser.add_argument("--port", type=int, default=DEFAULT_PORT, help=f"LDAP port (default: {DEFAULT_PORT})")
+4 -4
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""MS SQL client tool for ScadaLink test infrastructure."""
"""MS SQL client tool for ScadaBridge test infrastructure."""
import argparse
import sys
@@ -10,8 +10,8 @@ import pymssql
DEFAULT_HOST = "localhost"
DEFAULT_PORT = 1433
DEFAULT_USER = "sa"
DEFAULT_PASSWORD = "ScadaLink_Dev1#"
EXPECTED_DBS = ["ScadaLinkConfig", "ScadaLinkMachineData"]
DEFAULT_PASSWORD = "ScadaBridge_Dev1#"
EXPECTED_DBS = ["ScadaBridgeConfig", "ScadaBridgeMachineData"]
def get_connection(args, database=None):
@@ -168,7 +168,7 @@ def cmd_tables(args):
def main():
parser = argparse.ArgumentParser(description="MS SQL client tool for ScadaLink test infrastructure")
parser = argparse.ArgumentParser(description="MS SQL client tool for ScadaBridge test infrastructure")
parser.add_argument("--host", default=DEFAULT_HOST, help=f"SQL Server host (default: {DEFAULT_HOST})")
parser.add_argument("--port", type=int, default=DEFAULT_PORT, help=f"Port (default: {DEFAULT_PORT})")
parser.add_argument("--user", default=DEFAULT_USER, help=f"Username (default: {DEFAULT_USER})")
+2 -2
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""OPC UA client tool for ScadaLink test infrastructure."""
"""OPC UA client tool for ScadaBridge test infrastructure."""
import argparse
import sys
@@ -168,7 +168,7 @@ def cmd_monitor(args):
def main():
parser = argparse.ArgumentParser(description="OPC UA client tool for ScadaLink test infrastructure")
parser = argparse.ArgumentParser(description="OPC UA client tool for ScadaBridge test infrastructure")
parser.add_argument("--endpoint", default=DEFAULT_ENDPOINT, help=f"OPC UA endpoint (default: {DEFAULT_ENDPOINT})")
sub = parser.add_subparsers(dest="command", required=True)
+1 -1
View File
@@ -6,7 +6,7 @@ from playwright.sync_api import sync_playwright
# The browser runs inside Docker, so use the Docker network hostname for Traefik.
# The Playwright server WebSocket is exposed to the host on port 3000.
TRAEFIK_URL = "http://scadalink-traefik"
TRAEFIK_URL = "http://scadabridge-traefik"
PLAYWRIGHT_WS = "ws://localhost:3000"
+3 -3
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""REST API client tool for ScadaLink test infrastructure."""
"""REST API client tool for ScadaBridge test infrastructure."""
import argparse
import json
@@ -9,7 +9,7 @@ import requests
DEFAULT_URL = "http://localhost:5200"
DEFAULT_API_KEY = "scadalink-test-key-1"
DEFAULT_API_KEY = "scadabridge-test-key-1"
def cmd_check(args):
@@ -122,7 +122,7 @@ def cmd_methods(args):
def main():
parser = argparse.ArgumentParser(description="REST API client tool for ScadaLink test infrastructure")
parser = argparse.ArgumentParser(description="REST API client tool for ScadaBridge test infrastructure")
parser.add_argument("--url", default=DEFAULT_URL, help=f"API base URL (default: {DEFAULT_URL})")
parser.add_argument("--api-key", default=DEFAULT_API_KEY, help=f"API key (default: {DEFAULT_API_KEY})")
+3 -3
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""SMTP/Mailpit client tool for ScadaLink test infrastructure."""
"""SMTP/Mailpit client tool for ScadaBridge test infrastructure."""
import argparse
import email.mime.text
@@ -147,7 +147,7 @@ def cmd_clear(args):
def main():
parser = argparse.ArgumentParser(description="SMTP/Mailpit client tool for ScadaLink test infrastructure")
parser = argparse.ArgumentParser(description="SMTP/Mailpit client tool for ScadaBridge test infrastructure")
parser.add_argument("--host", default=DEFAULT_SMTP_HOST, help=f"SMTP host (default: {DEFAULT_SMTP_HOST})")
parser.add_argument("--port", type=int, default=DEFAULT_SMTP_PORT, help=f"SMTP port (default: {DEFAULT_SMTP_PORT})")
parser.add_argument("--api", default=DEFAULT_API_URL, help=f"Mailpit API URL (default: {DEFAULT_API_URL})")
@@ -162,7 +162,7 @@ def main():
send_p.add_argument("--to", required=True, help="Recipient address")
send_p.add_argument("--bcc", help="Comma-separated BCC addresses")
send_p.add_argument("--subject", default="Test notification", help="Subject line")
send_p.add_argument("--body", default="This is a test notification from ScadaLink.", help="Message body")
send_p.add_argument("--body", default="This is a test notification from ZB.MOM.WW.ScadaBridge.", help="Message body")
list_p = sub.add_parser("list", help="List messages in Mailpit inbox")
list_p.add_argument("--limit", type=int, default=20, help="Number of messages to show (default: 20)")