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:
@@ -8,7 +8,7 @@
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
Provide a file-based, encrypted, environment-agnostic way to **promote configuration artifacts from one ScadaLink cluster to another** (e.g., dev → staging → prod) through the Central UI.
|
||||
Provide a file-based, encrypted, environment-agnostic way to **promote configuration artifacts from one ScadaBridge cluster to another** (e.g., dev → staging → prod) through the Central UI.
|
||||
|
||||
A user with the `Design` role on the source cluster exports a selected set of templates and supporting artifacts to a `.scadabundle` file. A user with the `Admin` role on the target cluster uploads the bundle, reviews a diff, resolves conflicts, and applies it. Import is **config-only**: it updates the central configuration database and marks affected instances stale; the user redeploys to sites via the existing Deployments page.
|
||||
|
||||
@@ -18,11 +18,11 @@ Transport does **not** touch site nodes, does not move runtime state, and does n
|
||||
|
||||
## 2. Location
|
||||
|
||||
- New project: `src/ScadaLink.Transport/`
|
||||
- New tests: `tests/ScadaLink.Transport.Tests/`, `tests/ScadaLink.Transport.IntegrationTests/`
|
||||
- New project: `src/ZB.MOM.WW.ScadaBridge.Transport/`
|
||||
- New tests: `tests/ZB.MOM.WW.ScadaBridge.Transport.Tests/`, `tests/ZB.MOM.WW.ScadaBridge.Transport.IntegrationTests/`
|
||||
- New design doc: `docs/requirements/Component-Transport.md` (created as part of implementation).
|
||||
- Central UI pages: `src/ScadaLink.CentralUI/Components/Pages/Design/TransportExport.razor`, `TransportImport.razor`.
|
||||
- EF migration in `src/ScadaLink.ConfigurationDatabase/Migrations/` (adds `BundleImportId` column to `ConfigurationAuditLog`).
|
||||
- Central UI pages: `src/ZB.MOM.WW.ScadaBridge.CentralUI/Components/Pages/Design/TransportExport.razor`, `TransportImport.razor`.
|
||||
- EF migration in `src/ZB.MOM.WW.ScadaBridge.ConfigurationDatabase/Migrations/` (adds `BundleImportId` column to `ConfigurationAuditLog`).
|
||||
|
||||
---
|
||||
|
||||
@@ -123,7 +123,7 @@ The manifest is plaintext so the import wizard can preview bundle contents and s
|
||||
## 5. Architecture (Option A — new component)
|
||||
|
||||
```
|
||||
ScadaLink.Transport
|
||||
ZB.MOM.WW.ScadaBridge.Transport
|
||||
├── IBundleExporter
|
||||
│ ExportAsync(ExportSelection, Passphrase?, ct) → Stream
|
||||
├── IBundleImporter
|
||||
@@ -137,8 +137,8 @@ ScadaLink.Transport
|
||||
└── ManifestValidator (schema/version gating, hash check)
|
||||
```
|
||||
|
||||
- Component is **central-only**. Registered in `ScadaLink.Host` for central roles, never for site roles.
|
||||
- All persistence goes through **existing audited repository interfaces** in `ScadaLink.ConfigurationDatabase` — no raw `DbContext.SaveChangesAsync` from this component.
|
||||
- Component is **central-only**. Registered in `ZB.MOM.WW.ScadaBridge.Host` for central roles, never for site roles.
|
||||
- All persistence goes through **existing audited repository interfaces** in `ZB.MOM.WW.ScadaBridge.ConfigurationDatabase` — no raw `DbContext.SaveChangesAsync` from this component.
|
||||
- `BundleSessionStore` is in-process on the active central node (matches Blazor Server circuit affinity). 30-minute TTL, GC on expiry, 3-strike passphrase lockout per session.
|
||||
|
||||
Rejected alternatives:
|
||||
@@ -260,8 +260,8 @@ When an applied conflict overwrites a template (or a template composed by other
|
||||
|
||||
| Where | Failure | Surfaced as |
|
||||
|---|---|---|
|
||||
| Upload | Not a zip / missing `manifest.json` | Step 1 error: "Not a valid ScadaLink bundle" |
|
||||
| Upload | `bundleFormatVersion` newer than supported | Step 1 error: "Bundle was created by ScadaLink v{x}; upgrade this cluster" |
|
||||
| Upload | Not a zip / missing `manifest.json` | Step 1 error: "Not a valid ScadaBridge bundle" |
|
||||
| Upload | `bundleFormatVersion` newer than supported | Step 1 error: "Bundle was created by ScadaBridge v{x}; upgrade this cluster" |
|
||||
| Upload | Content hash mismatch | Step 1 error: "Bundle integrity check failed — file may be corrupt" |
|
||||
| Unlock | Wrong passphrase | Step 2 error; 3rd wrong attempt invalidates session, audit `BundleImportUnlockFailed` |
|
||||
| Preview | Bundle references shared script not in bundle and not in target DB | Listed as a blocker row in Step 3; cannot Apply until resolved |
|
||||
@@ -281,7 +281,7 @@ Imports are **all-or-nothing per bundle.** A bundle either applies fully or not
|
||||
- **Bundle size cap** on upload (default 100 MB, configurable) to bound memory.
|
||||
- **In-transit:** existing HTTPS to Central UI; no new channel.
|
||||
- **Audit trail is the chain of custody.** Every export, every import (including aborted ones at validation), every unlock failure is audit-logged with source env, content hash, encrypted yes/no, and artifact summary.
|
||||
- **Defense in depth on authorization:** `RequireDesign` (export) and `RequireAdmin` (import) enforced both on the Razor page and inside `ScadaLink.Transport` service entrypoints. UI is not the only gate.
|
||||
- **Defense in depth on authorization:** `RequireDesign` (export) and `RequireAdmin` (import) enforced both on the Razor page and inside `ZB.MOM.WW.ScadaBridge.Transport` service entrypoints. UI is not the only gate.
|
||||
- **Bundles are not retained server-side** after download (export) or after `ApplyAsync` commits (import).
|
||||
|
||||
---
|
||||
@@ -334,16 +334,16 @@ Import flows through the **same audited repository methods** the UI and CLI use,
|
||||
|
||||
## 13. CLI (Deferred)
|
||||
|
||||
The `ScadaLink.Transport` library is callable from both Razor pages and `ScadaLink.CLI`. CLI commands are **not** built in v1 but the design leaves a clean path:
|
||||
The `ZB.MOM.WW.ScadaBridge.Transport` library is callable from both Razor pages and `ZB.MOM.WW.ScadaBridge.CLI`. CLI commands are **not** built in v1 but the design leaves a clean path:
|
||||
|
||||
```
|
||||
scadalink transport export \
|
||||
scadabridge transport export \
|
||||
--templates Pump,Pump.WaterPump \
|
||||
--shared-scripts PumpUtils \
|
||||
--out bundle.scadabundle \
|
||||
--passphrase-file /run/secrets/p
|
||||
|
||||
scadalink transport import bundle.scadabundle \
|
||||
scadabridge transport import bundle.scadabundle \
|
||||
--passphrase-file /run/secrets/p \
|
||||
--on-conflict overwrite|skip|rename \
|
||||
--dry-run
|
||||
@@ -357,16 +357,16 @@ Same auth model via the Management API.
|
||||
|
||||
| Layer | Project | Coverage |
|
||||
|---|---|---|
|
||||
| Unit | `tests/ScadaLink.Transport.Tests` (new) | `BundleSerializer` round-trip, `DependencyResolver` topology + cycle handling, `SecretEncryptor` (encrypt → decrypt round-trip, wrong passphrase fails, tampered ciphertext fails GCM auth tag), `ManifestBuilder` schema-version gating, `ImportPreview` diff for each entity type (identical / modified / new), per-conflict resolution merge logic, stale-instance cascade through composed templates |
|
||||
| Integration | `tests/ScadaLink.Transport.IntegrationTests` (new) | End-to-end against in-memory + real SQL Server: export → import on same DB (no-op idempotency), export → wipe → import (full restore), export → modify-target → import under each conflict resolution, validation failure rolls back cleanly, audit rows correlated by `BundleImportId` |
|
||||
| UI | `tests/ScadaLink.CentralUI.Tests` | Razor page authorization (`RequireDesign` for export, `RequireAdmin` for import), wizard step navigation, session TTL expiry, tri-state checkbox tree behavior |
|
||||
| Unit | `tests/ZB.MOM.WW.ScadaBridge.Transport.Tests` (new) | `BundleSerializer` round-trip, `DependencyResolver` topology + cycle handling, `SecretEncryptor` (encrypt → decrypt round-trip, wrong passphrase fails, tampered ciphertext fails GCM auth tag), `ManifestBuilder` schema-version gating, `ImportPreview` diff for each entity type (identical / modified / new), per-conflict resolution merge logic, stale-instance cascade through composed templates |
|
||||
| Integration | `tests/ZB.MOM.WW.ScadaBridge.Transport.IntegrationTests` (new) | End-to-end against in-memory + real SQL Server: export → import on same DB (no-op idempotency), export → wipe → import (full restore), export → modify-target → import under each conflict resolution, validation failure rolls back cleanly, audit rows correlated by `BundleImportId` |
|
||||
| UI | `tests/ZB.MOM.WW.ScadaBridge.CentralUI.Tests` | Razor page authorization (`RequireDesign` for export, `RequireAdmin` for import), wizard step navigation, session TTL expiry, tri-state checkbox tree behavior |
|
||||
| Manual | docker cluster | Cross-cluster: bring up two clusters, export from one, import to the other, verify stale instances surface on Deployments page, redeploy works |
|
||||
|
||||
---
|
||||
|
||||
## 15. Deployment
|
||||
|
||||
- New component registered in `ScadaLink.Host` for central roles only.
|
||||
- New component registered in `ZB.MOM.WW.ScadaBridge.Host` for central roles only.
|
||||
- No new ports, no new database (uses existing central MS SQL).
|
||||
- One EF migration: `BundleImportId` nullable `uniqueidentifier` column on `ConfigurationAuditLog` + supporting index.
|
||||
- `bash docker/deploy.sh` picks up the change with the standard rebuild + restart — no compose changes.
|
||||
@@ -398,9 +398,9 @@ Same auth model via the Management API.
|
||||
## 18. Open Questions / Future Work
|
||||
|
||||
- **Site-scoped artifact transport** (Instances, Areas, bindings, DataConnections). Requires a name-mapping subsystem (source-env-site → target-env-site). Deferred until concrete demand.
|
||||
- **Direct cluster-to-cluster pull** as an alternative to file handoff. Same `ScadaLink.Transport` library can back it; needs cross-env auth design.
|
||||
- **Direct cluster-to-cluster pull** as an alternative to file handoff. Same `ZB.MOM.WW.ScadaBridge.Transport` library can back it; needs cross-env auth design.
|
||||
- **Bundle signing** with an asymmetric keypair (separate from passphrase encryption) for stronger non-repudiation. Manifest content hash is sufficient for v1 tamper detection.
|
||||
- **CLI commands** (`scadalink transport export/import`). Shape pre-decided in §13; not built in v1.
|
||||
- **CLI commands** (`scadabridge transport export/import`). Shape pre-decided in §13; not built in v1.
|
||||
- **Differential bundles** ("only what changed since last export"). YAGNI for v1.
|
||||
|
||||
---
|
||||
@@ -420,4 +420,4 @@ Same auth model via the Management API.
|
||||
| Post-import behavior | Config-only; user redeploys via existing Deployments page |
|
||||
| Authorization | `Design` to export, `Admin` to import |
|
||||
| Audit | Per-entity rows via existing audited repositories, correlated by `BundleImportId` |
|
||||
| Architecture | New component `ScadaLink.Transport` (Option A) |
|
||||
| Architecture | New component `ZB.MOM.WW.ScadaBridge.Transport` (Option A) |
|
||||
|
||||
Reference in New Issue
Block a user