# MQTT integration-test fixture — Eclipse Mosquitto (auth + TLS) + a JSON publisher # + an optional Sparkplug B edge-node simulator. # # PREREQUISITE — run this first, in this directory: # # MQTT_FIXTURE_PASSWORD='' ./gen-fixture-material.sh # # It writes ./secrets/ (password file + CA + server certificate/key), which is # gitignored and which both services below mount. Without it the broker will not # start: there is no anonymous fallback, by design. # # Bring up (on the shared Docker host — see infra/README.md §1): # # ssh dohertj2@10.100.0.35 'cd /opt/otopcua-mqtt \ # && MQTT_FIXTURE_USERNAME=otopcua MQTT_FIXTURE_PASSWORD= docker compose up -d' # # Endpoints: 10.100.0.35:8883 (TLS + auth) · 10.100.0.35:1883 (plaintext + auth, smoke) # # `mosquitto` + `publisher` carry no profile: they are always wanted together (a broker # with nothing publishing into it tests nothing) and they bind different ports so nothing # contends. `sparkplug-sim` DOES carry one (`--profile sparkplug`), because it is a # manual-driving aid rather than a test dependency — see its own banner. services: mosquitto: image: eclipse-mosquitto:2.0.22 container_name: otopcua-mosquitto restart: unless-stopped # Every lmxopcua fixture container carries this so the shared Docker host stays # discoverable with `docker ps --filter label=project=lmxopcua`. labels: project: lmxopcua ports: - "1883:1883" - "8883:8883" volumes: - ./mosquitto.conf:/mosquitto/config/mosquitto.conf:ro - ./secrets:/mosquitto/secrets:ro healthcheck: # Authenticated round-trip against the broker's own $SYS tree ($$ escapes the # compose variable syntax). Proves the password file loaded and the listener # accepts a real session — a bare port check would go green on a broker that # rejects every CONNECT. test: - CMD-SHELL - >- mosquitto_sub -h 127.0.0.1 -p 1883 -u "$$MQTT_FIXTURE_USERNAME" -P "$$MQTT_FIXTURE_PASSWORD" -t '$$SYS/broker/uptime' -C 1 -W 3 interval: 5s timeout: 8s retries: 12 start_period: 5s environment: # Consumed only by the healthcheck above; the broker itself reads the hashed # password file. Supplied by the operator's environment at `docker compose up` — # never defaulted here, so a missing value fails loudly instead of silently # provisioning a known-password broker. MQTT_FIXTURE_USERNAME: ${MQTT_FIXTURE_USERNAME:?set MQTT_FIXTURE_USERNAME (must match gen-fixture-material.sh)} MQTT_FIXTURE_PASSWORD: ${MQTT_FIXTURE_PASSWORD:?set MQTT_FIXTURE_PASSWORD (must match gen-fixture-material.sh)} publisher: image: eclipse-mosquitto:2.0.22 container_name: otopcua-mqtt-publisher restart: unless-stopped labels: project: lmxopcua depends_on: mosquitto: condition: service_healthy volumes: - ./publisher/publish.sh:/publish.sh:ro environment: BROKER_HOST: mosquitto BROKER_PORT: "1883" TOPIC_PREFIX: ${MQTT_FIXTURE_TOPIC_PREFIX:-otopcua/fixture} PUBLISH_INTERVAL: ${MQTT_FIXTURE_PUBLISH_INTERVAL:-2} MQTT_FIXTURE_USERNAME: ${MQTT_FIXTURE_USERNAME:?set MQTT_FIXTURE_USERNAME} MQTT_FIXTURE_PASSWORD: ${MQTT_FIXTURE_PASSWORD:?set MQTT_FIXTURE_PASSWORD} entrypoint: ["/bin/sh", "/publish.sh"] # --------------------------------------------------------------------------- # Sparkplug B edge-node simulator — the SAME `SparkplugEdgeNode` engine the live # tests drive, run standalone (see SparkplugSimulator/Program.cs). # # WHAT IT IS FOR, AND WHAT IT IS NOT FOR. It exists so an operator (and the Task-26 # AdminUI `/run` verification) has a live Sparkplug plant to point the address picker # at: NBIRTH/DBIRTH then N/DDATA every couple of seconds, honouring rebirth NCMDs. # `SparkplugLiveTests` does NOT use it and must not — the §3.6 matrix needs an edge # node it can command mid-test ("now reuse that alias", "now skip a seq", "now die"), # which a container cannot be. Hence the profile: the default `docker compose up` # leaves it out, and nothing in the test suite depends on it. # # PREREQUISITE: `./publish-simulator.sh` — the app directory below is build output, # gitignored and rsynced like the rest of this folder. Without it the container has # nothing to run. # --------------------------------------------------------------------------- sparkplug-sim: image: mcr.microsoft.com/dotnet/runtime:10.0 container_name: otopcua-sparkplug-sim profiles: ["sparkplug"] restart: unless-stopped labels: project: lmxopcua depends_on: mosquitto: condition: service_healthy volumes: # Framework-dependent publish output: portable IL, so it runs under the stock # runtime image whatever this file was rsynced from. - ./simulator/app:/app:ro # The fixture CA, so the simulator PINS the broker exactly as the driver does # rather than trusting anything. The cert's SAN list includes DNS:mosquitto (see # gen-fixture-material.sh), which is the host name used below. - ./secrets/ca.crt:/secrets/ca.crt:ro environment: MQTT_SIM_HOST: mosquitto MQTT_SIM_PORT: "8883" MQTT_SIM_USE_TLS: "true" MQTT_SIM_CA_CERT: /secrets/ca.crt MQTT_SIM_GROUP: ${MQTT_SIM_GROUP:-OtOpcUaSim} MQTT_SIM_NODES: ${MQTT_SIM_NODES:-EdgeA,EdgeB} MQTT_SIM_INTERVAL_SECONDS: ${MQTT_SIM_INTERVAL_SECONDS:-2} MQTT_FIXTURE_USERNAME: ${MQTT_FIXTURE_USERNAME:?set MQTT_FIXTURE_USERNAME} MQTT_FIXTURE_PASSWORD: ${MQTT_FIXTURE_PASSWORD:?set MQTT_FIXTURE_PASSWORD} command: ["dotnet", "/app/ZB.MOM.WW.OtOpcUa.Driver.Mqtt.SparkplugSimulator.dll"]