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:
@@ -2,7 +2,7 @@
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| Module | `src/ScadaLink.DeploymentManager` |
|
||||
| Module | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager` |
|
||||
| Design doc | `docs/requirements/Component-DeploymentManager.md` |
|
||||
| Status | Reviewed |
|
||||
| Last reviewed | 2026-05-28 |
|
||||
@@ -109,7 +109,7 @@ The remaining six findings are medium/low: lifecycle-timeout audit gap
|
||||
| Severity | High |
|
||||
| Category | Error handling & resilience |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/DeploymentService.cs:141-199` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/DeploymentService.cs:141-199` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -151,7 +151,7 @@ stuck in `InProgress`. Regression test:
|
||||
| Severity | High |
|
||||
| Category | Error handling & resilience |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/DeploymentService.cs:186-196` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/DeploymentService.cs:186-196` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -190,7 +190,7 @@ error) if persistence still fails. Regression test:
|
||||
| Severity | Medium |
|
||||
| Category | Error handling & resilience |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/DeploymentService.cs:155-170` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/DeploymentService.cs:155-170` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -237,7 +237,7 @@ Regression test:
|
||||
| Severity | Medium |
|
||||
| Category | Error handling & resilience |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/DeploymentService.cs:312-319` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/DeploymentService.cs:312-319` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -278,7 +278,7 @@ record is orphaned and must be reconciled. Regression test:
|
||||
| Severity | Medium |
|
||||
| Category | Performance & resource management |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/OperationLockManager.cs:15-33` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/OperationLockManager.cs:15-33` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -323,7 +323,7 @@ Regression tests: `AcquireAsync_ReleasedLock_RemovesSemaphoreEntry`,
|
||||
| Severity | High |
|
||||
| Category | Design-document adherence |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/DeploymentService.cs:84-200,363-368` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/DeploymentService.cs:84-200,363-368` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -386,7 +386,7 @@ stale-rejection) when the query fails. Regression tests:
|
||||
| Severity | Medium |
|
||||
| Category | Design-document adherence |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/DeploymentService.cs:334-358,401-406` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/DeploymentService.cs:334-358,401-406` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -432,7 +432,7 @@ Regression test: `GetDeploymentComparisonAsync_ProducesStructuredDiff`.
|
||||
| Severity | Medium |
|
||||
| Category | Code organization & conventions |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/ServiceCollectionExtensions.cs:7-14` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/ServiceCollectionExtensions.cs:7-14` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -453,14 +453,14 @@ no equivalent for `DeploymentManagerOptions`.
|
||||
|
||||
Add an `IConfiguration` parameter (or a configure callback) to
|
||||
`AddDeploymentManager` and bind `DeploymentManagerOptions` to a section such as
|
||||
`ScadaLink:DeploymentManager`, consistent with the other components.
|
||||
`ScadaBridge:DeploymentManager`, consistent with the other components.
|
||||
|
||||
**Resolution**
|
||||
|
||||
Resolved 2026-05-16 (commit pending): `AddDeploymentManager()` now calls
|
||||
`services.AddOptions<DeploymentManagerOptions>()` so `IOptions<DeploymentManagerOptions>`
|
||||
is always resolvable, and `Host/Program.cs` binds the
|
||||
`ScadaLink:DeploymentManager` section (exposed as
|
||||
`ScadaBridge:DeploymentManager` section (exposed as
|
||||
`ServiceCollectionExtensions.OptionsSection`) via
|
||||
`services.Configure<DeploymentManagerOptions>(...)` — the same pattern the Host
|
||||
uses for `SecurityOptions`/`InboundApiOptions`. An earlier attempt added an
|
||||
@@ -479,7 +479,7 @@ configuration binding. Regression tests:
|
||||
| Severity | Low |
|
||||
| Category | Documentation & comments |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/DeploymentService.cs:288` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/DeploymentService.cs:288` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -515,7 +515,7 @@ would be meaningless).
|
||||
| Severity | Low |
|
||||
| Category | Correctness & logic bugs |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/ArtifactDeploymentService.cs:136,194-211` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/ArtifactDeploymentService.cs:136,194-211` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -546,8 +546,8 @@ the audit log, and the UI summary all reference a single id instead of N+1
|
||||
unrelated GUIDs (`RetryForSiteAsync`, an independent single-site retry, still
|
||||
mints its own id). Adding a dedicated `DeploymentId` *column* to
|
||||
`SystemArtifactDeploymentRecord` was deliberately **not** done: that entity
|
||||
lives in `ScadaLink.Commons` with its EF mapping in
|
||||
`ScadaLink.ConfigurationDatabase`, both outside this module's edit scope.
|
||||
lives in `ZB.MOM.WW.ScadaBridge.Commons` with its EF mapping in
|
||||
`ZB.MOM.WW.ScadaBridge.ConfigurationDatabase`, both outside this module's edit scope.
|
||||
Instead the logical `deploymentId` is embedded in the record's free-form
|
||||
`PerSiteStatus` JSON payload (`{ DeploymentId, Sites }`), which is fully within
|
||||
this module's control, so the persisted record is correlatable with the
|
||||
@@ -564,7 +564,7 @@ Regression tests: `DeployToAllSitesAsync_AllPerSiteCommandsShareTheSummaryDeploy
|
||||
| Severity | Medium |
|
||||
| Category | Testing coverage |
|
||||
| Status | Resolved |
|
||||
| Location | `tests/ScadaLink.DeploymentManager.Tests/DeploymentServiceTests.cs:100-151,155-199` |
|
||||
| Location | `tests/ZB.MOM.WW.ScadaBridge.DeploymentManager.Tests/DeploymentServiceTests.cs:100-151,155-199` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -619,7 +619,7 @@ which asserts on `IAuditService.LogAsync`. Regression tests:
|
||||
| Severity | Low |
|
||||
| Category | Documentation & comments |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/DeploymentManagerOptions.cs:8-9` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/DeploymentManagerOptions.cs:8-9` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -664,7 +664,7 @@ the call hung the full 30 s and threw `AskTimeoutException`).
|
||||
| Severity | Low |
|
||||
| Category | Security |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/ArtifactDeploymentService.cs:108-111` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/ArtifactDeploymentService.cs:108-111` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -690,14 +690,14 @@ into the artifact (which the design explicitly mandates — SMTP configuration i
|
||||
a deployable artifact) and **never logs it** — the three log statements in
|
||||
`DeployToAllSitesAsync` only reference `SiteId`, `SiteName`, `DeploymentId`, and
|
||||
`ex.Message`, never the credential. There is no defect to fix purely within
|
||||
`src/ScadaLink.DeploymentManager`. The finding's remaining recommendations are
|
||||
`src/ZB.MOM.WW.ScadaBridge.DeploymentManager`. The finding's remaining recommendations are
|
||||
all cross-module and one needs a design decision:
|
||||
- inter-cluster transport TLS — `ScadaLink.Communication` /
|
||||
`ScadaLink.ClusterInfrastructure` (Akka remoting + ClusterClient config);
|
||||
- at-rest encryption of the credential on site SQLite — `ScadaLink.SiteRuntime`
|
||||
- inter-cluster transport TLS — `ZB.MOM.WW.ScadaBridge.Communication` /
|
||||
`ZB.MOM.WW.ScadaBridge.ClusterInfrastructure` (Akka remoting + ClusterClient config);
|
||||
- at-rest encryption of the credential on site SQLite — `ZB.MOM.WW.ScadaBridge.SiteRuntime`
|
||||
artifact store;
|
||||
- encrypting the credential field inside the artifact payload — needs the
|
||||
`SmtpConfigurationArtifact` shape in `ScadaLink.Commons` plus cooperating
|
||||
`SmtpConfigurationArtifact` shape in `ZB.MOM.WW.ScadaBridge.Commons` plus cooperating
|
||||
producer (DeploymentManager) and consumer (SiteRuntime) changes, and a
|
||||
**key-management design decision** (where the encryption key lives, how it
|
||||
is distributed to sites) that cannot be made unilaterally here.
|
||||
@@ -733,7 +733,7 @@ option rather than an open defect.
|
||||
| Severity | Low |
|
||||
| Category | Testing coverage |
|
||||
| Status | Resolved |
|
||||
| Location | `tests/ScadaLink.DeploymentManager.Tests/ArtifactDeploymentServiceTests.cs:86-90` |
|
||||
| Location | `tests/ZB.MOM.WW.ScadaBridge.DeploymentManager.Tests/ArtifactDeploymentServiceTests.cs:86-90` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -773,7 +773,7 @@ covers DeploymentManager-010), `DeployToAllSitesAsync_PartialFailure_ReportsPerS
|
||||
| Severity | High |
|
||||
| Category | Correctness & logic bugs |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/DeploymentService.cs:631-655` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/DeploymentService.cs:631-655` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -834,7 +834,7 @@ threaded into `TryReconcileWithSiteAsync`). Regression test:
|
||||
| Severity | Medium |
|
||||
| Category | Correctness & logic bugs |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/DeploymentService.cs:639-651` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/DeploymentService.cs:639-651` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -877,7 +877,7 @@ entry, and the site's actually-applied revision all agree. Regression test:
|
||||
| Severity | Low |
|
||||
| Category | Documentation & comments |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/DeploymentService.cs:562-570` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/DeploymentService.cs:562-570` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -913,7 +913,7 @@ would be meaningless).
|
||||
| Severity | High |
|
||||
| Category | Correctness & logic bugs |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/DeploymentService.cs:675-682,721-748` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/DeploymentService.cs:675-682,721-748` |
|
||||
|
||||
**Resolution** — Added a `forceEnabledState` parameter to `ApplyPostSuccessSideEffectsAsync`. The normal deploy path passes `true` (fresh apply legitimately ends in `Enabled`); the reconciliation path passes `false`, so the helper only promotes `NotDeployed → Enabled` and leaves an existing `Disabled` (or `Enabled`) untouched. Regression test `DeployInstanceAsync_Reconciled_DisabledInstance_PreservesDisabledState` exercises the failover scenario and asserts the prior record still flips to `Success` while `Instance.State` stays `Disabled`.
|
||||
|
||||
@@ -974,7 +974,7 @@ be marked `Success`.
|
||||
| Severity | Medium |
|
||||
| Category | Error handling & resilience |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/DeploymentService.cs:328-339,385-396,445-458` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/DeploymentService.cs:328-339,385-396,445-458` |
|
||||
|
||||
**Resolution (2026-05-28):** added `TryLogLifecycleTimeoutAsync`, a private
|
||||
helper that mirrors the `DeployFailed` pattern — it calls `_auditService.LogAsync`
|
||||
@@ -1032,7 +1032,7 @@ also produces an audit entry.
|
||||
| Severity | Low |
|
||||
| Category | Documentation & comments |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/DeploymentService.cs:698-712` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/DeploymentService.cs:698-712` |
|
||||
|
||||
**Description**
|
||||
|
||||
@@ -1077,7 +1077,7 @@ as the actor. Tests green (80/80 in DeploymentManager.Tests).
|
||||
| Severity | Low |
|
||||
| Category | Correctness & logic bugs |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/DeploymentService.cs:107-111` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/DeploymentService.cs:107-111` |
|
||||
|
||||
**Resolution (2026-05-28):** `ResolveSiteIdentifierAsync` now throws `InvalidOperationException` (`"Site with ID {siteId} not found; cannot resolve its SiteIdentifier for routing."`) when the `Site` row is missing, instead of returning the numeric id rendered as a string. The deploy path's existing try/catch turns the throw into a `DeploymentStatus.Failed` record carrying the descriptive message (the `DeploymentManager-001`/`-002` cleanup write the failure with `CancellationToken.None`); the lifecycle paths (Disable/Enable/Delete) propagate the exception so the CLI/UI caller surfaces the actual cause to the operator rather than seeing a confusing downstream "unknown site" routing error. The repository contract already returned `Site?`, so the null path is now type-visible at the call site instead of silently papered over.
|
||||
|
||||
@@ -1117,7 +1117,7 @@ returns `Site?`, so the null path is type-visible; just don't paper over it.
|
||||
| Severity | Low |
|
||||
| Category | Code organization & conventions |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/DeploymentService.cs:178-194` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/DeploymentService.cs:178-194` |
|
||||
|
||||
**Resolution (2026-05-28):** The transient `Pending` write was dropped — the deployment record is now created directly in `DeploymentStatus.InProgress`, which collapses the start of the deploy into a single `AddDeploymentRecordAsync` + `SaveChangesAsync` + `NotifyStatusChange` (instead of two writes back-to-back). The flattening, validation, and `TryReconcileWithSiteAsync` round-trip have all completed before the insert, and the deploy command is sent immediately after, so `Pending` carried no operational meaning between the two writes. `InProgress` retains its documented "sent to site, awaiting response" semantics. Eliminating the extra `SaveChangesAsync` round-trip also removes the `Pending`→`InProgress` flicker the CentralUI-006 deployment-status page used to render via the second `IDeploymentStatusNotifier.NotifyStatusChanged` invocation.
|
||||
|
||||
@@ -1162,7 +1162,7 @@ Either:
|
||||
| Severity | Low |
|
||||
| Category | Performance & resource management |
|
||||
| Status | Resolved |
|
||||
| Location | `src/ScadaLink.DeploymentManager/ArtifactDeploymentService.cs:82-144,169-173` |
|
||||
| Location | `src/ZB.MOM.WW.ScadaBridge.DeploymentManager/ArtifactDeploymentService.cs:82-144,169-173` |
|
||||
|
||||
**Resolution (2026-05-28):** Hoisted the global artifact queries (shared scripts, external systems + methods, DB connections, notification lists, SMTP configurations) out of the per-site loop into a new private `FetchGlobalArtifactsAsync` that produces a `GlobalArtifactSnapshot` record. `DeployToAllSitesAsync` now calls it ONCE before the loop and threads the snapshot through a new prefetched-globals overload of `BuildDeployArtifactsCommandAsync`; the public single-site overload keeps the prior fetch-then-build behaviour for `RetryForSiteAsync`. Only the per-site data-connection query remains inside the loop. Regression tests `DeployToAllSitesAsync_HoistsGlobalArtifactQueriesOutOfPerSiteLoop` (three sites; pins exactly-one call to each global getter and one per-site call to `GetDataConnectionsBySiteIdAsync`) and `RetryForSiteAsync_SingleSitePath_StillRunsTheGlobalQueriesOnce` (single-site path still owns its own fetch).
|
||||
|
||||
@@ -1207,9 +1207,9 @@ N-site deployment.
|
||||
| Severity | Low |
|
||||
| Category | Testing coverage |
|
||||
| Status | Resolved |
|
||||
| Location | `tests/ScadaLink.DeploymentManager.Tests/DeploymentServiceTests.cs:966-1075`, `tests/ScadaLink.DeploymentManager.Tests/ArtifactDeploymentServiceTests.cs:196-217` |
|
||||
| Location | `tests/ZB.MOM.WW.ScadaBridge.DeploymentManager.Tests/DeploymentServiceTests.cs:966-1075`, `tests/ZB.MOM.WW.ScadaBridge.DeploymentManager.Tests/ArtifactDeploymentServiceTests.cs:196-217` |
|
||||
|
||||
**Resolution (2026-05-28):** Replaced the `static` counters with per-test instance state. Introduced `ReconcileProbeCounters` and `SerializationProbeCounters` (in `DeploymentServiceTests`) and `ArtifactProbeRecorder` (in `ArtifactDeploymentServiceTests`); each probe actor now takes the counter object as its first constructor argument. Every test instantiates a fresh counter local, passes it via `Props.Create(() => new ReconcileProbeActor(counters, ...))`, and reads the counts directly off `counters` — no shared static fields remain. `ReconcileProbeActor`'s counter increments swap to `Interlocked.Increment` for the cross-thread CAS, and `SerializationProbeActor` retains its lock on a per-test `Gate`. All 85 `ScadaLink.DeploymentManager.Tests` continue to pass after the refactor.
|
||||
**Resolution (2026-05-28):** Replaced the `static` counters with per-test instance state. Introduced `ReconcileProbeCounters` and `SerializationProbeCounters` (in `DeploymentServiceTests`) and `ArtifactProbeRecorder` (in `ArtifactDeploymentServiceTests`); each probe actor now takes the counter object as its first constructor argument. Every test instantiates a fresh counter local, passes it via `Props.Create(() => new ReconcileProbeActor(counters, ...))`, and reads the counts directly off `counters` — no shared static fields remain. `ReconcileProbeActor`'s counter increments swap to `Interlocked.Increment` for the cross-thread CAS, and `SerializationProbeActor` retains its lock on a per-test `Gate`. All 85 `ZB.MOM.WW.ScadaBridge.DeploymentManager.Tests` continue to pass after the refactor.
|
||||
|
||||
**Description**
|
||||
|
||||
|
||||
Reference in New Issue
Block a user