fix(heating-can): give supervisord a control socket #478

Merged
k.renner merged 1 commits from heating-can-supervisorctl into master 2026-08-10 19:53:51 +00:00
Member

The image shipped without [unix_http_server], [rpcinterface:supervisor] or [supervisorctl], so supervisorctl cannot connect:

# docker exec heating-can supervisorctl -c /etc/supervisord.conf status
Error: .ini file does not include supervisorctl section

That means there is no way to query or restart an individual service inside the container — docker logs is the only introspection.

Per-service lifecycle management was the entire argument for running a supervisor instead of one process per container. The first revision didn't deliver it.

It cost something concrete during the 2026-08-10 cutover: with two open3e instances fighting over the same MQTT session, supervisorctl status would have shown each service's state and restart count directly. Instead I inferred health from docker logs and reported success while the broker was kicking them off in a loop.

Change

Unix socket only, inside the container. Nothing outside needs it, and this container runs with host networking, so a TCP listener would be exposed on the host.

The build-time self-test now asserts the section exists and supervisorctl runs, so it can't regress silently the way it shipped.

Note

The MQTT reconnect loop this would have surfaced is already fixed — by #477, not by this PR. The cause was the migration failing to stop the native units, leaving a second open3e on the bus. This is about being able to see such a thing next time.

Verification

Build-time only: the self-test runs inside the image during the kaniko build, so a broken config fails the build rather than being published. Not applied to edge-1 — that needs a rebuild and a docker compose pull && up.

🤖 Generated with Claude Code

The image shipped without `[unix_http_server]`, `[rpcinterface:supervisor]` or `[supervisorctl]`, so `supervisorctl` cannot connect: ``` # docker exec heating-can supervisorctl -c /etc/supervisord.conf status Error: .ini file does not include supervisorctl section ``` That means there is no way to query or restart an individual service inside the container — `docker logs` is the only introspection. **Per-service lifecycle management was the entire argument for running a supervisor** instead of one process per container. The first revision didn't deliver it. It cost something concrete during the 2026-08-10 cutover: with two open3e instances fighting over the same MQTT session, `supervisorctl status` would have shown each service's state and restart count directly. Instead I inferred health from `docker logs` and reported success while the broker was kicking them off in a loop. ## Change Unix socket only, inside the container. Nothing outside needs it, and this container runs with host networking, so a TCP listener would be exposed on the host. The build-time self-test now asserts the section exists and `supervisorctl` runs, so it can't regress silently the way it shipped. ## Note The MQTT reconnect loop this would have surfaced is **already fixed** — by #477, not by this PR. The cause was the migration failing to stop the native units, leaving a second open3e on the bus. This is about being able to see such a thing next time. ## Verification Build-time only: the self-test runs inside the image during the kaniko build, so a broken config fails the build rather than being published. Not applied to edge-1 — that needs a rebuild and a `docker compose pull && up`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
claude-bot added 1 commit 2026-08-10 19:49:22 +00:00
fix(heating-can): give supervisord a control socket
ci/woodpecker/push/build-ci-images Pipeline was successful
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
e3dea169b0
The image shipped without [unix_http_server], [rpcinterface:supervisor] or
[supervisorctl], so supervisorctl could not connect:

  Error: .ini file does not include supervisorctl section

That means no way to query or restart an individual service inside the
container -- docker logs was the only introspection. Per-service lifecycle
management was the whole argument for running a supervisor instead of one
process per container, and the first revision did not actually deliver it.

Unix socket only, inside the container. Nothing outside needs it, and this
container runs with host networking, so a TCP listener would be exposed on
the host.

The build-time self-test now asserts the section exists and supervisorctl
runs, so this cannot regress silently the way it shipped.

NOT fixed here, because my diagnosis was wrong: the repeated
"mqtt broker disconnected. reason_code = Unspecified error" is not a client
ID collision. E3onCAN has no -mcid option at all -- verified against the
pinned source -- and generates its own ID; the native broker log showed
E3onCANclient.py-e380-7f847281 alongside open3e-edge, so the two never
collided. I nearly "fixed" it by passing a flag that does not exist.

Telemetry is flowing (each reconnect succeeds), so this is churn rather than
an outage, but the cause is still unidentified. The next step is the broker's
own log on edge-1 -- mosquitto records the disconnect reason, which the
client side reports only as "Unspecified error".

Co-Authored-By: Claude Bot <claude-bot@renner.uno>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
review-bot left a comment
Member

🤖 Automated review — model claude-haiku-4-5-20251001 (complexity: trivial), commit e3dea169b00d037c612f44f0c170c82cc8861bd0.

