feat(process): add wait task surface
Co-authored-by: Virgil <virgil@lethean.io>
This commit is contained in:
parent
79e2ffa6ed
commit
85cd6dd7c8
5 changed files with 126 additions and 0 deletions
10
actions.go
10
actions.go
|
|
@ -89,6 +89,16 @@ type TaskProcessGet struct {
|
|||
ID string
|
||||
}
|
||||
|
||||
// TaskProcessWait waits for a managed process to finish through Core.PERFORM.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// c.PERFORM(process.TaskProcessWait{ID: "proc-1"})
|
||||
type TaskProcessWait struct {
|
||||
// ID identifies a managed process started by this service.
|
||||
ID string
|
||||
}
|
||||
|
||||
// TaskProcessOutput requests the captured output of a managed process through Core.PERFORM.
|
||||
//
|
||||
// Example:
|
||||
|
|
|
|||
|
|
@ -273,6 +273,26 @@ func TestGlobal_Output(t *testing.T) {
|
|||
assert.Contains(t, output, "global-output")
|
||||
}
|
||||
|
||||
func TestGlobal_Wait(t *testing.T) {
|
||||
svc, _ := newTestService(t)
|
||||
|
||||
old := defaultService.Swap(svc)
|
||||
defer func() {
|
||||
if old != nil {
|
||||
defaultService.Store(old)
|
||||
}
|
||||
}()
|
||||
|
||||
proc, err := Start(context.Background(), "echo", "global-wait")
|
||||
require.NoError(t, err)
|
||||
|
||||
info, err := Wait(proc.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, proc.ID, info.ID)
|
||||
assert.Equal(t, StatusExited, info.Status)
|
||||
assert.Equal(t, 0, info.ExitCode)
|
||||
}
|
||||
|
||||
func TestGlobal_Signal(t *testing.T) {
|
||||
svc, _ := newTestService(t)
|
||||
|
||||
|
|
|
|||
|
|
@ -114,6 +114,19 @@ func Output(id string) (string, error) {
|
|||
return svc.Output(id)
|
||||
}
|
||||
|
||||
// Wait blocks until a managed process exits and returns its final snapshot.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// info, err := process.Wait("proc-1")
|
||||
func Wait(id string) (Info, error) {
|
||||
svc := Default()
|
||||
if svc == nil {
|
||||
return Info{}, ErrServiceNotInitialized
|
||||
}
|
||||
return svc.Wait(id)
|
||||
}
|
||||
|
||||
// List returns all processes from the default service.
|
||||
//
|
||||
// Example:
|
||||
|
|
|
|||
29
service.go
29
service.go
|
|
@ -529,6 +529,24 @@ func (s *Service) Output(id string) (string, error) {
|
|||
return proc.Output(), nil
|
||||
}
|
||||
|
||||
// Wait blocks until a managed process exits and returns its final snapshot.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// info, err := svc.Wait("proc-1")
|
||||
func (s *Service) Wait(id string) (Info, error) {
|
||||
proc, err := s.Get(id)
|
||||
if err != nil {
|
||||
return Info{}, err
|
||||
}
|
||||
|
||||
if err := proc.Wait(); err != nil {
|
||||
return proc.Info(), err
|
||||
}
|
||||
|
||||
return proc.Info(), nil
|
||||
}
|
||||
|
||||
// findByPID locates a managed process by operating-system PID.
|
||||
func (s *Service) findByPID(pid int) *Process {
|
||||
s.mu.RLock()
|
||||
|
|
@ -672,6 +690,17 @@ func (s *Service) handleTask(c *core.Core, task core.Task) core.Result {
|
|||
}
|
||||
|
||||
return core.Result{Value: proc.Info(), OK: true}
|
||||
case TaskProcessWait:
|
||||
if m.ID == "" {
|
||||
return core.Result{Value: coreerr.E("Service.handleTask", "task process wait requires an id", nil), OK: false}
|
||||
}
|
||||
|
||||
info, err := s.Wait(m.ID)
|
||||
if err != nil {
|
||||
return core.Result{Value: err, OK: false}
|
||||
}
|
||||
|
||||
return core.Result{Value: info, OK: true}
|
||||
case TaskProcessOutput:
|
||||
if m.ID == "" {
|
||||
return core.Result{Value: coreerr.E("Service.handleTask", "task process output requires an id", nil), OK: false}
|
||||
|
|
|
|||
|
|
@ -597,6 +597,41 @@ func TestService_Output(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestService_Wait(t *testing.T) {
|
||||
t.Run("returns final info on success", func(t *testing.T) {
|
||||
svc, _ := newTestService(t)
|
||||
|
||||
proc, err := svc.Start(context.Background(), "echo", "waited")
|
||||
require.NoError(t, err)
|
||||
|
||||
info, err := svc.Wait(proc.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, proc.ID, info.ID)
|
||||
assert.Equal(t, StatusExited, info.Status)
|
||||
assert.Equal(t, 0, info.ExitCode)
|
||||
})
|
||||
|
||||
t.Run("returns error on unknown id", func(t *testing.T) {
|
||||
svc, _ := newTestService(t)
|
||||
|
||||
_, err := svc.Wait("nonexistent")
|
||||
assert.ErrorIs(t, err, ErrProcessNotFound)
|
||||
})
|
||||
|
||||
t.Run("returns info alongside failure", func(t *testing.T) {
|
||||
svc, _ := newTestService(t)
|
||||
|
||||
proc, err := svc.Start(context.Background(), "sh", "-c", "exit 7")
|
||||
require.NoError(t, err)
|
||||
|
||||
info, err := svc.Wait(proc.ID)
|
||||
require.Error(t, err)
|
||||
assert.Equal(t, proc.ID, info.ID)
|
||||
assert.Equal(t, StatusExited, info.Status)
|
||||
assert.Equal(t, 7, info.ExitCode)
|
||||
})
|
||||
}
|
||||
|
||||
func TestService_OnShutdown(t *testing.T) {
|
||||
t.Run("kills all running processes", func(t *testing.T) {
|
||||
svc, _ := newTestService(t)
|
||||
|
|
@ -770,6 +805,25 @@ func TestService_OnStartup(t *testing.T) {
|
|||
assert.Equal(t, StatusKilled, proc.Status)
|
||||
})
|
||||
|
||||
t.Run("registers process.wait task", func(t *testing.T) {
|
||||
svc, c := newTestService(t)
|
||||
|
||||
err := svc.OnStartup(context.Background())
|
||||
require.NoError(t, err)
|
||||
|
||||
proc, err := svc.Start(context.Background(), "echo", "action-wait")
|
||||
require.NoError(t, err)
|
||||
|
||||
result := c.PERFORM(TaskProcessWait{ID: proc.ID})
|
||||
require.True(t, result.OK)
|
||||
|
||||
info, ok := result.Value.(Info)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, proc.ID, info.ID)
|
||||
assert.Equal(t, StatusExited, info.Status)
|
||||
assert.Equal(t, 0, info.ExitCode)
|
||||
})
|
||||
|
||||
t.Run("registers process.list task", func(t *testing.T) {
|
||||
svc, c := newTestService(t)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue