waitForProcess waits for process completion using the blocking process output API instead of polling. waitForProcess blocks until the process exits or the context expires. On any error (timeout or transport), it tries a non-blocking snapshot to recover. Total wall time may exceed timeout by up to sn
( ctx context.Context, parentCtx context.Context, conn workspacesdk.AgentConn, processID string, timeout time.Duration, )
| 247 | // non-blocking snapshot to recover. Total wall time may exceed |
| 248 | // timeout by up to snapshotTimeout if recovery is needed. |
| 249 | func waitForProcess( |
| 250 | ctx context.Context, |
| 251 | parentCtx context.Context, |
| 252 | conn workspacesdk.AgentConn, |
| 253 | processID string, |
| 254 | timeout time.Duration, |
| 255 | ) ExecuteResult { |
| 256 | // Block until the process exits or the context is |
| 257 | // canceled. |
| 258 | resp, err := conn.ProcessOutput(ctx, processID, &workspacesdk.ProcessOutputOptions{ |
| 259 | Wait: true, |
| 260 | }) |
| 261 | if err != nil { |
| 262 | origErr := err |
| 263 | timedOut := ctx.Err() != nil |
| 264 | |
| 265 | // Fetch a snapshot with a fresh context. The blocking |
| 266 | // request may have failed due to a context timeout or |
| 267 | // a transport error (e.g. the server's WriteTimeout |
| 268 | // killed the connection). Either way, the process may |
| 269 | // still have output available. |
| 270 | bgCtx, bgCancel := context.WithTimeout( |
| 271 | parentCtx, |
| 272 | snapshotTimeout, |
| 273 | ) |
| 274 | defer bgCancel() |
| 275 | resp, err = conn.ProcessOutput(bgCtx, processID, nil) |
| 276 | if err != nil { |
| 277 | errMsg := fmt.Sprintf("get process output: %v; use process_output with ID %s to retry", origErr, processID) |
| 278 | if timedOut { |
| 279 | errMsg = fmt.Sprintf("command timed out after %s; failed to get output: %v", timeout, err) |
| 280 | } |
| 281 | return ExecuteResult{ |
| 282 | Success: false, |
| 283 | ExitCode: -1, |
| 284 | Error: errMsg, |
| 285 | BackgroundProcessID: processID, |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | // Snapshot succeeded. If the process finished, return |
| 290 | // its real result (transparent recovery). |
| 291 | if !resp.Running { |
| 292 | exitCode := 0 |
| 293 | if resp.ExitCode != nil { |
| 294 | exitCode = *resp.ExitCode |
| 295 | } |
| 296 | output := truncateOutput(resp.Output) |
| 297 | return ExecuteResult{ |
| 298 | Success: exitCode == 0, |
| 299 | Output: output, |
| 300 | ExitCode: exitCode, |
| 301 | Truncated: resp.Truncated, |
| 302 | } |
| 303 | } |
| 304 | |
| 305 | // Process still running, return partial output. |
| 306 | output := truncateOutput(resp.Output) |
no test coverage detected