feat(deploy,runtime): wire mxaccessgw connection — endpoint, key, seed row
Some checks failed
v2-ci / build (push) Failing after 37s
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been skipped
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.Host.IntegrationTests) (push) Has been skipped
v2-ci / integration (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.IntegrationTests) (push) Has been skipped

User confirmed the mxaccessgw client (Galaxy driver) doesn't need Windows
— only the gateway worker has that constraint. This wires the Galaxy
driver into the docker-dev fleet:

- docker-compose.yml: GALAXY_MXGW_API_KEY env var on every host service
  (admin nodes harmlessly ignore it; driver-role nodes pick it up when
  the seeded DriverInstance resolves ApiKeySecretRef=env:GALAXY_MXGW_API_KEY).
  Default value matches the key the operator provided; override via shell
  env (GALAXY_MXGW_API_KEY=... docker compose up -d) to rotate without
  editing compose.
- seed-clusters.sql: now creates a SystemPlatform Namespace
  (MAIN-galaxy, urn:zb:docker-dev:galaxy) plus a GalaxyMxGateway
  DriverInstance (MAIN-galaxy-mxgw) in the MAIN cluster pointing at
  http://10.100.0.48:5120 with UseTls=false. Idempotent via IF NOT EXISTS.
- DriverInstanceActor.ShouldStub: clarified the doc comment — only the
  legacy "Galaxy" type name and "Historian.Wonderware" are Windows-only;
  the v2 "GalaxyMxGateway" driver is .NET 10 cross-platform (gRPC to an
  external gateway) and is NOT stubbed.
- README: documents the final operator step — sign in, click "Deploy
  current configuration" on /deployments to materialise the seeded
  Galaxy driver into a running gRPC connection. Raw DriverInstance rows
  don't spawn drivers on their own; the v2 lifecycle requires a sealed
  Deployment first.
This commit is contained in:
Joseph Doherty
2026-05-26 14:58:02 -04:00
parent 6884de9774
commit 60beb9128e
4 changed files with 78 additions and 3 deletions

View File

@@ -60,6 +60,12 @@ The SQL lives at `seed/seed-clusters.sql`; the wait-and-apply wrapper lives at `
docker compose -f docker-dev/docker-compose.yml run --rm cluster-seed docker compose -f docker-dev/docker-compose.yml run --rm cluster-seed
``` ```
### Galaxy / MxAccess gateway
The seed also pre-creates a `SystemPlatform` Namespace + a `GalaxyMxGateway` DriverInstance in the MAIN cluster pointing at `http://10.100.0.48:5120`. The API key is resolved from the `GALAXY_MXGW_API_KEY` env var set on every driver-role container in compose; override via `GALAXY_MXGW_API_KEY=... docker compose up -d` to swap keys without editing the compose file.
The DriverHost actor doesn't spawn drivers from raw DriverInstance rows on its own — the v2 deploy lifecycle requires a *sealed Deployment* before drivers materialise. After first bring-up, sign in to the Admin UI and click **Deploy current configuration** on `/deployments` to compose the seeded rows into an artifact and dispatch it. The Galaxy driver instance will start its gRPC connection to the gateway on the next deploy ack.
## Bring up ## Bring up
```bash ```bash

View File

