yao/tai/docs/sandbox.md
Max d70694b7ac ci: update Tai to 1.2.0 with new default ports and fix documentation
- Update CI (unit-test.yml, pr-test.yml) to use yaoapp/tai:1.2.0
  with new default ports (gRPC:19100, HTTP:8099, VNC:16080, Docker:12375)
- Add explicit 0.0.0.0 bind for containerized Tai instances
- Fix sandbox/v2 grpc.go default port fallback (9100 → 19100)
- Fix tai/tunnel/proxy.go fallback ports (8080→8099, 6080→16080)
- Sync tai SDK and sandbox/v2 documentation with implementation
- Add new docs: api.md, registry.md, tunnel.md

Made-with: Cursor
2026-03-08 11:33:56 +08:00

272 lines
8.1 KiB
Markdown

# Package `sandbox`
Container lifecycle management. Provides a unified `Sandbox` interface with three implementations:
| Implementation | Constructor | Backend | Mode |
|----------------|-------------|---------|------|
| **Local** | `NewLocal(addr)` | Direct Docker daemon | Local |
| **Docker** | `NewDocker(addr)` | Docker via Tai proxy | Remote |
| **K8s** | `NewK8s(addr, opts)` | Kubernetes via Tai proxy | Remote |
## Interface
```go
type Sandbox interface {
Create(ctx context.Context, opts CreateOptions) (string, error)
Start(ctx context.Context, id string) error
Stop(ctx context.Context, id string, timeout time.Duration) error
Remove(ctx context.Context, id string, force bool) error
Exec(ctx context.Context, id string, cmd []string, opts ExecOptions) (*ExecResult, error)
ExecStream(ctx context.Context, id string, cmd []string, opts ExecOptions) (*StreamHandle, error)
Inspect(ctx context.Context, id string) (*ContainerInfo, error)
List(ctx context.Context, opts ListOptions) ([]ContainerInfo, error)
Close() error
}
```
### StreamHandle
```go
type StreamHandle struct {
Stdin io.WriteCloser
Stdout io.Reader
Stderr io.Reader
Wait func() (int, error) // blocks until exec finishes, returns exit code
Cancel func() // aborts the exec process
}
```
`ExecStream` provides real-time I/O access to a running exec process. Unlike `Exec` which collects all output, `ExecStream` returns immediately with readers/writers for interactive use.
## Constructors
### NewLocal
```go
func NewLocal(addr string) (Sandbox, error)
```
Connects directly to a Docker daemon. `addr` can be:
- `""` — platform default (Unix socket on Linux/macOS, named pipe on Windows)
- `"unix:///var/run/docker.sock"` — explicit Unix socket
- `"tcp://host:port"` — explicit TCP
Pings the daemon on creation; returns an error if unreachable.
### NewDocker
```go
func NewDocker(addr string) (Sandbox, error)
```
Connects to Docker Engine API through Tai's Docker proxy. `addr` should be `"tcp://tai-host:12375"`.
### NewK8s
```go
func NewK8s(addr string, opts ...K8sOption) (Sandbox, error)
```
Connects to Kubernetes through Tai's TCP proxy. Each sandbox maps to a single-container Pod.
**Parameters:**
- `addr``"host:port"` pointing to Tai's K8s proxy endpoint
- `opts.KubeConfig` — path to kubeconfig file (**required**). Relative paths are resolved to absolute.
- `opts.Namespace` — Kubernetes namespace (default `"default"`)
The constructor overrides the kubeconfig's `server` field to point at `addr`, enables insecure TLS (since Tai does TCP passthrough), and verifies connectivity by querying the namespace.
All pods created by K8s sandbox are labeled with `managed-by: yao-tai-sdk`.
## Types
### CreateOptions
```go
type CreateOptions struct {
Name string // container/pod name
Image string // container image
Cmd []string // entrypoint command
Env map[string]string // environment variables
Binds []string // volume binds (Docker only)
WorkingDir string // working directory
Memory int64 // memory limit in bytes, 0 = no limit
CPUs float64 // CPU limit, 0 = no limit
VNC bool // enable VNC port mapping (Local and Docker modes)
Ports []PortMapping // port mappings (Docker only)
Labels map[string]string // container/pod labels for discovery and management
User string // container user, e.g. "1000:1000" or "sandbox"
}
```
### PortMapping
```go
type PortMapping struct {
ContainerPort int // port inside the container
HostPort int // port on the host, 0 = random
HostIP string // host bind address, default "127.0.0.1"
Protocol string // "tcp" (default) or "udp"
}
```
### ContainerInfo
```go
type ContainerInfo struct {
ID string // container/pod ID
Name string // container/pod name
Image string // image name
Status string // "created", "running", "exited", "removing" (Docker)
// "Pending", "Running", "Succeeded", "Failed" (K8s)
IP string // container/pod IP address
Ports []PortMapping // mapped ports (Docker only)
Labels map[string]string // container/pod labels
}
```
### ExecOptions
```go
type ExecOptions struct {
WorkDir string // override working directory
Env map[string]string // additional environment variables
}
```
### ExecResult
```go
type ExecResult struct {
ExitCode int
Stdout string
Stderr string
}
```
### ListOptions
```go
type ListOptions struct {
All bool // include stopped containers
Labels map[string]string // filter by labels
}
```
### K8sOption
```go
type K8sOption struct {
Namespace string // default "default"
KubeConfig string // path to kubeconfig file (required)
}
```
## Behavioral Differences
| Behavior | Docker (Local/Remote) | K8s |
|----------|----------------------|-----|
| `Create` returns | container ID (hash) | pod name |
| `Start` | starts a stopped container | polls until pod leaves Pending (up to 60s) |
| `Stop` | stops with timeout, container persists | deletes the pod with grace period |
| `Remove(force=true)` | force-removes | deletes with grace period 0 |
| `Exec` | Docker exec API | `kubectl exec` via SPDY |
| `Inspect.Ports` | populated from Docker | always empty |
| `List` | filters only by `opts.Labels` (no auto label) | auto-merges `managed-by=yao-tai-sdk` + `opts.Labels` |
| `Binds` | supported | not supported |
| `VNC` flag | auto port-maps 6080 and 5900 (all platforms) | not applicable |
## Image Interface
```go
type Image interface {
Exists(ctx context.Context, ref string) (bool, error)
Pull(ctx context.Context, ref string, opts PullOptions) (<-chan PullProgress, error)
Remove(ctx context.Context, ref string, force bool) error
List(ctx context.Context) ([]ImageInfo, error)
}
```
Accessed via `c.Image()` on the top-level client. Nil when the Tai server has no container runtime.
| Implementation | Constructor | Backend | Notes |
|----------------|-------------|---------|-------|
| **Docker** | `NewDockerImage(cli)` | Docker SDK | Shared by Local and Docker-via-Tai modes |
| **K8s** | `NewK8sImage()` | No-op | Image pulling is handled by kubelet |
### DockerCli Helper
```go
func DockerCli(sb Sandbox) *client.Client
```
Extracts the underlying Docker SDK client from a `Sandbox` (Local or Docker). Returns `nil` for K8s sandboxes. Used internally to construct `NewDockerImage(DockerCli(sb))`.
### Types
```go
type PullOptions struct {
Auth *RegistryAuth // nil = anonymous / public
}
type RegistryAuth struct {
Username string
Password string
Server string // e.g. "ghcr.io", "registry.example.com"
}
type PullProgress struct {
Status string // "Pulling fs layer", "Downloading", "Extracting", "Pull complete", etc.
Layer string // layer digest / short ID
Current int64 // bytes completed
Total int64 // bytes total (0 if unknown)
Error string // non-empty on failure
}
type ImageInfo struct {
ID string
Tags []string
Size int64
Created time.Time
}
```
### Image Example
```go
c, _ := tai.New("tai://192.168.1.100")
defer c.Close()
progress, _ := c.Image().Pull(ctx, "alpine:latest", sandbox.PullOptions{})
for p := range progress {
fmt.Printf("%s %s %d/%d\n", p.Status, p.Layer, p.Current, p.Total)
}
images, _ := c.Image().List(ctx)
for _, img := range images {
fmt.Printf("%s %v\n", img.ID[:12], img.Tags)
}
```
## Sandbox Example
```go
sb, _ := sandbox.NewLocal("")
defer sb.Close()
id, _ := sb.Create(ctx, sandbox.CreateOptions{
Name: "worker",
Image: "alpine:latest",
Cmd: []string{"sleep", "300"},
Env: map[string]string{"FOO": "bar"},
Memory: 256 * 1024 * 1024, // 256 MB
})
sb.Start(ctx, id)
result, _ := sb.Exec(ctx, id, []string{"echo", "$FOO"}, sandbox.ExecOptions{})
fmt.Println(result.Stdout)
sb.Stop(ctx, id, 10*time.Second)
sb.Remove(ctx, id, false)
```