Files
tia-love/internal/docker/engine.go
T

98 lines
2.7 KiB
Go

package docker
import (
"context"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"github.com/docker/docker/client"
)
// EngineInfo — runtime yang benar-benar terdeteksi (Docker ATAU Podman).
type EngineInfo struct {
Name string // "docker" | "podman"
Host string // unix:///… atau tcp://… (kosong = default/env DOCKER_HOST)
}
// candidate — endpoint yang dicoba berurutan saat deteksi.
type candidate struct {
name string
host string // kosong = biarkan env/DOCKER_HOST yang menentukan
}
// engineCandidates — urutan kandidat: override config → DOCKER_HOST →
// docker default → podman rootless (XDG) → podman rootful.
func engineCandidates(override string) []candidate {
switch {
case override != "":
return []candidate{{name: nameFromHost(override), host: override}}
case os.Getenv("DOCKER_HOST") != "":
h := os.Getenv("DOCKER_HOST")
return []candidate{{name: nameFromHost(h), host: ""}}
}
out := []candidate{{name: "docker", host: ""}}
if xdg := os.Getenv("XDG_RUNTIME_DIR"); xdg != "" {
out = append(out, candidate{
name: "podman",
host: "unix://" + filepath.Join(xdg, "podman", "podman.sock"),
})
}
out = append(out, candidate{name: "podman", host: "unix:///run/podman/podman.sock"})
return out
}
func nameFromHost(host string) string {
if strings.Contains(host, "podman") {
return "podman"
}
return "docker"
}
// Detect — client pertama yang ping-nya hidup, plus info runtime-nya.
// Podman mengekspos API kompatibel-Docker di podman.sock, jadi client yang
// sama dipakai untuk keduanya.
func Detect(override string) (*Client, EngineInfo, error) {
connect := func(host string) (*Client, error) {
opts := []client.Opt{client.WithAPIVersionNegotiation()}
if host == "" {
opts = append(opts, client.FromEnv)
} else {
opts = append(opts, client.WithHost(host))
}
cli, err := client.NewClientWithOpts(opts...)
if err != nil {
return nil, err
}
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
if _, err := cli.Ping(ctx); err != nil {
_ = cli.Close()
return nil, err
}
return &Client{cli: cli}, nil
}
return detectWith(engineCandidates(override), connect)
}
// detectWith — inti deteksi; connect dapat di-stub di unit test.
func detectWith(cands []candidate, connect func(host string) (*Client, error)) (*Client, EngineInfo, error) {
var lastErr error
for _, c := range cands {
cl, err := connect(c.host)
if err == nil {
return cl, EngineInfo{Name: c.name, Host: c.host}, nil
}
lastErr = err
}
return nil, EngineInfo{}, fmt.Errorf("engine: tidak ada Docker/Podman yang merespons: %w", lastErr)
}
// New — pintasan auto-detect (kompatibilitas; info runtime diabaikan).
func New() (*Client, error) {
cl, _, err := Detect("")
return cl, err
}