 8d9b62daf3
			
		
	
	8d9b62daf3
	
	
	
		
			
			This commit implements Phase 2 of the CHORUS Task Execution Engine development plan, providing a comprehensive execution environment abstraction layer with Docker container sandboxing support. ## New Features ### Core Sandbox Interface - Comprehensive ExecutionSandbox interface with isolated task execution - Support for command execution, file I/O, environment management - Resource usage monitoring and sandbox lifecycle management - Standardized error handling with SandboxError types and categories ### Docker Container Sandbox Implementation - Full Docker API integration with secure container creation - Transparent repository mounting with configurable read/write access - Advanced security policies with capability dropping and privilege controls - Comprehensive resource limits (CPU, memory, disk, processes, file handles) - Support for tmpfs mounts, masked paths, and read-only bind mounts - Container lifecycle management with proper cleanup and health monitoring ### Security & Resource Management - Configurable security policies with SELinux, AppArmor, and Seccomp support - Fine-grained capability management with secure defaults - Network isolation options with configurable DNS and proxy settings - Resource monitoring with real-time CPU, memory, and network usage tracking - Comprehensive ulimits configuration for process and file handle limits ### Repository Integration - Seamless repository mounting from local paths to container workspaces - Git configuration support with user credentials and global settings - File inclusion/exclusion patterns for selective repository access - Configurable permissions and ownership for mounted repositories ### Testing Infrastructure - Comprehensive test suite with 60+ test cases covering all functionality - Docker integration tests with Alpine Linux containers (skipped in short mode) - Mock sandbox implementation for unit testing without Docker dependencies - Security policy validation tests with read-only filesystem enforcement - Resource usage monitoring and cleanup verification tests ## Technical Details ### Dependencies Added - github.com/docker/docker v28.4.0+incompatible - Docker API client - github.com/docker/go-connections v0.6.0 - Docker connection utilities - github.com/docker/go-units v0.5.0 - Docker units and formatting - Associated Docker API dependencies for complete container management ### Architecture - Interface-driven design enabling multiple sandbox implementations - Comprehensive configuration structures for all sandbox aspects - Resource usage tracking with detailed metrics collection - Error handling with retryable error classification - Proper cleanup and resource management throughout sandbox lifecycle ### Compatibility - Maintains backward compatibility with existing CHORUS architecture - Designed for future integration with Phase 3 Core Task Execution Engine - Extensible design supporting additional sandbox implementations (VM, process) This Phase 2 implementation provides the foundation for secure, isolated task execution that will be integrated with the AI model providers from Phase 1 in the upcoming Phase 3 development. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			415 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			415 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package execution
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"io"
 | |
| 	"time"
 | |
| )
 | |
| 
 | |
| // ExecutionSandbox defines the interface for isolated task execution environments
 | |
| type ExecutionSandbox interface {
 | |
| 	// Initialize sets up the sandbox environment
 | |
| 	Initialize(ctx context.Context, config *SandboxConfig) error
 | |
| 
 | |
| 	// ExecuteCommand runs a command within the sandbox
 | |
| 	ExecuteCommand(ctx context.Context, cmd *Command) (*CommandResult, error)
 | |
| 
 | |
| 	// CopyFiles copies files between host and sandbox
 | |
| 	CopyFiles(ctx context.Context, source, dest string) error
 | |
| 
 | |
| 	// WriteFile writes content to a file in the sandbox
 | |
| 	WriteFile(ctx context.Context, path string, content []byte, mode uint32) error
 | |
| 
 | |
| 	// ReadFile reads content from a file in the sandbox
 | |
| 	ReadFile(ctx context.Context, path string) ([]byte, error)
 | |
| 
 | |
| 	// ListFiles lists files in a directory within the sandbox
 | |
| 	ListFiles(ctx context.Context, path string) ([]FileInfo, error)
 | |
| 
 | |
| 	// GetWorkingDirectory returns the current working directory in the sandbox
 | |
| 	GetWorkingDirectory() string
 | |
| 
 | |
| 	// SetWorkingDirectory changes the working directory in the sandbox
 | |
| 	SetWorkingDirectory(path string) error
 | |
| 
 | |
| 	// GetEnvironment returns environment variables in the sandbox
 | |
| 	GetEnvironment() map[string]string
 | |
| 
 | |
| 	// SetEnvironment sets environment variables in the sandbox
 | |
| 	SetEnvironment(env map[string]string) error
 | |
| 
 | |
| 	// GetResourceUsage returns current resource usage statistics
 | |
| 	GetResourceUsage(ctx context.Context) (*ResourceUsage, error)
 | |
| 
 | |
| 	// Cleanup destroys the sandbox and cleans up resources
 | |
| 	Cleanup() error
 | |
| 
 | |
| 	// GetInfo returns information about the sandbox
 | |
| 	GetInfo() SandboxInfo
 | |
| }
 | |
