Start a watch stream that emits current state and live events. The stream first emits a snapshot of all currently-running managed sandboxes (initial state sync), then delivers live container events as they arrive from the Podman event stream. # Reconnection contract The returned stream is **single-use**. When the Podman event connection drops (daemon restart, socket error, or clean shutdown),
(client: PodmanClient)
| 70 | /// would race with `watch_loop`'s retry and produce duplicate initial-sync |
| 71 | /// events that corrupt the server's sandbox index. |
| 72 | pub async fn start_watch(client: PodmanClient) -> Result<WatchStream, PodmanApiError> { |
| 73 | let (tx, rx) = mpsc::channel::<Result<WatchSandboxesEvent, ComputeDriverError>>(256); |
| 74 | |
| 75 | // 1. Subscribe to events first so we don't miss any during the list. |
| 76 | let mut event_rx = client.events_stream(LABEL_MANAGED_FILTER).await?; |
| 77 | |
| 78 | // 2. List existing containers for initial state sync. |
| 79 | let existing = client.list_containers(LABEL_MANAGED_FILTER).await?; |
| 80 | |
| 81 | for entry in &existing { |
| 82 | // For running containers, use inspect to get full state including |
| 83 | // health check status — matching the same condition derivation used |
| 84 | // for live events. |
| 85 | if entry.state == "running" { |
| 86 | match client.inspect_container(&entry.id).await { |
| 87 | Ok(inspect) => { |
| 88 | if let Some(sandbox) = driver_sandbox_from_inspect(&inspect) { |
| 89 | if tx.send(Ok(sandbox_event(sandbox))).await.is_err() { |
| 90 | return Err(PodmanApiError::Connection( |
| 91 | "watch receiver dropped during initial sync".into(), |
| 92 | )); |
| 93 | } |
| 94 | continue; |
| 95 | } |
| 96 | } |
| 97 | Err(e) => { |
| 98 | warn!( |
| 99 | container_id = %entry.id, |
| 100 | error = %e, |
| 101 | "Failed to inspect running container during initial sync, falling back to list entry" |
| 102 | ); |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | if let Some(event) = driver_sandbox_from_list_entry(entry).map(sandbox_event) |
| 107 | && tx.send(Ok(event)).await.is_err() |
| 108 | { |
| 109 | return Err(PodmanApiError::Connection( |
| 110 | "watch receiver dropped during initial sync".into(), |
| 111 | )); |
| 112 | } |
| 113 | } |
| 114 | |
| 115 | // 3. Stream live events (buffered during the list operation above). |
| 116 | |
| 117 | tokio::spawn(async move { |
| 118 | while let Some(result) = event_rx.recv().await { |
| 119 | match result { |
| 120 | Ok(event) => { |
| 121 | if let Some(we) = map_podman_event(&event, &client).await |
| 122 | && tx.send(Ok(we)).await.is_err() |
| 123 | { |
| 124 | return; |
| 125 | } |
| 126 | } |
| 127 | Err(e) => { |
| 128 | if tx |
| 129 | .send(Err(ComputeDriverError::Message(e.to_string()))) |
no test coverage detected