@@ -98,6 +98,7 @@ services:
Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Issuer: "otopcua-dev"
Security__Jwt__Audience: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev"
Authentication__Ldap__DevStubMode: "true" Authentication__Ldap__DevStubMode: "true"
GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua__UY_NKlBl3vWuZt8HD7usfZsU76eibMKB6CufwzabUI}"
admin-b: admin-b:
<<: *otopcua-host <<: *otopcua-host
@@ -114,6 +115,7 @@ services:
Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Issuer: "otopcua-dev"
Security__Jwt__Audience: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev"
Authentication__Ldap__DevStubMode: "true" Authentication__Ldap__DevStubMode: "true"
GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua__UY_NKlBl3vWuZt8HD7usfZsU76eibMKB6CufwzabUI}"
driver-a: driver-a:
<<: *otopcua-host <<: *otopcua-host
@@ -125,6 +127,9 @@ services:
Cluster__PublicHostname: "driver-a" Cluster__PublicHostname: "driver-a"
Cluster__SeedNodes__0: "akka.tcp://otopcua@admin-a:4053" Cluster__SeedNodes__0: "akka.tcp://otopcua@admin-a:4053"
Cluster__Roles__0: "driver" Cluster__Roles__0: "driver"
# Resolved at runtime by GalaxyDriver.ResolveApiKey when a DriverInstance's
# Gateway.ApiKeySecretRef = "env:GALAXY_MXGW_API_KEY".
GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua__UY_NKlBl3vWuZt8HD7usfZsU76eibMKB6CufwzabUI}"
ports: ports:
- "4840:4840" - "4840:4840"
@@ -138,6 +143,7 @@ services:
Cluster__PublicHostname: "driver-b" Cluster__PublicHostname: "driver-b"
Cluster__SeedNodes__0: "akka.tcp://otopcua@admin-a:4053" Cluster__SeedNodes__0: "akka.tcp://otopcua@admin-a:4053"
Cluster__Roles__0: "driver" Cluster__Roles__0: "driver"
GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua__UY_NKlBl3vWuZt8HD7usfZsU76eibMKB6CufwzabUI}"
ports: ports:
- "4841:4840" - "4841:4840"
@@ -162,6 +168,7 @@ services:
Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Issuer: "otopcua-dev"
Security__Jwt__Audience: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev"
Authentication__Ldap__DevStubMode: "true" Authentication__Ldap__DevStubMode: "true"
GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua__UY_NKlBl3vWuZt8HD7usfZsU76eibMKB6CufwzabUI}"
ports: ports:
- "4842:4840" - "4842:4840"
@@ -184,6 +191,7 @@ services:
Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Issuer: "otopcua-dev"
Security__Jwt__Audience: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev"
Authentication__Ldap__DevStubMode: "true" Authentication__Ldap__DevStubMode: "true"
GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua__UY_NKlBl3vWuZt8HD7usfZsU76eibMKB6CufwzabUI}"
ports: ports:
- "4843:4840" - "4843:4840"
@@ -205,6 +213,7 @@ services:
Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Issuer: "otopcua-dev"
Security__Jwt__Audience: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev"
Authentication__Ldap__DevStubMode: "true" Authentication__Ldap__DevStubMode: "true"
GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua__UY_NKlBl3vWuZt8HD7usfZsU76eibMKB6CufwzabUI}"
ports: ports:
- "4844:4840" - "4844:4840"
@@ -227,6 +236,7 @@ services:
Security__Jwt__Issuer: "otopcua-dev" Security__Jwt__Issuer: "otopcua-dev"
Security__Jwt__Audience: "otopcua-dev" Security__Jwt__Audience: "otopcua-dev"
Authentication__Ldap__DevStubMode: "true" Authentication__Ldap__DevStubMode: "true"
GALAXY_MXGW_API_KEY: "${GALAXY_MXGW_API_KEY:-mxgw_otopcua__UY_NKlBl3vWuZt8HD7usfZsU76eibMKB6CufwzabUI}"
ports: ports:
- "4845:4840" - "4845:4840"

View File

