package docker import ( "bytes" "context" "encoding/json" "fmt" "io" "net" "net/http" "os" "os/exec" "path/filepath" "strings" ) type Client struct { http *http.Client socket string } func DetectSocket() string { // 1. DOCKER_HOST env var if host := os.Getenv("DOCKER_HOST"); host != "" { if strings.HasPrefix(host, "unix://") { return strings.TrimPrefix(host, "unix://") } } // 2. docker context inspect (active context) if sockPath := getContextSocket(); sockPath != "" { return sockPath } // 3. Known paths knownPaths := []string{ "/var/run/docker.sock", filepath.Join(os.Getenv("HOME"), ".docker/run/docker.sock"), filepath.Join(os.Getenv("HOME"), "Library/containers/com.docker.docker/Data/docker.raw.sock"), "/run/docker.sock", } for _, path := range knownPaths { if _, err := os.Stat(path); err == nil { return path } } // 4. Fallback return "/var/run/docker.sock" } func getContextSocket() string { cmd := exec.Command("docker", "context", "inspect") out, err := cmd.Output() if err != nil { return "" } var contexts []map[string]interface{} if err := json.Unmarshal(out, &contexts); err != nil { return "" } if len(contexts) > 0 { if meta, ok := contexts[0]["Metadata"].(map[string]interface{}); ok { if host, ok := meta["host"].(string); ok && strings.HasPrefix(host, "unix://") { return strings.TrimPrefix(host, "unix://") } } } return "" } func NewClient(socketPath string) *Client { httpClient := &http.Client{ Transport: &http.Transport{ DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { return net.Dial("unix", socketPath) }, }, } return &Client{ http: httpClient, socket: socketPath, } } func (c *Client) Do(method, path string, body io.Reader) (*http.Response, error) { url := fmt.Sprintf("http://localhost%s", path) req, err := http.NewRequest(method, url, body) if err != nil { return nil, err } return c.http.Do(req) } func (c *Client) DoWithJSONBody(method, path string, bodyData interface{}) (*http.Response, error) { jsonBody, err := json.Marshal(bodyData) if err != nil { return nil, err } body := bytes.NewReader(jsonBody) return c.Do(method, path, body) } func (c *Client) GetJSON(path string, result interface{}) error { resp, err := c.Do("GET", path, nil) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("docker api error: %d", resp.StatusCode) } return json.NewDecoder(resp.Body).Decode(result) } func (c *Client) DoStream(method, path string, body io.Reader) (io.ReadCloser, error) { resp, err := c.Do(method, path, body) if err != nil { return nil, err } if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated { resp.Body.Close() return nil, fmt.Errorf("docker api error: %d", resp.StatusCode) } return resp.Body, nil }