Files
2026-04-24 15:36:53 -03:00

131 lines
2.9 KiB
Go

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
}