Extend worker capabilities with new execution plugins and security features: - Jupyter plugin for notebook-based ML experiments - vLLM plugin for LLM inference workloads - Cross-platform process isolation (Unix/Windows) - Network policy enforcement with platform-specific implementations - Service manager integration for lifecycle management - Scheduler backend integration for queue coordination Update lifecycle management: - Enhanced runloop with state transitions - Service manager integration for plugin coordination - Improved state persistence and recovery Add test coverage: - Unit tests for Jupyter and vLLM plugins - Updated worker execution tests
122 lines
3.4 KiB
Go
122 lines
3.4 KiB
Go
//go:build !windows
|
|
// +build !windows
|
|
|
|
package process
|
|
|
|
import (
|
|
"fmt"
|
|
"syscall"
|
|
)
|
|
|
|
// applyResourceLimits sets Unix/Linux resource limits
|
|
func applyResourceLimits(cfg IsolationConfig) error {
|
|
// Apply file descriptor limits (RLIMIT_NOFILE for FD exhaustion protection)
|
|
if cfg.MaxOpenFiles > 0 {
|
|
if err := setResourceLimit(syscall.RLIMIT_NOFILE, uint64(cfg.MaxOpenFiles)); err != nil {
|
|
return fmt.Errorf("failed to set max open files limit: %w", err)
|
|
}
|
|
}
|
|
|
|
// Apply process limits if available (Linux only)
|
|
if cfg.MaxProcesses > 0 {
|
|
if err := setProcessLimit(cfg.MaxProcesses); err != nil {
|
|
// Log but don't fail - this is defense in depth
|
|
return fmt.Errorf("failed to set max processes limit: %w", err)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// setResourceLimit sets a soft and hard rlimit for the current process
|
|
func setResourceLimit(resource int, limit uint64) error {
|
|
rl := &syscall.Rlimit{
|
|
Cur: limit,
|
|
Max: limit,
|
|
}
|
|
return syscall.Setrlimit(resource, rl)
|
|
}
|
|
|
|
// setProcessLimit sets RLIMIT_NPROC on Linux, no-op on other Unix
|
|
func setProcessLimit(maxProcs int) error {
|
|
// Try to set RLIMIT_NPROC - only available on Linux
|
|
// On Darwin/macOS, this returns ENOTSUP
|
|
const RLIMIT_NPROC = 7 // Linux value
|
|
rl := &syscall.Rlimit{
|
|
Cur: uint64(maxProcs),
|
|
Max: uint64(maxProcs),
|
|
}
|
|
err := syscall.Setrlimit(RLIMIT_NPROC, rl)
|
|
if err != nil {
|
|
// ENOTSUP means not supported (macOS)
|
|
if err == syscall.ENOTSUP || err == syscall.EINVAL {
|
|
return nil // Silently ignore on platforms that don't support it
|
|
}
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// disableSwap attempts to lock memory to prevent swapping (mlockall)
|
|
// This is best-effort and requires CAP_IPC_LOCK capability
|
|
func disableSwap() error {
|
|
// MCL_CURRENT: lock all current pages
|
|
// MCL_FUTURE: lock all future pages
|
|
const MCL_CURRENT = 0x1
|
|
const MCL_FUTURE = 0x2
|
|
|
|
// Note: mlockall requires CAP_IPC_LOCK capability
|
|
// If this fails, we log but continue (defense in depth)
|
|
return syscall.Mlockall(MCL_CURRENT | MCL_FUTURE)
|
|
}
|
|
|
|
// GetCurrentLimits returns the current rlimit values for diagnostics
|
|
func GetCurrentLimits() (map[string]uint64, error) {
|
|
limits := make(map[string]uint64)
|
|
|
|
// Get NOFILE limit (available on all platforms)
|
|
var nofile syscall.Rlimit
|
|
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &nofile); err != nil {
|
|
return nil, fmt.Errorf("failed to get NOFILE limit: %w", err)
|
|
}
|
|
limits["NOFILE_soft"] = nofile.Cur
|
|
limits["NOFILE_hard"] = nofile.Max
|
|
|
|
// Get platform-specific limits
|
|
getPlatformLimits(limits)
|
|
|
|
return limits, nil
|
|
}
|
|
|
|
// getPlatformLimits adds platform-specific limits to the map
|
|
func getPlatformLimits(limits map[string]uint64) {
|
|
// Get virtual memory limit (AS)
|
|
var as syscall.Rlimit
|
|
if err := syscall.Getrlimit(syscall.RLIMIT_AS, &as); err == nil {
|
|
limits["AS_soft"] = as.Cur
|
|
limits["AS_hard"] = as.Max
|
|
}
|
|
|
|
// Get data segment limit
|
|
var data syscall.Rlimit
|
|
if err := syscall.Getrlimit(syscall.RLIMIT_DATA, &data); err == nil {
|
|
limits["DATA_soft"] = data.Cur
|
|
limits["DATA_hard"] = data.Max
|
|
}
|
|
|
|
// Try to get RLIMIT_NPROC (Linux only)
|
|
const RLIMIT_NPROC = 7
|
|
var nproc syscall.Rlimit
|
|
if err := syscall.Getrlimit(RLIMIT_NPROC, &nproc); err == nil {
|
|
limits["NPROC_soft"] = nproc.Cur
|
|
limits["NPROC_hard"] = nproc.Max
|
|
}
|
|
|
|
// Try to get RLIMIT_RSS (Linux only)
|
|
const RLIMIT_RSS = 5
|
|
var rss syscall.Rlimit
|
|
if err := syscall.Getrlimit(RLIMIT_RSS, &rss); err == nil {
|
|
limits["RSS_soft"] = rss.Cur
|
|
limits["RSS_hard"] = rss.Max
|
|
}
|
|
}
|