package docker import ( "runtime" "strings" ) // DiagCategory classifies a Docker connectivity error. type DiagCategory string const ( DiagSocketNotFound DiagCategory = "socket_not_found" DiagConnectionRefused DiagCategory = "connection_refused" DiagPermissionDenied DiagCategory = "permission_denied" DiagTimeout DiagCategory = "timeout" DiagTLSError DiagCategory = "tls_error" DiagUnknown DiagCategory = "unknown" ) // Diagnostic holds a classified error with platform-specific hints. type Diagnostic struct { Category DiagCategory `json:"category"` Hints []string `json:"hints"` Platform string `json:"platform"` } // Diagnose classifies a Docker Ping error and returns platform-aware hints. // Pure function — takes an error and platform, returns structured diagnostics. func Diagnose(err error, platform string) Diagnostic { if platform == "" { platform = runtime.GOOS } msg := "" if err != nil { msg = strings.ToLower(err.Error()) } cat := classifyError(msg) return Diagnostic{ Category: cat, Hints: hintsFor(cat, platform), Platform: platform, } } func classifyError(msg string) DiagCategory { switch { case containsAny(msg, "no such file or directory", "cannot find the file specified", "the system cannot find"): return DiagSocketNotFound case containsAny(msg, "connection refused"): return DiagConnectionRefused case containsAny(msg, "permission denied", "access is denied"): return DiagPermissionDenied case containsAny(msg, "context deadline exceeded", "i/o timeout", "timeout"): return DiagTimeout case containsAny(msg, "tls:", "certificate"): return DiagTLSError default: return DiagUnknown } } func containsAny(s string, substrs ...string) bool { for _, sub := range substrs { if strings.Contains(s, sub) { return true } } return false } var hintTable = map[DiagCategory]map[string][]string{ DiagSocketNotFound: { "windows": { "Docker Desktop does not appear to be running.", "Start Docker Desktop from the Start Menu or system tray.", "If using a custom socket path, check the DOCKER_HOST environment variable.", }, "linux": { "Docker daemon is not running.", "Start it with: sudo systemctl start docker", "If using a custom socket path, check the DOCKER_HOST environment variable.", }, "darwin": { "Docker Desktop does not appear to be running.", "Start it from Applications or run: open -a Docker", "If using a custom socket path, check the DOCKER_HOST environment variable.", }, }, DiagConnectionRefused: { "windows": { "Docker Desktop is starting up — wait ~30 seconds and retry.", "If it persists, restart Docker Desktop.", }, "linux": { "Docker daemon is starting up.", "Check status with: sudo systemctl status docker", }, "darwin": { "Docker Desktop is starting up.", "Check the whale icon in the menu bar for status.", }, }, DiagPermissionDenied: { "windows": { "Run the application as Administrator, or add your user to the docker-users group.", }, "linux": { "Add your user to the docker group: sudo usermod -aG docker $USER", "Then log out and log back in for the change to take effect.", }, "darwin": { "Check Docker Desktop settings under Resources > File Sharing.", }, }, DiagTimeout: { "windows": { "Docker Desktop may be overloaded or hanging.", "Try restarting Docker Desktop.", }, "linux": { "Docker daemon may be overloaded.", "Check logs with: journalctl -u docker --no-pager -n 50", }, "darwin": { "Docker Desktop may be unresponsive.", "Restart it from the menu bar whale icon.", }, }, DiagTLSError: { "windows": { "Check Docker TLS certificate configuration.", "Verify the DOCKER_TLS_VERIFY environment variable.", }, "linux": { "Verify certificates in ~/.docker/ match the daemon configuration.", }, "darwin": { "Check ~/.docker/ TLS configuration.", }, }, DiagUnknown: { "windows": { "An unexpected Docker error occurred.", "Try restarting Docker Desktop.", }, "linux": { "An unexpected Docker error occurred.", "Check daemon status with: sudo systemctl status docker", }, "darwin": { "An unexpected Docker error occurred.", "Try restarting Docker Desktop.", }, }, } func hintsFor(cat DiagCategory, platform string) []string { catHints, ok := hintTable[cat] if !ok { catHints = hintTable[DiagUnknown] } hints, ok := catHints[platform] if !ok { hints = catHints["linux"] // fallback } return hints }