feat: add testing support for crypto initialization and process management, including auto-recovery and filtering of go wrapper processes
This commit is contained in:
+55
-12
@@ -43,17 +43,57 @@ type ProcessStats struct {
|
||||
LogBytes int64
|
||||
}
|
||||
|
||||
// processProber abstracts process detection for testing.
|
||||
type processProber interface {
|
||||
// pgrepPIDs runs pgrep -f with the given pattern and returns matching PIDs.
|
||||
pgrepPIDs(pattern string) []int
|
||||
// processComm returns the comm name for a PID (e.g. "launcher", "go").
|
||||
processComm(pid int) string
|
||||
// isAlive checks if a PID is running.
|
||||
isAlive(pid int) bool
|
||||
}
|
||||
|
||||
// osProber is the real implementation using OS calls.
|
||||
type osProber struct{}
|
||||
|
||||
func (osProber) pgrepPIDs(pattern string) []int {
|
||||
out, err := exec.Command("pgrep", "-f", pattern).Output()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var pids []int
|
||||
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
|
||||
if p, err := strconv.Atoi(strings.TrimSpace(line)); err == nil && p > 0 {
|
||||
pids = append(pids, p)
|
||||
}
|
||||
}
|
||||
return pids
|
||||
}
|
||||
|
||||
func (osProber) processComm(pid int) string {
|
||||
data, err := os.ReadFile(fmt.Sprintf("/proc/%d/comm", pid))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(data))
|
||||
}
|
||||
|
||||
func (osProber) isAlive(pid int) bool {
|
||||
return syscall.Kill(pid, 0) == nil
|
||||
}
|
||||
|
||||
// Manager handles agent process lifecycle.
|
||||
type Manager struct {
|
||||
runDir string
|
||||
agentsGlob string
|
||||
binPath string
|
||||
envFile string // path to .env file for child processes
|
||||
prober processProber
|
||||
}
|
||||
|
||||
// NewManager creates a Manager. binPath can be empty for auto-detection.
|
||||
func NewManager(runDir, agentsGlob, binPath string) *Manager {
|
||||
return &Manager{runDir: runDir, agentsGlob: agentsGlob, binPath: binPath, envFile: ".env"}
|
||||
return &Manager{runDir: runDir, agentsGlob: agentsGlob, binPath: binPath, envFile: ".env", prober: osProber{}}
|
||||
}
|
||||
|
||||
// Scan discovers all agents from config files.
|
||||
@@ -110,8 +150,11 @@ func (m *Manager) StatusAll() ([]AgentStatus, error) {
|
||||
}
|
||||
|
||||
// Start launches an agent process in the background.
|
||||
// Multiple instances of the same agent are allowed.
|
||||
// Returns an error if the agent is already running.
|
||||
func (m *Manager) Start(info AgentInfo) error {
|
||||
if pids := m.findProcessPIDs(info.ID); len(pids) > 0 {
|
||||
return fmt.Errorf("agent %q is already running (PID %d)", info.ID, pids[0])
|
||||
}
|
||||
if err := os.MkdirAll(m.runDir, 0o755); err != nil {
|
||||
return fmt.Errorf("create run dir: %w", err)
|
||||
}
|
||||
@@ -351,23 +394,23 @@ func (m *Manager) readPID(id string) int {
|
||||
}
|
||||
|
||||
// findProcessPIDs searches for running launcher processes for a given agent ID
|
||||
// using pgrep. Returns all matching PIDs.
|
||||
// using pgrep. Filters out "go run" wrapper PIDs to avoid double-counting.
|
||||
func (m *Manager) findProcessPIDs(id string) []int {
|
||||
// First try to find the config path for this agent
|
||||
configPath := m.configPathFor(id)
|
||||
if configPath == "" {
|
||||
return nil
|
||||
}
|
||||
pattern := fmt.Sprintf("launcher.*-c.*%s", configPath)
|
||||
out, err := exec.Command("pgrep", "-f", pattern).Output()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
raw := m.prober.pgrepPIDs(pattern)
|
||||
|
||||
// Filter out the "go" wrapper process that appears when using "go run".
|
||||
var pids []int
|
||||
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
|
||||
if p, err := strconv.Atoi(strings.TrimSpace(line)); err == nil && p > 0 {
|
||||
pids = append(pids, p)
|
||||
for _, p := range raw {
|
||||
comm := m.prober.processComm(p)
|
||||
if comm == "go" {
|
||||
continue
|
||||
}
|
||||
pids = append(pids, p)
|
||||
}
|
||||
return pids
|
||||
}
|
||||
@@ -415,7 +458,7 @@ func (m *Manager) resolveRunningPID(id string) int {
|
||||
}
|
||||
|
||||
func (m *Manager) isAlive(pid int) bool {
|
||||
return syscall.Kill(pid, 0) == nil
|
||||
return m.prober.isAlive(pid)
|
||||
}
|
||||
|
||||
func (m *Manager) removePID(id string) {
|
||||
|
||||
Reference in New Issue
Block a user