feat(docker): add Docker client integration #642

Merged
fuzzy merged 1 commit from feat/docker-client into main 2026-08-13 01:13:46 +00:00
Owner

What

Add Docker client integration for standalone mode (Phase 1 of the Docker roadmap). New internal/docker package wrapping the Docker SDK client over the Unix socket, with centralized connection-error types and startup socket-accessibility verification.

  • Docker SDK dependency (#328)github.com/docker/docker v28.5.2, vendored via go mod vendor.
  • Client wrapper (#329)docker.NewClient(socketPath) (empty → /var/run/docker.sock), Ping(ctx) with API-version negotiation, SocketPath(), APIVersion().
  • Connection errors (#330)errors package gains ErrDockerUnavailable, ErrDockerSocketMissing, ErrDockerSocketType, ErrDockerPermissionDenied and wrap helpers with actionable messages (e.g. "add your user to the docker group").
  • Startup verification (#331)Client.CheckAccess(ctx) statically verifies the socket exists, is a Unix socket, is readable/writable, then pings the daemon; fails fast with the typed errors above.

Per the standalone deployment model, no cmd/thwap-pagesd wiring yet — deployer selection (--deployer, #336/#337) is a separate task.

Why

Phase 1 requires the Docker client integration before the deployer abstraction (#337) and configuration extensions (#343) can build on it. This is the standalone-mode foundation: transport, error semantics, and startup checks.

Testing

  • Unit tests: internal/docker table-driven tests over a fake unix-socket /_ping HTTP server (no real daemon needed)
  • Error-path tests: missing socket, non-socket path, unreachable daemon
  • go vet ./... clean
  • go test -race -count=1 ./... full suite passes
  • Pre-commit hooks pass (golangci-lint, go fmt, go mod tidy, go test, gitleaks)

Breaking Changes

None. Additive package + new error types.

Notes

The dependency is tagged +incompatible upstream (no root go.mod); the SDK's client package builds and tests cleanly. go-ansiterm is a transitive dep of the Docker SDK via moby/term.

Closes #328
Closes #329
Closes #330
Closes #331
Closes #332

## What Add Docker client integration for standalone mode (Phase 1 of the Docker roadmap). New `internal/docker` package wrapping the Docker SDK client over the Unix socket, with centralized connection-error types and startup socket-accessibility verification. - **Docker SDK dependency (#328)** — `github.com/docker/docker v28.5.2`, vendored via `go mod vendor`. - **Client wrapper (#329)** — `docker.NewClient(socketPath)` (empty → `/var/run/docker.sock`), `Ping(ctx)` with API-version negotiation, `SocketPath()`, `APIVersion()`. - **Connection errors (#330)** — `errors` package gains `ErrDockerUnavailable`, `ErrDockerSocketMissing`, `ErrDockerSocketType`, `ErrDockerPermissionDenied` and wrap helpers with actionable messages (e.g. "add your user to the docker group"). - **Startup verification (#331)** — `Client.CheckAccess(ctx)` statically verifies the socket exists, is a Unix socket, is readable/writable, then pings the daemon; fails fast with the typed errors above. Per the standalone deployment model, no `cmd/thwap-pagesd` wiring yet — deployer selection (`--deployer`, #336/#337) is a separate task. ## Why Phase 1 requires the Docker client integration before the deployer abstraction (#337) and configuration extensions (#343) can build on it. This is the standalone-mode foundation: transport, error semantics, and startup checks. ## Testing - [x] Unit tests: `internal/docker` table-driven tests over a fake unix-socket `/_ping` HTTP server (no real daemon needed) - [x] Error-path tests: missing socket, non-socket path, unreachable daemon - [x] `go vet ./...` clean - [x] `go test -race -count=1 ./...` full suite passes - [x] Pre-commit hooks pass (golangci-lint, go fmt, go mod tidy, go test, gitleaks) ## Breaking Changes None. Additive package + new error types. ## Notes The dependency is tagged `+incompatible` upstream (no root `go.mod`); the SDK's `client` package builds and tests cleanly. `go-ansiterm` is a transitive dep of the Docker SDK via `moby/term`. Closes #328 Closes #329 Closes #330 Closes #331 Closes #332
feat(docker): add Docker client integration
All checks were successful
CI / docker (pull_request) Successful in 4m27s
CI / lint (pull_request) Successful in 2m18s
CI / build (pull_request) Successful in 8m24s
CI / test (pull_request) Successful in 19m58s
CI / build (push) Successful in 10m57s
CI / lint (push) Successful in 15m58s
CI / docker (push) Successful in 30m8s
CI / test (push) Successful in 30m30s
5eeeb309ad
Add the internal/docker package wrapping the Docker SDK client over the
Unix socket, with centralized connection errors and startup socket
accessibility verification. Vendored dependencies via go mod vendor.

- internal/docker: NewClient (default /var/run/docker.sock), Ping with
  API version negotiation, CheckAccess combining socket verification and
  ping
- errors: ErrDockerUnavailable, ErrDockerSocketMissing, ErrDockerSocketType,
  ErrDockerPermissionDenied plus wrap helpers
- table-driven tests over a fake unix-socket /_ping server
- DOCKER-ROADMAP.md: mark #332 and subtasks complete

Closes #328
Closes #329
Closes #330
Closes #331
Closes #332
fuzzy scheduled this pull request to auto merge when all checks succeed 2026-08-13 00:53:27 +00:00
the.auditor left a comment

Solid implementation. Code correctness verified against the vendored SDK: Ping hits the unversioned /_ping (HEAD→GET fallback), ping.APIVersion comes from the Api-Version header, so the fake-server tests are valid and APIVersion() returns the daemon's advertised version. Error chains (errors.Is) work with the double-%w wraps on go 1.26. Referenced issues #328–#332 all exist and are closed; roadmap checkboxes match. Vendoring follows the repo's existing pattern.

Suggestions

  1. internal/docker/docker.go (verifySocket) — access check uses R_OK|W_OK, but connecting to a Unix socket only requires write access; a write-only socket (mode 0200) would be usable yet fail the check. Consider W_OK only, matching the docker CLI. Filed as #643.
  2. internal/docker/docker.goClient has no Close(); the SDK client holds an HTTP transport and idle connections. Add one for clean daemon shutdown. Filed as #644.
  3. internal/docker/docker_test.go — the permission-denied path (ErrDockerPermissionDenied) is untested; the wrap helper is not exercised. Add a test that skips when running as root. Filed as #645.
  4. internal/docker/docker.go (CheckAccess/Ping) — no internal timeout; the "fails fast" startup guarantee depends entirely on the caller's context, so a hung daemon could block startup indefinitely. Consider context.WithTimeout. Filed as #646.

No blocking issues. Approving.

Solid implementation. Code correctness verified against the vendored SDK: `Ping` hits the unversioned `/_ping` (HEAD→GET fallback), `ping.APIVersion` comes from the `Api-Version` header, so the fake-server tests are valid and `APIVersion()` returns the daemon's advertised version. Error chains (`errors.Is`) work with the double-`%w` wraps on go 1.26. Referenced issues #328–#332 all exist and are closed; roadmap checkboxes match. Vendoring follows the repo's existing pattern. ## Suggestions 1. `internal/docker/docker.go` (`verifySocket`) — access check uses `R_OK|W_OK`, but connecting to a Unix socket only requires write access; a write-only socket (mode `0200`) would be usable yet fail the check. Consider `W_OK` only, matching the docker CLI. Filed as #643. 2. `internal/docker/docker.go` — `Client` has no `Close()`; the SDK client holds an HTTP transport and idle connections. Add one for clean daemon shutdown. Filed as #644. 3. `internal/docker/docker_test.go` — the permission-denied path (`ErrDockerPermissionDenied`) is untested; the wrap helper is not exercised. Add a test that skips when running as root. Filed as #645. 4. `internal/docker/docker.go` (`CheckAccess`/`Ping`) — no internal timeout; the "fails fast" startup guarantee depends entirely on the caller's context, so a hung daemon could block startup indefinitely. Consider `context.WithTimeout`. Filed as #646. No blocking issues. Approving.
fuzzy merged commit 5eeeb309ad into main 2026-08-13 01:13:46 +00:00
fuzzy deleted branch feat/docker-client 2026-08-13 01:13:46 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
thwap/thwap-pagesd!642
No description provided.