Skip to content

Implement Docker container engine for services#48

Merged
munezaclovis merged 14 commits intomainfrom
feat/docker-container-engine
Mar 25, 2026
Merged

Implement Docker container engine for services#48
munezaclovis merged 14 commits intomainfrom
feat/docker-container-engine

Conversation

@munezaclovis
Copy link
Copy Markdown
Contributor

Summary

Implements the entire Docker container layer that was previously stubbed out. All pv service:* commands now create, start, stop, remove, and inspect real Docker containers via the Colima Docker socket.

  • container.Engine — Full Docker SDK implementation with Pull, CreateAndStart, Start, Stop, Remove, Exec, Logs, IsRunning, Exists, and health check waiting
  • service:add — Real image pull + container creation with health checks, port bindings, volume mounts, restart policy
  • service:start — Real container start via Docker SDK
  • service:stop — Real container stop with 10s timeout
  • service:remove — Stop + remove container, preserve data
  • service:destroy — Stop + remove container + delete data directory
  • service:logs — Real log streaming to stdout via Docker SDK
  • service:status — Live Docker query instead of registry-based check
  • service:list — Live Docker query for running state
  • MySQL CreateDatabaseCREATE DATABASE IF NOT EXISTS via docker exec
  • PostgreSQL CreateDatabaseCREATE DATABASE via docker exec
  • Container recovery — Daemon startup restarts stopped service containers after Colima VM restart
  • Registry cleanup — Removed ContainerID field (Docker is source of truth for state)

Test plan

  • All existing tests pass (go test ./...)
  • go vet ./... clean
  • go build ./... clean
  • Manual: pv service:add mysql — verify image pulls and container starts
  • Manual: pv service:status mysql — verify shows "running"
  • Manual: pv service:stop mysql — verify container stops
  • Manual: pv service:start mysql — verify container restarts
  • Manual: pv service:logs mysql — verify log streaming
  • Manual: pv service:remove mysql — verify container removed, data preserved
  • Manual: pv service:add postgres && pv link myapp — verify database creation
  • Manual: restart pv daemon — verify containers recover automatically

Add Pull, CreateAndStart, Start, Stop, Remove, Exec, Logs,
IsRunning, Exists, and health check waiting. Connected via
Colima Docker socket using github.com/docker/docker/client.
Docker is the source of truth for container state. service:status
and service:list now query the engine via IsRunning. Container names
are deterministic from service name + version, so no ID storage needed.
- service:add — real engine.Pull + engine.CreateAndStart
- service:start — real engine.Start with container name from service def
- service:stop — real engine.Stop with container name from service def
- service:remove — engine.Stop + engine.Remove, preserves data
- service:destroy — engine.Stop + engine.Remove + data deletion
- service:logs streams container logs to stdout via engine.Logs
- MySQL CreateDatabase runs CREATE DATABASE IF NOT EXISTS via exec
- PostgreSQL CreateDatabase runs CREATE DATABASE via exec
- Daemon startup recovers stopped service containers after Colima
  VM restart
Docker bind mounts require the source path to exist. The data
directory was being created after CreateAndStart, causing
"bind source path does not exist" errors.
PostgreSQL 18+ stores data in version-specific subdirectories under
/var/lib/postgresql/ instead of directly in /var/lib/postgresql/data.
Mount at /var/lib/postgresql to support both old and new versions.
Set POSTGRES_USER and POSTGRES_PASSWORD explicitly (matching Laravel
Sail's approach). Use pg_isready with -d and -U flags for a more
reliable health check. Increase retries to 20 at 3s intervals to
give Postgres v18 more time to initialize its new directory layout.
Allow 'pv service:logs postgres' instead of requiring the full
key 'postgres:18-alpine'. ResolveServiceKey matches by name
prefix when exact key is not found.
FindService now matches by name prefix when exact key not found
(e.g. "postgres" matches "postgres:18-alpine"). ResolveServiceKey
added for commands to resolve shorthand names. Applied to all
service commands so users can type 'pv service:logs postgres'
instead of 'pv service:logs postgres:18-alpine'.
…s config

- Surface engine.IsRunning() and NewEngine() errors instead of silently
  defaulting to "stopped" in doctor, list, status, and process recovery
- Make ResolveServiceKey return error on ambiguous prefix match (e.g.
  "mysql" matching both "mysql:8.0" and "mysql:8.4")
- Simplify FindService to delegate to ResolveServiceKey
- Make Postgres CreateDatabase idempotent (check existence before CREATE)
- Fix Postgres port parsing for suffixed versions ("18-alpine" → 54018)
- Remove POSTGRES_HOST_AUTH_METHOD=trust, use password auth consistently
- Set DB_PASSWORD to "postgres" in Postgres EnvVars to match container
- Handle time.ParseDuration errors with sensible defaults in engine
- Surface stop/remove errors in service:destroy and service:remove
- Use ui.Fail instead of ui.Subtle for errors in service:add
- Share single engine connection in service:add (pull + create)
- Check os.Getwd() error in service:env
- Add ParseServiceKey helper, consolidate inline key splitting
- Add tests: ResolveServiceKey (exact/prefix/ambiguous), FindService
  fuzzy, Postgres defaults/port/env/CreateOpts, backward compat
…plication

- Fix SQL/shell injection: SanitizeProjectName now strips all non-[a-zA-Z0-9_] chars
- Fix service:add to return errors on Docker failure instead of saving phantom entries
- Fix service:start to recreate missing containers via Exists check + CreateAndStart
- Fix service:destroy to warn when Docker is unreachable (orphaned container risk)
- Change FindService to return (instance, error) so ambiguity errors propagate
- Fix service:list to warn on Docker connection and Lookup failures
- Fix doctor.go to distinguish "unknown service type" from "Docker unavailable"
- Fix Exec to use stdcopy.StdCopy for multiplexed Docker stream
- Eliminate duplicate health interval parse; reuse validated value
- Fix stop.go to use resolved key instead of raw args[0]
- Deduplicate extractServiceName/extractVersion with services.ParseServiceKey
- Add tests: ParseServiceKey, SanitizeProjectName, FindService ambiguity, Port edge case
@munezaclovis munezaclovis merged commit 385713b into main Mar 25, 2026
@munezaclovis munezaclovis deleted the feat/docker-container-engine branch March 25, 2026 22:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant