Files
jiggablend/internal/runner/sandbox/sandbox.go
T
s1d3sw1ped 1a69fcfd04 Refactor installation and runner scripts for enhanced usability and security
- Updated the installation script to clarify that production wrappers do not include fixed test secrets, improving user guidance for local testing.
- Modified the jiggablend-manager and jiggablend-runner scripts to remove fixed test configurations, emphasizing the use of local test credentials via `make init-test`.
- Enhanced error handling in the runner script to ensure that an API key is provided, improving security and user feedback.
- Added new flags and options for the runner, including sandboxing capabilities and GPU ray tracing control, enhancing flexibility for users.
- Improved README documentation to reflect changes in usage and configuration, ensuring users have clear instructions for setup and execution.
2026-07-12 10:01:15 -05:00

129 lines
3.5 KiB
Go

// Package sandbox wraps Blender (and similar) invocations in optional isolation.
//
// Backends:
// - none: run on the host (current behavior)
// - podman: rootless podman container; Blender tarball is bind-mounted (not baked into an image)
//
// GPU access is provided by passing host device nodes and common userspace lib roots
// discovered at wrap time (NVIDIA / AMD ROCm / Intel DRM), not by shipping per-version
// Blender container images.
package sandbox
import (
"fmt"
"os"
"os/exec"
"strings"
)
// Backend names accepted by New and CLI flags.
const (
BackendNone = "none"
BackendPodman = "podman"
)
// Options configures sandbox construction.
type Options struct {
// Backend is none|podman (default none).
Backend string
// AllowNetwork keeps network in the sandbox (default false for podman).
AllowNetwork bool
// PodmanImage is used only for backend=podman. The image is a thin OS root;
// Blender comes from a host bind of the versioned tarball tree.
// Default: registry.fedoraproject.org/fedora-minimal:41
PodmanImage string
}
// Spec describes a Blender process to run under the sandbox.
type Spec struct {
// BlenderBinary is an absolute path to the blender executable.
BlenderBinary string
// Args are arguments after the binary (not including argv0).
Args []string
// WorkDir is the process working directory (job workspace); bind-mounted RW.
WorkDir string
// HomeDir is Blender HOME (usually WorkDir/home); bind-mounted RW.
HomeDir string
// Env is the full environment for the process (HOME, LD_LIBRARY_PATH, etc.).
Env []string
// GPU hints from host detection (used to select device/lib binds).
HasAMD bool
HasNVIDIA bool
HasIntel bool
// ForceCPU skips GPU device binds when true.
ForceCPU bool
// AllowNetwork overrides Options.AllowNetwork when non-nil... kept simple: use Options only.
}
// Wrapper turns a Spec into an *exec.Cmd ready to Start.
type Wrapper interface {
// Name returns the backend name.
Name() string
// Available reports whether required host tools exist.
Available() error
// Wrap builds the command. Callers own Start/Wait/pipes.
Wrap(spec Spec) (*exec.Cmd, error)
}
// New returns a sandbox Wrapper for the given options.
func New(opts Options) (Wrapper, error) {
backend := strings.ToLower(strings.TrimSpace(opts.Backend))
if backend == "" {
backend = BackendPodman
}
switch backend {
case BackendNone:
return &noneWrapper{}, nil
case BackendPodman:
img := opts.PodmanImage
if img == "" {
img = defaultPodmanImage
}
w := &podmanWrapper{allowNetwork: opts.AllowNetwork, image: img}
return w, nil
default:
return nil, fmt.Errorf("unknown sandbox backend %q (want none or podman)", opts.Backend)
}
}
// NormalizeBackend validates and returns a canonical backend name.
func NormalizeBackend(s string) (string, error) {
b := strings.ToLower(strings.TrimSpace(s))
if b == "" {
return BackendPodman, nil
}
switch b {
case BackendNone, BackendPodman:
return b, nil
default:
return "", fmt.Errorf("unknown sandbox backend %q (want none or podman)", s)
}
}
// LookPath is os/exec.LookPath, overridable in tests.
var LookPath = exec.LookPath
// pathExists reports whether path exists.
func pathExists(p string) bool {
_, err := os.Stat(p)
return err == nil
}
// mustAbs returns an absolute path or the original on error.
func mustAbs(p string) string {
if p == "" {
return p
}
abs, err := absPath(p)
if err != nil {
return p
}
return abs
}
// absPath is filepath.Abs, isolated for tests.
var absPath = func(p string) (string, error) {
return execAbs(p)
}