yao/tai/runtime/sandbox.go
Max c987bd687f fix(sandbox): pass explicit user to docker exec for UID-mapped containers
After tai sandbox containers start as root with dynamic UID mapping,
docker exec defaults to root. Add User field to ExecOptions and
hardcode "sandbox" in Box.Exec/Stream to ensure commands run as the
correct user.

Made-with: Cursor
2026-04-13 18:26:41 +08:00

112 lines
2.9 KiB
Go

package runtime
import (
"context"
"io"
"time"
)
// Runtime manages container lifecycle.
// Local connects directly to a Docker daemon; Docker/Containerd/K8s connect via Tai proxy.
type Runtime 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 provides real-time I/O access to a running exec process.
type StreamHandle struct {
Stdin io.WriteCloser
Stdout io.Reader
Stderr io.Reader
// Wait blocks until the exec process finishes and returns the exit code.
Wait func() (int, error)
// Cancel aborts the exec process.
Cancel func()
}
// CreateOptions configures a new container.
type CreateOptions struct {
Name string
Image string
Cmd []string
Env map[string]string
Binds []string
WorkingDir string
Memory int64 // bytes, 0 = no limit
CPUs float64 // 0 = no limit
VNC bool
Ports []PortMapping
Labels map[string]string // container/pod labels for discovery and management
User string // container user, e.g. "1000:1000" or "sandbox"
}
// PortMapping maps a container port to a host port.
type PortMapping struct {
ContainerPort int
HostPort int // 0 = random
HostIP string // default "127.0.0.1"
Protocol string // "tcp" (default) or "udp"
}
// ContainerInfo describes a running or stopped container.
type ContainerInfo struct {
ID string
Name string
Image string
Status string // "created", "running", "exited", "removing"
IP string
Ports []PortMapping
Labels map[string]string
}
// ExecOptions configures a command execution inside a container.
type ExecOptions struct {
WorkDir string
Env map[string]string
User string // container exec user; empty = container default (container.Config.User)
}
// ExecResult holds output from an exec command.
type ExecResult struct {
ExitCode int
Stdout string
Stderr string
}
// ListOptions filters container listing.
type ListOptions struct {
All bool // include stopped containers
Labels map[string]string // filter by labels
}
func envSlice(m map[string]string) []string {
if len(m) == 0 {
return nil
}
s := make([]string, 0, len(m))
for k, v := range m {
s = append(s, k+"="+v)
}
return s
}
func proto(p string) string {
if p == "" {
return "tcp"
}
return p
}
func hostIP(ip string) string {
if ip == "" {
return "127.0.0.1"
}
return ip
}