The third attempt filled five functions, then ran out of room planning all of run in one turn. run and run_container are now given; spawn, Io::start, Io::finish and answer are small todo!()s. Checked fillable: 11 of 11 passed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
4.6 KiB
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)
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()isNone→ exactly as before:tool_args(…, None)andrun_container.spec.egress()isSome(hosts)→ start the proxy (below), and on success callrun_containerwithtool_args(spec, &runner, &name, Some(&dir)).
run_container (from task 11) does steps 3 to 7 for any argument list, so the only change to
run is this choice. Put the proxy's start in its own function (start_egress), returning the
guard or the error.
Starting the proxy: every step and exit
Use a guard: a private struct holding the proxy's container name (<name>-egress) and the
directory, whose Drop runs podman rm -f <name>-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.
dir = egress_dir.join(&name). Makeegress_dir(and its parents) with mode 0700 (DirBuilder::new().recursive(true).mode(0o700)), then set its mode to 0700 anyway (it may have existed). Ifdiralready exists, it was left by a crash:remove_dir_allit (aNotFoundis fine). Then createdirwith mode 0700, not recursively. Any failure → logbrokerd: cannot make {dir}: {e}+"\n"+RUNBOOK, returnErr(RunError::Unavailable(CANNOT_START)).- Run
podmanwithpodman::egress_args(&runner, &name, &dir, hosts), standard input and output null, standard error piped, with.output(). It does not succeed → logbrokerd: podman could not start {name}-egress: {stderr}+"\n"+RUNBOOK, returnUnavailable(CANNOT_START). It cannot be started at all → the same log as task 11 step 3, and the same answer. - Wait until
dir.join("egress.sock")exists, checking every 20 ms, for at mostegress_wait. It does not appear → logbrokerd: {name}-egress did not make its socket within {ms} ms+"\n"+RUNBOOK, returnUnavailable(CANNOT_START). - 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 <name>-egress. For a tool past its time limit it sees the
proxy's run, the tool's run, kill <name>, rm -f <name>, then rm -f <name>-egress.
Steps
- 1. Copy.
git switch m3b, thencp 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_waitdoes not exist). - 3. Write the code. Run
cargo fmt --all.container.rsmust 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 gateprintsgate: 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.