| 
 | |
| // SandboxConfig represents configuration for a sandbox environment
 | |
| type SandboxConfig struct {
 | |
| 	// Sandbox type and runtime
 | |
| 	Type          string            `json:"type"`           // docker, vm, process
 | |
| 	Image         string            `json:"image"`          // Container/VM image
 | |
| 	Runtime       string            `json:"runtime"`        // docker, containerd, etc.
 | |
| 	Architecture  string            `json:"architecture"`   // amd64, arm64
 | |
| 
 | |
| 	// Resource limits
 | |
| 	Resources     ResourceLimits    `json:"resources"`
 | |
| 
 | |
| 	// Security settings
 | |
| 	Security      SecurityPolicy    `json:"security"`
 | |
| 
 | |
| 	// Repository configuration
 | |
| 	Repository    RepositoryConfig  `json:"repository"`
 | |
| 
 | |
| 	// Network settings
 | |
| 	Network       NetworkConfig     `json:"network"`
 | |
| 
 | |
| 	// Environment settings
 | |
| 	Environment   map[string]string `json:"environment"`
 | |
| 	WorkingDir    string            `json:"working_dir"`
 | |
| 
 | |
| 	// Tool and service access
 | |
| 	Tools         []string          `json:"tools"`          // Available tools in sandbox
 | |
| 	MCPServers    []string          `json:"mcp_servers"`    // MCP servers to connect to
 | |
| 
 | |
| 	// Execution settings
 | |
| 	Timeout       time.Duration     `json:"timeout"`        // Maximum execution time
 | |
| 	CleanupDelay  time.Duration     `json:"cleanup_delay"`  // Delay before cleanup
 | |
| 
 | |
| 	// Metadata
 | |
| 	Labels        map[string]string `json:"labels"`
 | |
| 	Annotations   map[string]string `json:"annotations"`
 | |
| }
 | |
| 
 | |
| // Command represents a command to execute in the sandbox
 | |
| type Command struct {
 | |
| 	// Command specification
 | |
| 	Executable    string            `json:"executable"`
 | |
| 	Args          []string          `json:"args"`
 | |
| 	WorkingDir    string            `json:"working_dir"`
 | |
| 	Environment   map[string]string `json:"environment"`
 | |
| 
 | |
| 	// Input/Output
 | |
| 	Stdin         io.Reader         `json:"-"`
 | |
| 	StdinContent  string            `json:"stdin_content"`
 | |
| 
 | |
| 	// Execution settings
 | |
| 	Timeout       time.Duration     `json:"timeout"`
 | |
| 	User          string            `json:"user"`
 | |
| 
 | |
| 	// Security settings
 | |
| 	AllowNetwork  bool              `json:"allow_network"`
 | |
| 	AllowWrite    bool              `json:"allow_write"`
 | |
| 	RestrictPaths []string          `json:"restrict_paths"`
 | |
| }
 | |
| 
 | |
| // CommandResult represents the result of command execution
 | |
