Skip to content

feat(doctor): report whether the running proxy has the docker socket mounted - #101

Merged
mhenrixon merged 1 commit into
dashfrom
issue-98-doctor-docker-socket
Aug 3, 2026
Merged

mhenrixon merged 1 commit into
dashfrom
issue-98-doctor-docker-socket

Conversation

@mhenrixon

Copy link
Copy Markdown
Collaborator

Summary

kamal doctor now inspects what the running kamal-proxy was actually booted with, not just what the config says. The config-time sleep/docker_socket pairing check can't see a proxy booted before the socket was added (sleeping services never wake — one hung request) or after it was removed (root-equivalent host access lingers).

  • Kamal::Commands::Proxy#mount_destinations — docker inspect, one mount destination per line
  • New per-host doctor row "Proxy docker socket":
    • OK — configured socket is mounted; or proxy not running (arrives on boot); or nothing configured/mounted
    • FAIL — sleep: configured somewhere and the running proxy lacks the mount → kamal proxy reboot
    • WARN — socket configured without any sleep: and not mounted (drift, nothing hangs yet); or a docker.sock mount the config no longer asks for; or the inspection itself failed (the doctor never crashes)
  • Latent bug fixed in path: the doctor's catch-all rescued SSHKit::Runner::MultipleExecuteError, which doesn't exist in sshkit 1.25 — a host whose checks failed wholesale became a NameError instead of a recorded SSH failure. The new test was the first thing to trip it.

Closes #98

Test plan

  • All seven outcomes covered in test/cli/doctor_test.rb (two new fixtures: sleep+socket, socket-only)
  • mount_destinations command shape in test/commands/proxy_test.rb
  • Pre-existing doctor tests green (the shared version stub now defaults mounts to empty)
  • bundle exec rubocop --parallel clean; full unit suite green
  • CI

Deviations & judgment calls

  • Severity is graded by consequence: a missing mount is only a FAIL when some role actually has sleep: configured — docker_socket alone is a legal one-key convenience, and a missing mount then breaks nothing yet, so it warns as drift instead. The issue didn't specify; erring toward "FAIL means something is broken right now".
  • The stray-socket detection is a heuristic (docker.sock$ on mount destinations) — the gem always mounts the socket at its host path, so this catches every gem-made mount; an operator-mounted socket via run.options with an exotic name would not warn. Deliberate: better to under-warn than false-alarm on custom mounts.
  • The sshkit constant fix is bundled rather than split out — it's two lines, the new check is what exposed it, and shipping the check without it would leave the doctor able to crash on the exact hosts it should be diagnosing.
  • stub_proxy_version now also stubs mounts to empty — every running-proxy doctor test hits the new inspection, and per-test boilerplate in eight places was worse than one documented default in the shared helper.
  • Built in a worktree from latest dash per request (the main mirror is never a valid root — "latest main" for this fork means dash).

…mounted

## Summary

The config-time sleep/docker_socket pairing check covers the *current*
config, but the running container keeps whatever it was booted with: a proxy
from before run.docker_socket was added silently lacks the mount (the
failure mode is one hung request when a sleeping service never wakes), and
one from before it was removed keeps root-equivalent host access.

kamal doctor now inspects the running kamal-proxy's mount destinations per
host (Commands::Proxy#mount_destinations) and reports a "Proxy docker
socket" row: OK when the configured socket is mounted (or arrives on boot,
or nothing is configured), FAIL with a `kamal proxy reboot` remedy when
sleep is configured and the mount is missing, WARN when the socket is
configured without sleep (drift, nothing hangs yet) or when a docker.sock
mount lingers that the config no longer asks for.

Also fixes a latent bug the new test surfaced: the doctor's catch-all named
SSHKit::Runner::MultipleExecuteError, which does not exist in sshkit 1.25 -
a host whose checks failed wholesale became a NameError instead of a
recorded SSH failure, the opposite of the doctor's never-crash contract.

## Test Coverage

- mounted / missing-with-sleep (fail) / missing-without-sleep (warn) /
  stray-unconfigured (warn) / boot-time (ok) / quiet-when-absent (ok)
- mount_destinations command shape

## Verification

- [x] bundle exec rubocop --parallel passes
- [x] unit suite passes

Closes #98
@mhenrixon mhenrixon self-assigned this Aug 3, 2026
@mhenrixon mhenrixon added enhancement New feature or request dx Developer experience — agent commands and rules, generators, local tooling labels Aug 3, 2026
@mhenrixon
mhenrixon merged commit 9cdc204 into dash Aug 3, 2026
17 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dx Developer experience — agent commands and rules, generators, local tooling enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

kamal doctor: report whether the running proxy was booted with a docker socket

1 participant