feat(core): klien Docker SDK + tia-love doctor (docker/audio/go/stacks) + e2e doctor
CI / test (push) Successful in 10s

This commit is contained in:
oxpoe committed 2026-10-15 09:27:49 +07:00
1 parent 3f3cc9e8bb
commit 18055979da
9 files changed
+322 -9

No files matched your search

+62
View File
@@ -0,0 +1,62 @@
// Package docker — lapisan tipis di atas Docker SDK untuk core tia.
// Bebas UI: dipakai bersama oleh TUI dan CLI (F-028).
package docker
import (
"context"
"fmt"
"github.com/docker/docker/client"
)
// Client — pembungkus Docker SDK dengan negosiasi versi API
// (mendukung Docker Engine 20.10+, PRD §9).
type Client struct {
cli *client.Client
}
// New membaca konfigurasi dari env (DOCKER_HOST dll) atau default socket.
func New() (*Client, error) {
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
return nil, fmt.Errorf("docker: buat client: %w", err)
}
return &Client{cli: cli}, nil
}
func (c *Client) Close() error { return c.cli.Close() }
// Info — ringkasan daemon untuk dashboard, doctor, dan status bar.
type Info struct {
ServerVersion string
APIVersion string
Containers int
Running int
Images int
MemoryTotal int64
CPUs int
}
func (c *Client) Info(ctx context.Context) (Info, error) {
i, err := c.cli.Info(ctx)
if err != nil {
return Info{}, fmt.Errorf("docker: info: %w", err)
}
return Info{
ServerVersion: i.ServerVersion,
APIVersion: c.cli.ClientVersion(), // hasil negosiasi API
Containers: i.Containers,
Running: i.ContainersRunning,
Images: i.Images,
MemoryTotal: i.MemTotal,
CPUs: i.NCPU,
}, nil
}
// Ping — cek daemon hidup (dipakai doctor).
func (c *Client) Ping(ctx context.Context) error {
if _, err := c.cli.Ping(ctx); err != nil {
return fmt.Errorf("docker: ping: %w", err)
}
return nil
}
+38
View File
@@ -0,0 +1,38 @@
package docker
import (
"context"
"os"
"testing"
"time"
)
// TestIntegrationInfo dijalankan hanya bila docker socket tersedia —
// di CI runner tanpa socket, test di-skip otomatis.
func TestIntegrationInfo(t *testing.T) {
if _, err := os.Stat("/var/run/docker.sock"); err != nil {
t.Skip("docker socket tidak ada — skip tes integrasi")
}
c, err := New()
if err != nil {
t.Fatalf("New: %v", err)
}
defer c.Close()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := c.Ping(ctx); err != nil {
t.Fatalf("Ping: %v", err)
}
info, err := c.Info(ctx)
if err != nil {
t.Fatalf("Info: %v", err)
}
if info.ServerVersion == "" {
t.Fatal("ServerVersion kosong")
}
t.Logf("engine %s (API %s) · %d/%d kontainer berjalan · %d image · %d CPU · %d MB RAM",
info.ServerVersion, info.APIVersion, info.Running, info.Containers,
info.Images, info.CPUs, info.MemoryTotal/1024/1024)
}