| type CommandResult struct {
 | |
| 	// Exit information
 | |
| 	ExitCode      int               `json:"exit_code"`
 | |
| 	Success       bool              `json:"success"`
 | |
| 
 | |
| 	// Output
 | |
| 	Stdout        string            `json:"stdout"`
 | |
| 	Stderr        string            `json:"stderr"`
 | |
| 	Combined      string            `json:"combined"`
 | |
| 
 | |
| 	// Timing
 | |
| 	StartTime     time.Time         `json:"start_time"`
 | |
| 	EndTime       time.Time         `json:"end_time"`
 | |
| 	Duration      time.Duration     `json:"duration"`
 | |
| 
 | |
| 	// Resource usage during execution
 | |
| 	ResourceUsage ResourceUsage     `json:"resource_usage"`
 | |
| 
 | |
| 	// Error information
 | |
| 	Error         string            `json:"error,omitempty"`
 | |
| 	Signal        string            `json:"signal,omitempty"`
 | |
| 
 | |
| 	// Metadata
 | |
| 	ProcessID     int               `json:"process_id,omitempty"`
 | |
| 	Metadata      map[string]interface{} `json:"metadata,omitempty"`
 | |
| }
 | |
| 
 | |
| // FileInfo represents information about a file in the sandbox
 | |
| type FileInfo struct {
 | |
| 	Name          string            `json:"name"`
 | |
| 	Path          string            `json:"path"`
 | |
| 	Size          int64             `json:"size"`
 | |
| 	Mode          uint32            `json:"mode"`
 | |
| 	ModTime       time.Time         `json:"mod_time"`
 | |
| 	IsDir         bool              `json:"is_dir"`
 | |
| 	Owner         string            `json:"owner"`
 | |
| 	Group         string            `json:"group"`
 | |
| 	Permissions   string            `json:"permissions"`
 | |
| }
 | |
| 
 | |
| // ResourceLimits defines resource constraints for the sandbox
 | |
| type ResourceLimits struct {
 | |
| 	// CPU limits
 | |
| 	CPULimit      float64           `json:"cpu_limit"`       // CPU cores (e.g., 1.5)
 | |
| 	CPURequest    float64           `json:"cpu_request"`     // CPU cores requested
 | |
| 
 | |
| 	// Memory limits
 | |
| 	MemoryLimit   int64             `json:"memory_limit"`    // Bytes
 | |
| 	MemoryRequest int64             `json:"memory_request"`  // Bytes
 | |
| 
 | |
| 	// Storage limits
 | |
| 	DiskLimit     int64             `json:"disk_limit"`      // Bytes
 | |
| 	DiskRequest   int64             `json:"disk_request"`    // Bytes
 | |
| 
 | |
| 	// Network limits
 | |
| 	NetworkInLimit  int64           `json:"network_in_limit"`   // Bytes/sec
 | |
| 	NetworkOutLimit int64           `json:"network_out_limit"`  // Bytes/sec
 | |
| 
 | |
| 	// Process limits
 | |
| 	ProcessLimit  int               `json:"process_limit"`   // Max processes
 | |
| 	FileLimit     int               `json:"file_limit"`      // Max open files
 | |
| 
 | |
| 	// Time limits
 | |
| 	WallTimeLimit time.Duration     `json:"wall_time_limit"` // Max wall clock time
 | |
| 	CPUTimeLimit  time.Duration     `json:"cpu_time_limit"`  // Max CPU time
 | |
| }
 | |
| 
 | |
| // SecurityPolicy defines security constraints and policies
 | |
