# M3b task 12: the egress proxy's lifecycle **Branch:** `m3b` (run `git switch m3b`; `git status --short` must be empty, otherwise stop) **Commit subject:** `brokerd: start and remove the egress proxy for http_fetch` ## Goal For an `http_fetch` call, the tool's container still has no network. Before it runs, `brokerd` makes a directory for this call, starts the egress proxy (task 08) in its own container with a network and that directory mounted, and waits for the proxy's socket. The tool's container gets the same directory. Afterwards, **on every path**, the proxy's container and the directory are removed. Spec section 6, "`http_fetch`". ## Files - Copy: `crates/brokerd/tests/container_egress.rs` - Modify: `crates/brokerd/src/container.rs`, `docs/implementer-log.md` ## Interfaces (added to `container.rs`) ```rust pub const EGRESS_WAIT: Duration = Duration::from_secs(5); impl Podman { /// The same runtime with another wait for the proxy's socket, for tests. pub fn with_egress_wait(self, egress_wait: Duration) -> Podman; } ``` `Podman` gains a private field `egress_wait`, set to `EGRESS_WAIT` by `new`. ## `run`, now After step 2 of task 11 (name, input, limit): - `spec.egress()` is `None` → exactly as before: `tool_args(…, None)` and run the container. - `spec.egress()` is `Some(hosts)` → start the proxy (below), and on success run the container with `tool_args(spec, &runner, &name, Some(&dir))`. ### Starting the proxy: every step and exit Use a **guard**: a private struct holding the proxy's container name (`-egress`) and the directory, whose `Drop` runs `podman rm -f -egress` (the helper from task 11) and then `remove_dir_all(dir)` (log an error other than `NotFound`). Create the guard **first**, before anything can fail, so that every return below cleans up by dropping it, and keep it alive until the tool's container has finished. 1. `dir = egress_dir.join(&name)`. Make `egress_dir` (and its parents) with mode 0700 (`DirBuilder::new().recursive(true).mode(0o700)`), then set its mode to 0700 anyway (it may have existed). If `dir` already exists, it was left by a crash: `remove_dir_all` it (a `NotFound` is fine). Then create `dir` with mode 0700, not recursively. Any failure → log `brokerd: cannot make {dir}: {e}` + `"\n"` + `RUNBOOK`, return `Err(RunError::Unavailable(CANNOT_START))`. 2. Run `podman` with `podman::egress_args(&runner, &name, &dir, hosts)`, standard input and output null, standard error piped, with `.output()`. It does not succeed → log `brokerd: podman could not start {name}-egress: {stderr}` + `"\n"` + `RUNBOOK`, return `Unavailable(CANNOT_START)`. It cannot be started at all → the same log as task 11 step 3, and the same answer. 3. Wait until `dir.join("egress.sock")` exists, checking every 20 ms, for at most `egress_wait`. It does not appear → log `brokerd: {name}-egress did not make its socket within {ms} ms` + `"\n"` + `RUNBOOK`, return `Unavailable(CANNOT_START)`. 4. Return the guard. Run the tool's container as in task 11. Whatever it answers, the guard is dropped after it, removing the proxy and the directory. So for a successful call the fake sees exactly three `podman` calls: `run -d …` (the proxy), `run --rm -i …` (the tool), `rm -f -egress`. For a tool past its time limit it sees the proxy's run, the tool's run, `kill `, `rm -f `, then `rm -f -egress`. ## Steps - [ ] **1. Copy.** `git switch m3b`, then `cp docs/plans/M3b/files/crates/brokerd/tests/container_egress.rs crates/brokerd/tests/` - [ ] **2. See it fail.** `cargo test -p brokerd --test container_egress`. Expected: it does not compile (`with_egress_wait` does not exist). - [ ] **3. Write the code.** Run `cargo fmt --all`. `container.rs` must stay under 500 lines. - [ ] **4. See it pass.** `cargo test -p brokerd --test container_egress --test container`. Expected: 6 and 11 passed. Run them ten times; they must pass every time. - [ ] **5. Walk the exits.** For each of the four steps, say how the guard cleans up after it. - [ ] **6. Run the gate.** `make gate`. Expected last line: `gate: ok`. - [ ] **7. Log and commit.** `git add crates/brokerd docs/implementer-log.md Cargo.lock && git commit` ## Done when - Both suites pass ten times running; `make gate` prints `gate: ok`. ## Stop and report if - A path leaves the proxy's container or the directory behind, and a guard does not fix it. - The tool's container would get a network.