diff --git a/docker/build.sh b/docker/build.sh index 52957617..a8919b5b 100755 --- a/docker/build.sh +++ b/docker/build.sh @@ -23,10 +23,12 @@ fi # Build from repo root (so COPY paths in Dockerfile resolve correctly) echo "Building scadabridge:latest image..." +# Note: ${NUGET_ARGS[@]+...} guards against the "unbound variable" error that +# set -u raises on bash 3.2 (macOS default) when expanding an empty array. docker build \ -t scadabridge:latest \ -f "$SCRIPT_DIR/Dockerfile" \ - "${NUGET_ARGS[@]}" \ + ${NUGET_ARGS[@]+"${NUGET_ARGS[@]}"} \ "$REPO_ROOT" echo "Build complete: scadabridge:latest" diff --git a/docker/seed-sites.sh b/docker/seed-sites.sh index 7c2c9d89..2139b347 100755 --- a/docker/seed-sites.sh +++ b/docker/seed-sites.sh @@ -63,6 +63,35 @@ $CLI $URL $AUTH notification create \ --emails "engineer@company.com" \ || echo " (Engineering Alerts may already exist)" +echo "" +echo "Creating MxGateway data connections (shared gateway) on each site..." +# A shared MxGateway data connection pointing at the MxAccess Gateway. Data +# connections are site-scoped (the DCL runs on site clusters only — central +# nodes do not host data connections), so one is created per site. The same +# gateway endpoint + API key is reused across all sites. +# +# Config is the typed MxGatewayEndpointConfig JSON (camelCase keys), matching +# MxGatewayEndpointConfigSerializer. +MXGW_ENDPOINT="http://10.100.0.48:5120" +MXGW_APIKEY="mxgw_scadabridgeshared_O193yRm28zftUAcL-HPkTjAuE-vPz86MUtNLFWpcbOY" +MXGW_CONFIG="{\"endpoint\":\"${MXGW_ENDPOINT}\",\"apiKey\":\"${MXGW_APIKEY}\",\"clientName\":\"\",\"writeUserId\":0,\"useTls\":false,\"caFile\":\"\",\"serverName\":\"\",\"readTimeoutMs\":5000}" + +for ident in site-a site-b site-c; do + SITE_ID=$($CLI $URL $AUTH --format json site list \ + | python3 -c "import sys,json; print(next((s['id'] for s in json.load(sys.stdin) if s.get('siteIdentifier')=='$ident'), ''))" 2>/dev/null) + if [ -z "$SITE_ID" ]; then + echo " ($ident not found — skipping MxGateway connection)" + continue + fi + echo " $ident (id=$SITE_ID): creating 'MxGateway Shared'..." + $CLI $URL $AUTH data-connection create \ + --site-id "$SITE_ID" \ + --name "MxGateway Shared" \ + --protocol "MxGateway" \ + --primary-config "$MXGW_CONFIG" \ + || echo " (MxGateway connection may already exist on $ident)" +done + echo "" echo "Seeding LDAP group mappings (Design + Deployment)..." # SecurityConfiguration.HasData declares 4 mappings but the InitialSchema @@ -82,9 +111,18 @@ IF NOT EXISTS (SELECT 1 FROM LdapGroupMappings WHERE Id = 4) SET IDENTITY_INSERT LdapGroupMappings OFF; " +echo "" +echo "Deploying artifacts to all sites (pushes data connections so the sites" +echo "establish them — the MxGateway DataConnectionActor connects eagerly)..." +$CLI $URL $AUTH deploy artifacts \ +|| echo " (artifact deploy reported an issue — check 'deploy status')" + echo "" echo "=== Site seeding complete ===" echo "" echo "Verify with: $CLI $URL $AUTH site list" +echo "Verify connections: $CLI $URL $AUTH data-connection list" +echo "Verify MxGateway is live (per site container):" +echo " docker logs scadabridge-site-a-a 2>&1 | grep -i mxgateway" echo "Multi-role test user has Admin + Design + Deployment." echo "Sign out and back in to refresh session role claims." diff --git a/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Actors/DeploymentManagerActor.cs b/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Actors/DeploymentManagerActor.cs index d53212e4..55ca8163 100644 --- a/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Actors/DeploymentManagerActor.cs +++ b/src/ZB.MOM.WW.ScadaBridge.SiteRuntime/Actors/DeploymentManagerActor.cs @@ -1002,6 +1002,11 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers "Deploying system artifacts, deploymentId={DeploymentId}", command.DeploymentId); var sender = Sender; + // Capture Self before entering Task.Run: the Self/Sender/Context properties + // are backed by the ambient ActorCell, which is null on a thread-pool thread, + // so reading Self *inside* the lambda throws "no active ActorContext". The + // data-connections branch below dispatches via this captured ref. + var self = Self; Task.Run(async () => { @@ -1071,7 +1076,7 @@ public class DeploymentManagerActor : ReceiveActor, IWithTimers // helper's hash cache skips unchanged definitions, so // the push is idempotent for re-deploys of the same // artifact bundle. - Self.Tell(new ApplyArtifactDataConnectionsToDcl(command.DataConnections)); + self.Tell(new ApplyArtifactDataConnectionsToDcl(command.DataConnections)); } // Store SMTP configurations