Task 04 added dependencies to toolkit and its git add line left out the lock file, so the driver stopped on an unclean tree. The lock change is folded into task 04's commit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
108 lines
4.4 KiB
Markdown
108 lines
4.4 KiB
Markdown
# M3b task 10: the `podman` argument lists
|
|
|
|
**Branch:** `m3b` (run `git switch m3b`; `git status --short` must be empty, otherwise stop)
|
|
**Commit subject:** `brokerd: the podman argument lists`
|
|
|
|
## Goal
|
|
|
|
Build, without running anything, the exact `podman` arguments for one call's container, and for
|
|
`http_fetch`'s egress proxy. Every argument is its own `OsString`: nothing is ever passed through a
|
|
shell, and the tool's arguments go on standard input, never on the command line. The lists are
|
|
tested as golden files, one argument per line. Spec section 6.
|
|
|
|
The container's name says whose call it is, so `RunSpec` gains the session and the call.
|
|
|
|
## Files
|
|
|
|
- Copy: `crates/brokerd/tests/podman_args.rs` and the six files
|
|
`crates/brokerd/tests/fixtures/podman/*.args`
|
|
- Create: `crates/brokerd/src/podman.rs`
|
|
- Modify: `crates/brokerd/src/runner.rs`, `crates/brokerd/src/lib.rs` (`pub mod podman;`),
|
|
`docs/implementer-log.md`
|
|
|
|
## `RunSpec` gains two fields
|
|
|
|
In `runner.rs`, `RunSpec` gets `session: SessionId` and `call: CallId` as its **first** two
|
|
fields (private, like the others), with getters:
|
|
|
|
```rust
|
|
pub fn session(&self) -> &SessionId;
|
|
pub fn call(&self) -> CallId;
|
|
```
|
|
|
|
`run` fills them from `decision.request().session.clone()` and `decision.request().call`. The
|
|
`compile_fail` doctest at the top of `runner.rs` builds a `RunSpec` with a struct literal: add the
|
|
two fields to it, first, so it still fails **only** because the fields are private:
|
|
|
|
```rust
|
|
//! session: proto::SessionId::new("s1").unwrap(),
|
|
//! call: proto::CallId(1),
|
|
```
|
|
|
|
## `podman.rs`
|
|
|
|
```rust
|
|
pub const EGRESS_MOUNT: &str = "/run/egress";
|
|
pub const EGRESS_SOCKET: &str = "/run/egress/egress.sock";
|
|
pub const TOOLKIT: &str = "/bin/toolkit";
|
|
|
|
/// "boxmaker-<session>-<call>-<n>"
|
|
pub fn container_name(session: &SessionId, call: CallId, n: u64) -> String;
|
|
|
|
/// The tool's container. `egress` is the call's egress directory, for `http_fetch` only.
|
|
pub fn tool_args(spec: &RunSpec, runner: &Runner, name: &str, egress: Option<&Path>) -> Vec<OsString>;
|
|
|
|
/// The egress proxy's container.
|
|
pub fn egress_args(runner: &Runner, name: &str, dir: &Path, hosts: &[String]) -> Vec<OsString>;
|
|
```
|
|
|
|
`tool_args`, in this order:
|
|
|
|
```
|
|
run --rm -i --name=<name> --label=boxmaker=tool --network=none
|
|
--read-only --cap-drop=all --security-opt=no-new-privileges --userns=keep-id
|
|
--pids-limit=<runner.pids> --memory=<runner.memory>
|
|
--tmpfs=/tmp:rw,size=64m,mode=1777
|
|
--volume=<path>:<path>:ro (or :rw when writable) one per spec.mounts(), in order
|
|
--volume=<egress dir>:/run/egress:rw only when `egress` is Some
|
|
<runner.image> /bin/toolkit <spec.tool().as_str()>
|
|
```
|
|
|
|
`egress_args`, in this order:
|
|
|
|
```
|
|
run -d --rm --name=<name>-egress --label=boxmaker=egress --network=<runner.egress_network>
|
|
--read-only --cap-drop=all --security-opt=no-new-privileges --userns=keep-id
|
|
--pids-limit=64 --memory=128m
|
|
--volume=<dir>:/run/egress:rw
|
|
<runner.image> /bin/toolkit egress-proxy --socket /run/egress/egress.sock --allow <hosts joined with ",">
|
|
```
|
|
|
|
The six hardening flags (`--read-only` to `--memory=…`) are the same in both, so write them once in
|
|
a private function. Build the `--volume=<dir>:…` argument as an `OsString` with `push`, so a
|
|
directory need not be UTF-8 (`dir.as_os_str()`), not with `format!` on `display()`.
|
|
|
|
## Steps
|
|
|
|
- [ ] **1. Copy.** `git switch m3b`, then
|
|
`cp docs/plans/M3b/files/crates/brokerd/tests/podman_args.rs crates/brokerd/tests/`,
|
|
`mkdir -p crates/brokerd/tests/fixtures/podman`,
|
|
`cp docs/plans/M3b/files/crates/brokerd/tests/fixtures/podman/*.args crates/brokerd/tests/fixtures/podman/`
|
|
- [ ] **2. See it fail.** `cargo test -p brokerd --test podman_args`. Expected: it does not
|
|
compile.
|
|
- [ ] **3. Write the code.** Run `cargo fmt --all`.
|
|
- [ ] **4. See it pass.** `cargo test -p brokerd --test podman_args --test runner`. Expected: 7
|
|
and 8 passed. `cargo test -p brokerd --doc`: every doctest passes.
|
|
- [ ] **5. Run the gate.** `make gate`. Expected last line: `gate: ok`.
|
|
- [ ] **6. Log and commit.** `git add crates/brokerd docs/implementer-log.md Cargo.lock && git commit`
|
|
|
|
## Done when
|
|
|
|
- `cargo test -p brokerd` and `cargo test -p brokerd --doc` pass; `make gate` prints `gate: ok`.
|
|
|
|
## Stop and report if
|
|
|
|
- A golden file disagrees with the lists above: report both, do not edit the golden file.
|
|
- Anything would put the tool's arguments (the command, the path, the URL) on `podman`'s command
|
|
line.
|