@@ -95,6 +95,56 @@ IF NOT EXISTS (SELECT 1 FROM dbo.ClusterNode WHERE NodeId = 'site-b-2')
(NodeId, ClusterId, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy) (NodeId, ClusterId, Host, OpcUaPort, DashboardPort, ApplicationUri, ServiceLevelBase, Enabled, CreatedBy)
VALUES ('site-b-2', 'SITE-B', 'site-b-2', 4840, 8081, 'urn:OtOpcUa:site-b-2', 150, 1, 'docker-dev-seed'); VALUES ('site-b-2', 'SITE-B', 'site-b-2', 4840, 8081, 'urn:OtOpcUa:site-b-2', 150, 1, 'docker-dev-seed');
------------------------------------------------------------------------------
-- Galaxy MxAccess gateway — MAIN cluster
--
-- Namespace.Kind=SystemPlatform is required for Galaxy/MXAccess data per
-- decision #107; raw equipment drivers use Equipment. DriverInstance points
-- at the external mxaccessgw process. The driver code lives in this repo
-- (.NET 10, cross-platform); only the gateway worker needs Windows.
--
-- ApiKeySecretRef = env:GALAXY_MXGW_API_KEY → resolved at runtime by
-- GalaxyDriver.ResolveApiKey. The env var is set on every driver-role
-- container in docker-compose.yml.
------------------------------------------------------------------------------
IF NOT EXISTS (SELECT 1 FROM dbo.Namespace WHERE NamespaceId = 'MAIN-galaxy')
INSERT INTO dbo.Namespace
(NamespaceRowId, NamespaceId, ClusterId, Kind, NamespaceUri, Enabled, Notes)
VALUES
(NEWID(), 'MAIN-galaxy', 'MAIN', 'SystemPlatform',
'urn:zb:docker-dev:galaxy', 1,
'docker-dev seed — Galaxy / MXAccess namespace served by the MAIN cluster.');
IF NOT EXISTS (SELECT 1 FROM dbo.DriverInstance WHERE DriverInstanceId = 'MAIN-galaxy-mxgw')
INSERT INTO dbo.DriverInstance
(DriverInstanceRowId, DriverInstanceId, ClusterId, NamespaceId, Name, DriverType, Enabled, DriverConfig)
VALUES
(NEWID(), 'MAIN-galaxy-mxgw', 'MAIN', 'MAIN-galaxy',
'MxAccess gateway (10.100.0.48:5120)', 'GalaxyMxGateway', 1,
N'{
"Gateway": {
"Endpoint": "http://10.100.0.48:5120",
"ApiKeySecretRef": "env:GALAXY_MXGW_API_KEY",
"UseTls": false,
"ConnectTimeoutSeconds": 10,
"DefaultCallTimeoutSeconds": 30
},
"MxAccess": {
"ClientName": "OtOpcUa-MAIN-docker-dev",
"PublishingIntervalMs": 1000
},
"Repository": {
"DiscoverPageSize": 5000,
"WatchDeployEvents": true
},
"Reconnect": {
"InitialBackoffMs": 500,
"MaxBackoffMs": 30000,
"ReplayOnSessionLost": true
}
}');
COMMIT TRANSACTION; COMMIT TRANSACTION;
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
@@ -104,3 +154,6 @@ COMMIT TRANSACTION;
SELECT ClusterId, Name, NodeCount, RedundancyMode FROM dbo.ServerCluster ORDER BY ClusterId; SELECT ClusterId, Name, NodeCount, RedundancyMode FROM dbo.ServerCluster ORDER BY ClusterId;
SELECT NodeId, ClusterId, Host, OpcUaPort, ApplicationUri, ServiceLevelBase SELECT NodeId, ClusterId, Host, OpcUaPort, ApplicationUri, ServiceLevelBase
FROM dbo.ClusterNode ORDER BY ClusterId, NodeId; FROM dbo.ClusterNode ORDER BY ClusterId, NodeId;
SELECT NamespaceId, ClusterId, Kind, NamespaceUri FROM dbo.Namespace ORDER BY ClusterId, NamespaceId;
SELECT DriverInstanceId, ClusterId, DriverType, NamespaceId, Name
FROM dbo.DriverInstance ORDER BY ClusterId, DriverInstanceId;

View File

@@ -66,9 +66,15 @@ public sealed class DriverInstanceActor : ReceiveActor, IWithTimers
/// <summary> /// <summary>
/// Returns true when the driver should boot in DEV-STUB mode based on host platform and /// Returns true when the driver should boot in DEV-STUB mode based on host platform and
/// configured roles. Mirrors plan §Task 55: Windows-only driver types (Galaxy, Wonderware /// configured roles. Only the v1 in-process types stay Windows-only:
/// Historian) are stubbed when running on non-Windows OR when the host carries the /// <list type="bullet">
/// <c>dev</c> role. /// <item><c>"Galaxy"</c> — legacy MXAccess COM proxy (retired in PR 7.2; gated for any
/// leftover DriverInstance rows that still reference the old type name).</item>
/// <item><c>"Historian.Wonderware"</c> — Wonderware Historian sidecar over Windows-only
/// named pipes.</item>
/// </list>
/// The v2 <c>"GalaxyMxGateway"</c> driver talks gRPC to an external mxaccessgw process,
/// so it runs on any platform .NET 10 supports — Linux containers included. Not stubbed.
/// </summary> /// </summary>
public static bool ShouldStub(string driverType, IEnumerable<string> roles) public static bool ShouldStub(string driverType, IEnumerable<string> roles)
{ {