| type SecurityPolicy struct {
 | |
| 	// Container security
 | |
| 	RunAsUser       string            `json:"run_as_user"`
 | |
| 	RunAsGroup      string            `json:"run_as_group"`
 | |
| 	ReadOnlyRoot    bool              `json:"read_only_root"`
 | |
| 	NoNewPrivileges bool              `json:"no_new_privileges"`
 | |
| 
 | |
| 	// Capabilities
 | |
| 	AddCapabilities    []string       `json:"add_capabilities"`
 | |
| 	DropCapabilities   []string       `json:"drop_capabilities"`
 | |
| 
 | |
| 	// SELinux/AppArmor
 | |
| 	SELinuxContext     string         `json:"selinux_context"`
 | |
| 	AppArmorProfile    string         `json:"apparmor_profile"`
 | |
| 	SeccompProfile     string         `json:"seccomp_profile"`
 | |
| 
 | |
| 	// Network security
 | |
| 	AllowNetworking    bool           `json:"allow_networking"`
 | |
| 	AllowedHosts       []string       `json:"allowed_hosts"`
 | |
| 	BlockedHosts       []string       `json:"blocked_hosts"`
 | |
| 	AllowedPorts       []int          `json:"allowed_ports"`
 | |
| 
 | |
| 	// File system security
 | |
| 	ReadOnlyPaths      []string       `json:"read_only_paths"`
 | |
| 	MaskedPaths        []string       `json:"masked_paths"`
 | |
| 	TmpfsPaths         []string       `json:"tmpfs_paths"`
 | |
| 
 | |
| 	// Resource protection
 | |
| 	PreventEscalation  bool           `json:"prevent_escalation"`
 | |
| 	IsolateNetwork     bool           `json:"isolate_network"`
 | |
| 	IsolateProcess     bool           `json:"isolate_process"`
 | |
| 
 | |
| 	// Monitoring
 | |
| 	EnableAuditLog     bool           `json:"enable_audit_log"`
 | |
| 	LogSecurityEvents  bool           `json:"log_security_events"`
 | |
| }
 | |
| 
 | |
| // RepositoryConfig defines how the repository is mounted in the sandbox
 | |
| type RepositoryConfig struct {
 | |
| 	// Repository source
 | |
| 	URL           string            `json:"url"`
 | |
| 	Branch        string            `json:"branch"`
 | |
| 	CommitHash    string            `json:"commit_hash"`
 | |
| 	LocalPath     string            `json:"local_path"`
 | |
| 
 | |
| 	// Mount configuration
 | |
| 	MountPoint    string            `json:"mount_point"`     // Path in sandbox
 | |
| 	ReadOnly      bool              `json:"read_only"`
 | |
| 
 | |
| 	// Git configuration
 | |
| 	GitConfig     GitConfig         `json:"git_config"`
 | |
| 
 | |
| 	// File filters
 | |
| 	IncludeFiles  []string          `json:"include_files"`   // Glob patterns
 | |
| 	ExcludeFiles  []string          `json:"exclude_files"`   // Glob patterns
 | |
| 
 | |
| 	// Access permissions
 | |
| 	Permissions   string            `json:"permissions"`     // rwx format
 | |
| 	Owner         string            `json:"owner"`
 | |
| 	Group         string            `json:"group"`
 | |
| }
 | |
| 
 | |
| // GitConfig defines Git configuration within the sandbox
 | |
| type GitConfig struct {
 | |
| 	UserName      string            `json:"user_name"`
 | |
| 	UserEmail     string            `json:"user_email"`
 | |
| 	SigningKey    string            `json:"signing_key"`
 | |
| 	ConfigValues  map[string]string `json:"config_values"`
 | |
| }
 | |
| 
 | |
| // NetworkConfig defines network settings for the sandbox
 | |
| type NetworkConfig struct {
 | |
| 	// Network isolation
 | |
| 	Isolated      bool              `json:"isolated"`        // No network access
 | |
| 	Bridge        string            `json:"bridge"`          // Network bridge
 | |
| 
 | |
| 	// DNS settings
 | |
| 	DNSServers    []string          `json:"dns_servers"`
 | |
| 	DNSSearch     []string          `json:"dns_search"`
 | |
| 
 | |
| 	// Proxy settings
 | |
| 	HTTPProxy     string            `json:"http_proxy"`
 | |
| 	HTTPSProxy    string            `json:"https_proxy"`
 | |
| 	NoProxy       string            `json:"no_proxy"`
 | |
| 
 | |
| 	// Port mappings
 | |
| 	PortMappings  []PortMapping     `json:"port_mappings"`
 | |
| 
 | |
| 	// Bandwidth limits
 | |
| 	IngressLimit  int64             `json:"ingress_limit"`   // Bytes/sec
 | |
| 	EgressLimit   int64             `json:"egress_limit"`    // Bytes/sec
 | |
| }
 | |
