- Consolidate DialRemote/DialTunnel common logic into buildResources + dialEnv interface - Remove strict capability check that prevented ConnResources creation for host-exec-only nodes - Merge gRPC-discovered capabilities with registration-declared capabilities in DialTunnel - Replace requireKubeConfig hard-fail with graceful skip when kubeconfig is absent - Introduce tai/types package for shared Ports/Capabilities/SystemInfo/AuthInfo/NodeMeta - Add tai/conn.go (ConnResources) and tai/dial.go (DialRemote/DialTunnel/DialLocal) - Rename tai/sandbox → tai/runtime for clarity - Update sandbox/v2, workspace, agent/sandbox/v2 test utilities for build-tag isolation Made-with: Cursor
239 lines
5.2 KiB
Go
239 lines
5.2 KiB
Go
package tai
|
|
|
|
import (
|
|
"os"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/yaoapp/yao/tai/registry"
|
|
"github.com/yaoapp/yao/tai/types"
|
|
"github.com/yaoapp/yao/tai/volume"
|
|
)
|
|
|
|
func taiTestHost() string {
|
|
if h := os.Getenv("TAI_TEST_HOST"); h != "" {
|
|
return h
|
|
}
|
|
return "127.0.0.1"
|
|
}
|
|
|
|
func taiTestPorts() Ports {
|
|
return Ports{
|
|
Docker: envPort("TAI_TEST_DOCKER_PORT", 0),
|
|
HTTP: envPort("TAI_TEST_HTTP_PORT", 0),
|
|
VNC: envPort("TAI_TEST_VNC_PORT", 0),
|
|
}
|
|
}
|
|
|
|
func envPort(key string, fallback int) int {
|
|
if v := os.Getenv(key); v != "" {
|
|
if p, err := strconv.Atoi(v); err == nil {
|
|
return p
|
|
}
|
|
}
|
|
return fallback
|
|
}
|
|
|
|
func TestMergedPorts(t *testing.T) {
|
|
p := mergedPorts(Ports{HTTP: 8888})
|
|
if p.HTTP != 8888 {
|
|
t.Errorf("HTTP = %d, want 8888", p.HTTP)
|
|
}
|
|
if p.GRPC != 19100 {
|
|
t.Errorf("GRPC = %d, want 19100 (default)", p.GRPC)
|
|
}
|
|
if p.VNC != 16080 {
|
|
t.Errorf("VNC = %d, want 16080 (default)", p.VNC)
|
|
}
|
|
if p.Docker != 0 {
|
|
t.Errorf("Docker = %d, want 0 (unset)", p.Docker)
|
|
}
|
|
if p.K8s != 0 {
|
|
t.Errorf("K8s = %d, want 0 (unset)", p.K8s)
|
|
}
|
|
}
|
|
|
|
func TestMergedPortsAll(t *testing.T) {
|
|
p := mergedPorts(Ports{GRPC: 1, HTTP: 2, VNC: 3, Docker: 4, K8s: 5})
|
|
if p.GRPC != 1 || p.HTTP != 2 || p.VNC != 3 || p.Docker != 4 || p.K8s != 5 {
|
|
t.Errorf("unexpected ports: %+v", p)
|
|
}
|
|
}
|
|
|
|
func TestDialLocalSuccess(t *testing.T) {
|
|
res, err := DialLocal("", t.TempDir(), nil)
|
|
if err != nil {
|
|
t.Skipf("Docker not available: %v", err)
|
|
}
|
|
defer res.Close()
|
|
|
|
if res.Volume == nil {
|
|
t.Error("Volume should not be nil")
|
|
}
|
|
if res.Runtime == nil {
|
|
t.Error("Runtime should not be nil")
|
|
}
|
|
}
|
|
|
|
func TestDialLocalWithVolume(t *testing.T) {
|
|
dir := t.TempDir()
|
|
vol := volume.NewLocal(dir)
|
|
res, err := DialLocal("", dir, vol)
|
|
if err != nil {
|
|
t.Skipf("Docker not available: %v", err)
|
|
}
|
|
defer res.Close()
|
|
|
|
if res.DataDir != dir {
|
|
t.Errorf("DataDir = %q, want %q", res.DataDir, dir)
|
|
}
|
|
if res.Volume == nil {
|
|
t.Error("Volume should not be nil")
|
|
}
|
|
}
|
|
|
|
func TestDialLocalExplicitSocket(t *testing.T) {
|
|
res, err := DialLocal("unix:///var/run/docker.sock", t.TempDir(), nil)
|
|
if err != nil {
|
|
t.Skipf("Docker not available: %v", err)
|
|
}
|
|
defer res.Close()
|
|
|
|
if res.Runtime == nil {
|
|
t.Error("Runtime should not be nil for explicit unix socket")
|
|
}
|
|
}
|
|
|
|
func TestDialRemoteDocker(t *testing.T) {
|
|
host := taiTestHost()
|
|
grpcPort := envPort("TAI_TEST_GRPC_PORT", 19100)
|
|
ports := taiTestPorts()
|
|
ports.GRPC = grpcPort
|
|
|
|
res, err := DialRemote(host, ports)
|
|
if err != nil {
|
|
t.Skipf("Tai not available at %s:%d: %v", host, grpcPort, err)
|
|
}
|
|
defer res.Close()
|
|
|
|
t.Logf("remote docker: host=%s ports=%+v", host, res.Ports)
|
|
|
|
if res.Volume == nil {
|
|
t.Error("Volume should not be nil")
|
|
}
|
|
if res.Runtime == nil {
|
|
t.Error("Runtime should not be nil")
|
|
}
|
|
}
|
|
|
|
func TestDialRemoteK8s(t *testing.T) {
|
|
host := os.Getenv("TAI_TEST_K8S_HOST")
|
|
kubeconfig := os.Getenv("TAI_TEST_KUBECONFIG")
|
|
if host == "" || kubeconfig == "" {
|
|
t.Skip("TAI_TEST_K8S_HOST or TAI_TEST_KUBECONFIG not set")
|
|
}
|
|
|
|
grpcPort := envPort("TAI_TEST_K8S_GRPC_PORT", envPort("TAI_TEST_GRPC_PORT", 19100))
|
|
ports := Ports{
|
|
K8s: envPort("TAI_TEST_K8S_PORT", 16443),
|
|
GRPC: grpcPort,
|
|
HTTP: envPort("TAI_TEST_K8S_HTTP_PORT", 8099),
|
|
VNC: envPort("TAI_TEST_K8S_VNC_PORT", 16080),
|
|
}
|
|
|
|
res, err := DialRemote(host, ports,
|
|
WithDialRuntime(types.K8s),
|
|
WithDialKubeConfig(kubeconfig),
|
|
WithDialNamespace("default"),
|
|
)
|
|
if err != nil {
|
|
t.Skipf("Tai K8s not available: %v", err)
|
|
}
|
|
defer res.Close()
|
|
|
|
if res.Runtime == nil {
|
|
t.Error("Runtime should not be nil")
|
|
}
|
|
}
|
|
|
|
func TestDialRemoteK8sMissingKubeConfig(t *testing.T) {
|
|
host := taiTestHost()
|
|
grpcPort := envPort("TAI_TEST_GRPC_PORT", 19100)
|
|
|
|
_, err := DialRemote(host, Ports{GRPC: grpcPort}, WithDialRuntime(types.K8s))
|
|
if err == nil {
|
|
t.Skip("Tai happened to be reachable; test only valid when gRPC is up")
|
|
}
|
|
}
|
|
|
|
func TestRegisterLocal(t *testing.T) {
|
|
registry.Init(nil)
|
|
reg := registry.Global()
|
|
|
|
dir := t.TempDir()
|
|
ok := RegisterLocal(WithDataDir(dir))
|
|
if !ok {
|
|
t.Skip("Docker not available, skipping RegisterLocal test")
|
|
}
|
|
|
|
meta, found := reg.Get("local")
|
|
if !found {
|
|
t.Fatal("expected 'local' node in registry after RegisterLocal")
|
|
}
|
|
if meta.Mode != "local" {
|
|
t.Errorf("mode = %q, want 'local'", meta.Mode)
|
|
}
|
|
if meta.Status != "online" {
|
|
t.Errorf("status = %q, want 'online'", meta.Status)
|
|
}
|
|
|
|
res, got := GetResources("local")
|
|
if !got {
|
|
t.Fatal("GetResources('local') returned false after RegisterLocal")
|
|
}
|
|
if res.DataDir != dir {
|
|
t.Errorf("DataDir = %q, want %q", res.DataDir, dir)
|
|
}
|
|
if res.Runtime == nil {
|
|
t.Error("local resources Runtime should not be nil")
|
|
}
|
|
|
|
ok2 := RegisterLocal(WithDataDir(dir))
|
|
if !ok2 {
|
|
t.Error("second RegisterLocal should return true (idempotent)")
|
|
}
|
|
|
|
res.Close()
|
|
}
|
|
|
|
func TestRegisterLocal_NoRegistry(t *testing.T) {
|
|
origReg := registry.Global()
|
|
defer func() {
|
|
if origReg != nil {
|
|
registry.Init(nil)
|
|
}
|
|
}()
|
|
|
|
ok := RegisterLocal()
|
|
_ = ok
|
|
}
|
|
|
|
func TestRegisterLocal_NoDocker(t *testing.T) {
|
|
registry.Init(nil)
|
|
|
|
ok := RegisterLocal(WithDataDir(t.TempDir()))
|
|
if !ok {
|
|
return
|
|
}
|
|
res, got := GetResources("local")
|
|
if got && res != nil {
|
|
res.Close()
|
|
}
|
|
}
|
|
|
|
func TestConnResourcesCloseNil(t *testing.T) {
|
|
var r *ConnResources
|
|
if err := r.Close(); err != nil {
|
|
t.Errorf("Close on nil should return nil, got %v", err)
|
|
}
|
|
}
|