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:
+12
-12
@@ -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
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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,5 +1,5 @@
|
||||
{
|
||||
"Folder": "ScadaLink",
|
||||
"Folder": "ScadaBridge",
|
||||
"NodeList": [],
|
||||
"FolderList": [
|
||||
{
|
||||
|
||||
+17
-17
@@ -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"
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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})")
|
||||
|
||||
|
||||
@@ -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})")
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
|
||||
@@ -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})")
|
||||
|
||||
|
||||
@@ -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)")
|
||||
|
||||
Reference in New Issue
Block a user