| 
 | |
| // PortMapping defines port forwarding configuration
 | |
| type PortMapping struct {
 | |
| 	HostPort      int               `json:"host_port"`
 | |
| 	ContainerPort int               `json:"container_port"`
 | |
| 	Protocol      string            `json:"protocol"`        // tcp, udp
 | |
| }
 | |
| 
 | |
| // ResourceUsage represents current resource consumption
 | |
| type ResourceUsage struct {
 | |
| 	// Timestamp of measurement
 | |
| 	Timestamp     time.Time         `json:"timestamp"`
 | |
| 
 | |
| 	// CPU usage
 | |
| 	CPUUsage      float64           `json:"cpu_usage"`       // Percentage
 | |
| 	CPUTime       time.Duration     `json:"cpu_time"`        // Total CPU time
 | |
| 
 | |
| 	// Memory usage
 | |
| 	MemoryUsage   int64             `json:"memory_usage"`    // Bytes
 | |
| 	MemoryPercent float64           `json:"memory_percent"`  // Percentage of limit
 | |
| 	MemoryPeak    int64             `json:"memory_peak"`     // Peak usage
 | |
| 
 | |
| 	// Disk usage
 | |
| 	DiskUsage     int64             `json:"disk_usage"`      // Bytes
 | |
| 	DiskReads     int64             `json:"disk_reads"`      // Read operations
 | |
| 	DiskWrites    int64             `json:"disk_writes"`     // Write operations
 | |
| 
 | |
| 	// Network usage
 | |
| 	NetworkIn     int64             `json:"network_in"`      // Bytes received
 | |
| 	NetworkOut    int64             `json:"network_out"`     // Bytes sent
 | |
| 
 | |
| 	// Process information
 | |
| 	ProcessCount  int               `json:"process_count"`   // Active processes
 | |
| 	ThreadCount   int               `json:"thread_count"`    // Active threads
 | |
| 	FileHandles   int               `json:"file_handles"`    // Open file handles
 | |
| 
 | |
| 	// Runtime information
 | |
| 	Uptime        time.Duration     `json:"uptime"`          // Sandbox uptime
 | |
| }
 | |
| 
 | |
| // SandboxInfo provides information about a sandbox instance
 | |
| type SandboxInfo struct {
 | |
| 	// Identification
 | |
| 	ID            string            `json:"id"`
 | |
| 	Name          string            `json:"name"`
 | |
| 	Type          string            `json:"type"`
 | |
| 
 | |
| 	// Status
 | |
| 	Status        SandboxStatus     `json:"status"`
 | |
| 	CreatedAt     time.Time         `json:"created_at"`
 | |
| 	StartedAt     time.Time         `json:"started_at"`
 | |
| 
 | |
| 	// Runtime information
 | |
| 	Runtime       string            `json:"runtime"`
 | |
| 	Image         string            `json:"image"`
 | |
| 	Platform      string            `json:"platform"`
 | |
| 
 | |
| 	// Network information
 | |
| 	IPAddress     string            `json:"ip_address"`
 | |
| 	MACAddress    string            `json:"mac_address"`
 | |
| 	Hostname      string            `json:"hostname"`
 | |
| 
 | |
| 	// Resource information
 | |
| 	AllocatedResources ResourceLimits `json:"allocated_resources"`
 | |
| 
 | |
| 	// Configuration
 | |
| 	Config        SandboxConfig     `json:"config"`
 | |
| 
 | |
| 	// Metadata
 | |
| 	Labels        map[string]string `json:"labels"`
 | |
| 	Annotations   map[string]string `json:"annotations"`
 | |
| }
 | |
| 
 | |
| // SandboxStatus represents the current status of a sandbox
 | |
| type SandboxStatus string
 | |
| 
 | |
