package orchestrator import ( "net" "sort" "strings" "sync" "time" ) func instanceIsActive(inst *InstanceInternal) bool { if inst == nil { return false } if inst.cmd != nil { return isProcessAlive(inst.cmd.PID()) } return inst.Status == "starting" || inst.Status == "running" || inst.Status == "stopping" } func waitForProcessExit(pid int, timeout time.Duration) bool { if pid <= 0 { return true } deadline := time.Now().Add(timeout) for time.Now().Before(deadline) { if !isProcessAlive(pid) { return true } time.Sleep(150 * time.Millisecond) } return !isProcessAlive(pid) } var processAliveFunc = processAlive func isProcessAlive(pid int) bool { return processAliveFunc(pid) } func isPortAvailable(port string) bool { ln, err := net.Listen("tcp", ":"+port) if err != nil { return false } _ = ln.Close() return true } func tailLogLine(logs string) string { if logs == "" { return "" } lines := strings.Split(strings.TrimSpace(logs), "\n") for i := len(lines) - 1; i >= 0; i-- { line := strings.TrimSpace(lines[i]) if line == "" { continue } const max = 220 if len(line) > max { return line[len(line)-max:] } return line } return "" } func mergeEnvWithOverrides(base []string, overrides map[string]string) []string { out := make([]string, 0, len(base)+len(overrides)) for _, kv := range base { key, _, ok := strings.Cut(kv, "=") if !ok { continue } if _, exists := overrides[key]; exists { continue } out = append(out, kv) } keys := make([]string, 0, len(overrides)) for k := range overrides { keys = append(keys, k) } sort.Strings(keys) for _, k := range keys { out = append(out, k+"="+overrides[k]) } return out } func filterEnvWithPrefixes(env []string, prefixes ...string) []string { out := make([]string, 0, len(env)) for _, e := range env { skip := false for _, p := range prefixes { if strings.HasPrefix(e, p) { skip = true break } } if !skip { out = append(out, e) } } return out } type ringBuffer struct { mu sync.Mutex data []byte max int } func newRingBuffer(max int) *ringBuffer { return &ringBuffer{max: max, data: make([]byte, 0, max)} } func (rb *ringBuffer) Write(p []byte) (int, error) { rb.mu.Lock() defer rb.mu.Unlock() rb.data = append(rb.data, p...) if len(rb.data) > rb.max { rb.data = rb.data[len(rb.data)-rb.max:] } return len(p), nil } func (rb *ringBuffer) String() string { rb.mu.Lock() defer rb.mu.Unlock() return string(rb.data) }