# opc-plc — OPC UA PLC simulator from Microsoft Industrial IoT. # https://github.com/Azure-Samples/iot-edge-opc-plc # # Why pinned: MCR tags only go forward; keeping the suite reproducible means # we test against a known feature surface. Bump deliberately alongside a # driver-side change that needs the newer image. services: opc-plc: image: mcr.microsoft.com/iotedge/opc-plc:2.14.10 container_name: otopcua-opc-plc restart: "no" ports: - "50000:50000" command: # --pn: Bind port 50000 (opc-plc default; matches fixture default) # --ut: Advertise an Unsecured transport endpoint (SecurityPolicy=None). # Tests that need signed/encrypted endpoints pick those off the # negotiated endpoint list separately — opc-plc always advertises # the secure policies even with --ut on. # --aa: Auto-accept client certs. Tests wouldn't otherwise survive the # first contact because opc-plc's cert trust store lives inside # the container + resets each spin-up. # --daa: Disable anonymous auth — forces the driver to go through the # Anonymous user-token policy negotiation rather than opc-plc's # "no auth required" short-circuit. Would flip to username/cert # if we needed that coverage. # Commented out for first-pass smoke; flip on when the cert-auth # and username-auth smoke tests land. # --alm: Turn on alarm simulation (TripAlarm / ExclusiveDeviation / # NonExclusiveLevel / DialogCondition). Closes the IAlarmSource # gap the OpcUaClient-Test-Fixture doc calls out. - "--pn=50000" - "--ut" - "--aa" - "--alm" # - "--daa" healthcheck: # opc-plc doesn't expose an HTTP health endpoint by default; use a TCP # probe via a shell the base image ships with. The fixture does its own # TCP probe but healthcheck surfaces status in `docker ps` for humans. test: ["CMD-SHELL", "netstat -an | grep -q ':50000.*LISTEN' || exit 1"] interval: 5s timeout: 2s retries: 10 start_period: 10s # opc-plc-rc — reverse-connect (server-initiated) variant. The simulator # acts as the OPC UA server but, unlike the regular service above, it dials # OUT to the client's listener URL instead of accepting an inbound dial. # Mirrors the OT-DMZ topology where the plant firewall only permits # outbound traffic from the upstream server. The driver-side test fixture # binds opc.tcp://0.0.0.0:4844 and waits for opc-plc-rc to ReverseHello. # # `--rc` is opc-plc's reverse-connect knob — value is the client URL the # simulator should dial when it has no inbound connection. host.docker.internal # is the docker-for-windows / docker-for-mac shorthand for the host's IP; # on Linux hosts use --add-host=host.docker.internal:host-gateway. opc-plc-rc: image: mcr.microsoft.com/iotedge/opc-plc:2.14.10 container_name: otopcua-opc-plc-rc restart: "no" extra_hosts: - "host.docker.internal:host-gateway" command: # --pn=50001: bind on a different port so this container can run alongside # the dial-mode simulator above. Reverse-connect doesn't require # the client to know this port (the simulator is the dialer) # but it still has to bind one for any incoming admin queries. # --rc: reverse-connect target — the simulator dials this URL and # presents its OPC UA endpoint over the inbound socket. Must # point at the test runner's listener. # --ut/--aa/--alm: same flags as the regular profile. - "--pn=50001" - "--rc=opc.tcp://host.docker.internal:4844" - "--ut" - "--aa" - "--alm" healthcheck: test: ["CMD-SHELL", "netstat -an | grep -q ':50001.*LISTEN' || exit 1"] interval: 5s timeout: 2s retries: 10 start_period: 10s