| const (
 | |
| 	StatusCreating  SandboxStatus = "creating"
 | |
| 	StatusStarting  SandboxStatus = "starting"
 | |
| 	StatusRunning   SandboxStatus = "running"
 | |
| 	StatusPaused    SandboxStatus = "paused"
 | |
| 	StatusStopping  SandboxStatus = "stopping"
 | |
| 	StatusStopped   SandboxStatus = "stopped"
 | |
| 	StatusFailed    SandboxStatus = "failed"
 | |
| 	StatusDestroyed SandboxStatus = "destroyed"
 | |
| )
 | |
| 
 | |
| // Common sandbox errors
 | |
| var (
 | |
| 	ErrSandboxNotFound        = &SandboxError{Code: "SANDBOX_NOT_FOUND", Message: "Sandbox not found"}
 | |
| 	ErrSandboxAlreadyExists   = &SandboxError{Code: "SANDBOX_ALREADY_EXISTS", Message: "Sandbox already exists"}
 | |
| 	ErrSandboxNotRunning      = &SandboxError{Code: "SANDBOX_NOT_RUNNING", Message: "Sandbox is not running"}
 | |
| 	ErrSandboxInitFailed      = &SandboxError{Code: "SANDBOX_INIT_FAILED", Message: "Sandbox initialization failed"}
 | |
| 	ErrCommandExecutionFailed = &SandboxError{Code: "COMMAND_EXECUTION_FAILED", Message: "Command execution failed"}
 | |
| 	ErrResourceLimitExceeded  = &SandboxError{Code: "RESOURCE_LIMIT_EXCEEDED", Message: "Resource limit exceeded"}
 | |
| 	ErrSecurityViolation      = &SandboxError{Code: "SECURITY_VIOLATION", Message: "Security policy violation"}
 | |
| 	ErrFileOperationFailed    = &SandboxError{Code: "FILE_OPERATION_FAILED", Message: "File operation failed"}
 | |
| 	ErrNetworkAccessDenied    = &SandboxError{Code: "NETWORK_ACCESS_DENIED", Message: "Network access denied"}
 | |
| 	ErrTimeoutExceeded        = &SandboxError{Code: "TIMEOUT_EXCEEDED", Message: "Execution timeout exceeded"}
 | |
| )
 | |
| 
 | |
| // SandboxError represents sandbox-specific errors
 | |
| type SandboxError struct {
 | |
| 	Code       string `json:"code"`
 | |
| 	Message    string `json:"message"`
 | |
| 	Details    string `json:"details,omitempty"`
 | |
| 	Retryable  bool   `json:"retryable"`
 | |
| 	Cause      error  `json:"-"`
 | |
| }
 | |
| 
 | |
| func (e *SandboxError) Error() string {
 | |
| 	if e.Details != "" {
 | |
| 		return e.Message + ": " + e.Details
 | |
| 	}
 | |
| 	return e.Message
 | |
| }
 | |
| 
 | |
| func (e *SandboxError) Unwrap() error {
 | |
| 	return e.Cause
 | |
| }
 | |
| 
 | |
| func (e *SandboxError) IsRetryable() bool {
 | |
| 	return e.Retryable
 | |
| }
 | |
| 
 | |
| // NewSandboxError creates a new sandbox error with details
 | |
| func NewSandboxError(base *SandboxError, details string) *SandboxError {
 | |
| 	return &SandboxError{
 | |
| 		Code:      base.Code,
 | |
| 		Message:   base.Message,
 | |
| 		Details:   details,
 | |
| 		Retryable: base.Retryable,
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // NewSandboxErrorWithCause creates a new sandbox error with an underlying cause
 | |
| func NewSandboxErrorWithCause(base *SandboxError, details string, cause error) *SandboxError {
 | |
| 	return &SandboxError{
 | |
| 		Code:      base.Code,
 | |
| 		Message:   base.Message,
 | |
| 		Details:   details,
 | |
| 		Retryable: base.Retryable,
 | |
| 		Cause:     cause,
 | |
| 	}
 | |
| } |