From 038730d432f2bcb223bc4762295524a29c240189 Mon Sep 17 00:00:00 2001 From: dawn Date: Tue, 15 Sep 2026 05:02:08 +0300 Subject: [PATCH] spindle/engines/nixery: unblock tailStep on context done and bound wait Signed-off-by: dawn --- spindle/engines/nixery/engine.go | 36 +++++++++++++++++++------------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/spindle/engines/nixery/engine.go b/spindle/engines/nixery/engine.go index 942cab2d..484ce6ba 100644 --- a/spindle/engines/nixery/engine.go +++ b/spindle/engines/nixery/engine.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "log/slog" + "net" "path" "runtime" "sync" @@ -477,21 +478,24 @@ func (e *Engine) RunStep(ctx context.Context, wid models.WorkflowId, w *models.W case <-tailDone: case <-ctx.Done(): - // cleanup will be handled by DestroyWorkflow, since - // Docker doesn't provide an API to kill an exec run - // (sure, we could grab the PID and kill it ourselves, - // but that's wasted effort) - e.l.Warn("step timed out", "step", step.Name()) - - <-tailDone - - return engine.ErrTimedOut + if errors.Is(ctx.Err(), context.DeadlineExceeded) { + e.l.Warn("step timed out", "step", step.Name()) + } + select { + case <-tailDone: + case <-time.After(2 * time.Second): + } + if errors.Is(ctx.Err(), context.DeadlineExceeded) { + return engine.ErrTimedOut + } + return ctx.Err() } - select { - case <-ctx.Done(): + if ctx.Err() != nil { + if errors.Is(ctx.Err(), context.DeadlineExceeded) { + return engine.ErrTimedOut + } return ctx.Err() - default: } execInspectResp, err := e.docker.ContainerExecInspect(ctx, mkExecResp.ID) @@ -533,19 +537,23 @@ func (e *Engine) tailStep(ctx context.Context, wfLogger models.WorkflowLogger, e return nil } - // This actually *starts* the command. Thanks, Docker! logs, err := e.docker.ContainerExecAttach(ctx, execID, container.ExecAttachOptions{}) if err != nil { return err } defer logs.Close() + stop := context.AfterFunc(ctx, func() { + logs.Close() + }) + defer stop() + _, err = stdcopy.StdCopy( wfLogger.DataWriter(stepIdx, "stdout"), wfLogger.DataWriter(stepIdx, "stderr"), logs.Reader, ) - if err != nil && err != io.EOF && !errors.Is(err, context.DeadlineExceeded) { + if err != nil && err != io.EOF && !errors.Is(err, context.DeadlineExceeded) && !errors.Is(err, context.Canceled) && !errors.Is(err, net.ErrClosed) && ctx.Err() == nil { return fmt.Errorf("failed to copy logs: %w", err) } -- 2.51.2