feat: execute durable client commands through supervisor
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
//go:build !windows
|
||||
|
||||
package windows
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/rvbox/rvbox/internal/client/supervisor"
|
||||
)
|
||||
|
||||
func stdinLineEnding() []byte { return []byte{'\n'} }
|
||||
|
||||
// NewSupervisor returns the deterministic local adapter used by tests and by
|
||||
// non-Windows development builds. The v1 client does not advertise this
|
||||
// adapter as a supported Unix client; it exists so protocol/runtime tests can
|
||||
// exercise real child processes without a Windows host.
|
||||
func NewSupervisor(options NativeOptions) (supervisor.Supervisor, error) {
|
||||
return newExecSupervisor(options), nil
|
||||
}
|
||||
|
||||
func (manager *execSupervisor) Start(ctx context.Context, spec supervisor.StartSpec) (supervisor.Process, error) {
|
||||
return manager.startPortable(ctx, spec)
|
||||
}
|
||||
|
||||
func (manager *execSupervisor) Signal(ctx context.Context, process supervisor.Process, signal supervisor.SignalKind) (supervisor.SignalOutcome, error) {
|
||||
if process == nil {
|
||||
return supervisor.SignalOutcome{}, ErrProcessNotFound
|
||||
}
|
||||
executable, ok := process.(*execProcess)
|
||||
if !ok {
|
||||
return supervisor.SignalOutcome{}, ErrProcessNotFound
|
||||
}
|
||||
if signal != supervisor.SignalTerm && signal != supervisor.SignalKill {
|
||||
return supervisor.SignalOutcome{}, errors.New("unsupported signal")
|
||||
}
|
||||
if signal == supervisor.SignalTerm {
|
||||
if executable.cmd.Process != nil {
|
||||
_ = executable.cmd.Process.Signal(os.Interrupt)
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return supervisor.SignalOutcome{}, ctx.Err()
|
||||
case <-time.After(manager.options.WindowsTermGrace):
|
||||
}
|
||||
}
|
||||
if err := executable.terminate(1); err != nil {
|
||||
return supervisor.SignalOutcome{}, err
|
||||
}
|
||||
return supervisor.SignalOutcome{Delivered: true, Escalated: signal == supervisor.SignalTerm, Detail: "process terminated", ObservedAt: manager.options.Now()}, nil
|
||||
}
|
||||
|
||||
func (manager *execSupervisor) Snapshot(ctx context.Context, process supervisor.Process) (supervisor.ResourceSnapshot, error) {
|
||||
if process == nil {
|
||||
return supervisor.ResourceSnapshot{}, ErrProcessNotFound
|
||||
}
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return supervisor.ResourceSnapshot{}, ctx.Err()
|
||||
default:
|
||||
}
|
||||
return supervisor.ResourceSnapshot{ProcessCount: 1, ObservedAt: manager.options.Now(), Complete: false, Detail: "portable adapter does not expose aggregate process accounting"}, nil
|
||||
}
|
||||
|
||||
func (manager *execSupervisor) StopAll(_ context.Context) error {
|
||||
manager.mu.Lock()
|
||||
processes := make([]*execProcess, 0, len(manager.active))
|
||||
for _, process := range manager.active {
|
||||
processes = append(processes, process)
|
||||
}
|
||||
manager.mu.Unlock()
|
||||
for _, process := range processes {
|
||||
if err := process.terminate(1); err != nil && !errors.Is(err, os.ErrProcessDone) {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user