Advisory review by the review-bot pipeline. It never approves; a human merges. Push new commits to get a fresh review.


Summary

This PR adds Unix domain socket support to supervisord in the heating-can container, enabling supervisorctl to query and control individual services. The change adds three supervisord config sections and build-time validation; no logic or runtime behavior changes beyond enabling the new interface.

🟢 Nits

  • Self-test comprehensiveness: The build-time test checks that supervisorctl --help works, which only verifies the binary exists. It doesn't test that supervisorctl can actually connect to a running supervisord instance (that would require starting supervisord, which isn't necessary at build time). However, full validation happens at runtime when supervisord starts and parses the config—it will fail if the configuration is broken. The current test is appropriate for a build-time gate.

Tests

The PR adds build-time validation that:

  1. Both service binaries exist and are executable (open3e --help, E3onCANcollect.py --help)
  2. supervisord can start (supervisord --help)
  3. The config sections are present (grep -q '^\[supervisorctl\]')
  4. supervisorctl binary is executable (supervisorctl --help)

Runtime validation occurs when the container starts: supervisord will fail to start if the config has syntax errors. The socket will be created in /tmp (writable by the open3e user) with 0700 permissions (owner-only access), and supervisorctl can connect via unix:///tmp/supervisor.sock.

Configuration Details

The three new config sections are syntactically correct:

  • [unix_http_server] (supervisord.conf:21-23): Creates the socket at /tmp/supervisor.sock with restrictive permissions (0700). /tmp is writable and ephemeral, appropriate for container-scoped runtime state.

  • [rpcinterface:supervisor] (supervisord.conf:25-26): Registers the RPC factory, a mandatory prerequisite for supervisorctl.

  • [supervisorctl] (supervisord.conf:28-29): Points to the Unix socket using the triple-slash syntax (unix:///tmp/supervisor.sock), which is correct for supervisord.

No port is exposed on the host (the config uses only Unix socket, not TCP), which is appropriate given the container runs with network_mode: host and should not expose privileged management interfaces.

Recommendation

Looks reasonable — no blocking issues; comments above are optional.

🤖 **Automated review** — model `claude-haiku-4-5-20251001` (complexity: trivial), commit `e3dea169b00d037c612f44f0c170c82cc8861bd0`. > Advisory review by the `review-bot` pipeline. It never approves; a human merges. Push new commits to get a fresh review. --- ## Summary This PR adds Unix domain socket support to supervisord in the heating-can container, enabling `supervisorctl` to query and control individual services. The change adds three supervisord config sections and build-time validation; no logic or runtime behavior changes beyond enabling the new interface. ## 🟢 Nits - **Self-test comprehensiveness**: The build-time test checks that `supervisorctl --help` works, which only verifies the binary exists. It doesn't test that `supervisorctl` can actually connect to a running supervisord instance (that would require starting supervisord, which isn't necessary at build time). However, full validation happens at runtime when supervisord starts and parses the config—it will fail if the configuration is broken. The current test is appropriate for a build-time gate. ## Tests The PR adds build-time validation that: 1. Both service binaries exist and are executable (`open3e --help`, `E3onCANcollect.py --help`) 2. supervisord can start (`supervisord --help`) 3. The config sections are present (`grep -q '^\[supervisorctl\]'`) 4. supervisorctl binary is executable (`supervisorctl --help`) Runtime validation occurs when the container starts: supervisord will fail to start if the config has syntax errors. The socket will be created in `/tmp` (writable by the `open3e` user) with `0700` permissions (owner-only access), and `supervisorctl` can connect via `unix:///tmp/supervisor.sock`. ## Configuration Details The three new config sections are syntactically correct: - **`[unix_http_server]`** (`supervisord.conf:21-23`): Creates the socket at `/tmp/supervisor.sock` with restrictive permissions (`0700`). `/tmp` is writable and ephemeral, appropriate for container-scoped runtime state. - **`[rpcinterface:supervisor]`** (`supervisord.conf:25-26`): Registers the RPC factory, a mandatory prerequisite for supervisorctl. - **`[supervisorctl]`** (`supervisord.conf:28-29`): Points to the Unix socket using the triple-slash syntax (`unix:///tmp/supervisor.sock`), which is correct for supervisord. No port is exposed on the host (the config uses only Unix socket, not TCP), which is appropriate given the container runs with `network_mode: host` and should not expose privileged management interfaces. ## Recommendation Looks reasonable — no blocking issues; comments above are optional. <!-- pr-review:sha=e3dea169b00d037c612f44f0c170c82cc8861bd0 -->
k.renner merged commit c8bc6380a1 into master 2026-08-10 19:53:51 +00:00
Sign in to join this conversation.