M3b plan: task 11 starts from a compiling skeleton of container.rs
Two sessions ended with nothing written, each out of room while planning the whole file in one turn. The skeleton has the signatures, the constants and the steps as comments; the task says to fill one function at a time. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
//! The Podman runtime: one fresh container per call, with the limits of `[runner]`. Whatever the
|
||||
//! tool prints is the result's content; every failure is a fixed sentence, and what Podman itself
|
||||
//! said goes only to `brokerd`'s log. M3b spec, section 6.
|
||||
//!
|
||||
//! SKELETON from task 11: replace every `todo!()`, one function at a time, running
|
||||
//! `cargo check -p brokerd` after each. Then delete this paragraph.
|
||||
|
||||
use std::io::{Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::{Child, Command, ExitStatus, Stdio};
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
use crate::config::Runner;
|
||||
use crate::podman;
|
||||
use crate::runner::{RunError, RunOutput, RunSpec, Runtime};
|
||||
|
||||
pub const RUNBOOK: &str = "see docs/runbook.md#runner-unavailable";
|
||||
pub const COULD_NOT_RUN: &str = "the tool could not run";
|
||||
pub const CANNOT_START: &str = "the tool runner could not start the container";
|
||||
pub const KILLED: &str = "the tool was stopped: it ran out of memory or was killed";
|
||||
pub const TIMED_OUT: &str = "the tool ran past its time limit";
|
||||
pub const UNEXPECTED: &str = "the tool failed with an unexpected status";
|
||||
/// How often a running container is checked.
|
||||
pub const POLL: Duration = Duration::from_millis(50);
|
||||
/// How much of Podman's standard error is kept for the log.
|
||||
pub const STDERR_KEPT: usize = 4096;
|
||||
|
||||
/// A log sink. Call it as `(self.log)("a line")`.
|
||||
pub type Log = Arc<dyn Fn(&str) + Send + Sync>;
|
||||
|
||||
pub struct Podman {
|
||||
runner: Runner,
|
||||
egress_dir: PathBuf,
|
||||
log: Log,
|
||||
next: AtomicU64,
|
||||
}
|
||||
|
||||
impl Podman {
|
||||
/// `egress_dir` is `Config::egress_dir()`; task 12 uses it.
|
||||
pub fn new(runner: Runner, egress_dir: PathBuf, log: Log) -> Podman {
|
||||
todo!("store the four fields; `next` starts at 0")
|
||||
}
|
||||
|
||||
/// Where `http_fetch` calls get their directories. Public, so the field counts as read.
|
||||
pub fn egress_dir(&self) -> &Path {
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Run one short `podman` command (`kill`, `rm -f`) whose result only matters for the log.
|
||||
pub(crate) fn podman(&self, args: &[&str]) {
|
||||
todo!("`.status()` with the three standard streams null; log a line if it is not a success")
|
||||
}
|
||||
|
||||
/// Wait for `child` until `limit` has passed since `started`. `Some(status)` if it ended;
|
||||
/// `None` if it ran too long, after `podman kill <name>`, `podman rm -f <name>`,
|
||||
/// `child.kill()` and `child.wait()` (step 5).
|
||||
fn wait(&self, child: &mut Child, name: &str, started: Instant, limit: Duration) -> Option<ExitStatus> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Runtime for Podman {
|
||||
fn run(&self, spec: &RunSpec) -> Result<RunOutput, RunError> {
|
||||
// 1. `n` and `name`.
|
||||
// 2. `args`, `input`, `limit`.
|
||||
// 3. Spawn podman with all three standard streams piped; a failure is Unavailable.
|
||||
// 4. Three threads: write `input` then drop stdin; `read_capped` stdout with the cap;
|
||||
// `read_capped` stderr with STDERR_KEPT.
|
||||
// 5. `self.wait(…)`.
|
||||
// 6. Join the three threads (`join().ok()`, `unwrap_or_default()`).
|
||||
// 7. The answer, by the table in the task.
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
/// Everything `from` gives, keeping the first `cap` bytes; `true` if there was more. Reads on past
|
||||
/// the cap, so the writer is never blocked or broken by a closed pipe. No indexing: `buf.get(..n)`.
|
||||
fn read_capped(from: impl Read, cap: usize) -> (Vec<u8>, bool) {
|
||||
todo!()
|
||||
}
|
||||
Reference in New Issue
Block a user