mirror of
https://github.com/vhaudiquet/ecrou.git
synced 2026-11-12 22:22:44 +00:00
feat(cli): docker-style run flags and local rootfs references
The run interface read like a library's. `ecrou run -it alpine` now hands the guest a terminal: with no console output file, libkrun wires the guest console to the process's own stdio and raw-modes the tty. The parent saves and restores the termios around the run, because libkrun's own restore lives in the child's exit observers, which a SIGKILLed machine never runs. A tty run refuses to start without a terminal on stdin and stdout, and --capture refuses to combine with -t: the payload's streams cannot land in files and on the terminal at once. -i without -t keeps the machine's stdin open through pipes: libkrun exposes the non-tty streams as virtio console ports (krun-stdin, krun-stdout, krun-stderr), and the guest init points the payload's stdio at whichever port nodes exist. `echo hi | ecrou run -i alpine -- cat` crosses the machine; a stream the host left on a tty has no port node and stays on the console. -v (--volume HOST:GUEST[:ro]) replaces --share, and -w aliases --workdir. The global -v/--verbose loses its short flag to make room; verbose stays reachable by its long form. A reference starting with ./, ../ or / names a local rootfs archive: read in place on every run, never downloaded, never cached as a pristine entry. Provisioning over one persists into a derived cache entry keyed by size and mtime, so the caller's file is never overwritten and an edited archive lands in a fresh entry.
This commit is contained in:
1 parent
719a8209e0
commit
4a0c613ecb
9 files changed
+569
-72
No files matched your search
@@ -132,8 +132,9 @@ of the image (a `PreparedRootfs`, TempDir-backed): it exists only for
|
||||
the run and dies with it. Persistence crosses a workspace share or it
|
||||
does not happen.
|
||||
|
||||
**Workspace shares.** `--share HOST:GUEST[:ro]`, one virtiofs share per
|
||||
entry (`krun_add_virtiofs`, `krun_set_root` keeps the rootfs itself).
|
||||
**Workspace shares.** `-v HOST:GUEST[:ro]` (`--volume`), one virtiofs
|
||||
share per entry (`krun_add_virtiofs`, `krun_set_root` keeps the rootfs
|
||||
itself).
|
||||
The host side is plain fs; the guest init mounts each share at its
|
||||
guest path. There is no implicit share: an empty sandbox is the
|
||||
default, and the consumer chooses what crosses. This is deliberate; a
|
||||
@@ -174,15 +175,36 @@ machine with it. No orphan survives. This is stronger than `docker
|
||||
exec`'s cancel, which kills only the local client; the tests keep it
|
||||
true (T4).
|
||||
|
||||
**Console.** Guest output reaches real stdout through a console file
|
||||
the parent streams while the machine runs, with a final drain after
|
||||
the reap. The direct stdio wiring does not survive contact with
|
||||
reality: with a tty on fd 0 libkrun's terminal setup stops a
|
||||
**Console.** Three wirings, chosen by the run's flags.
|
||||
|
||||
Headless (the default): guest output reaches real stdout through a
|
||||
console file the parent streams while the machine runs, with a final
|
||||
drain after the reap. The direct stdio wiring does not survive contact
|
||||
with reality: with a tty on fd 0 libkrun's terminal setup stops a
|
||||
background caller with SIGTTOU, and without one its console bytes go
|
||||
to the log sink, not the terminal. The child therefore unwires fd 0 to
|
||||
/dev/null (v1 carries no guest input; a Ctrl-C to the process group is
|
||||
still forwarded into the guest). There is no QEMU monitor to mux;
|
||||
`--verbose` maps to `krun_set_log_level`.
|
||||
/dev/null (no guest input; a Ctrl-C to the process group is still
|
||||
forwarded into the guest).
|
||||
|
||||
`-t`: the machine owns the terminal. With no console output file set,
|
||||
libkrun wires the guest console to the process's own stdin and stdout,
|
||||
puts the tty in raw mode, and restores it through its exit observers.
|
||||
The parent additionally saves and restores the termios around the run:
|
||||
the observers do not run in a SIGKILLed machine. Input is the typed
|
||||
keyboard; Ctrl-C reaches the guest as a byte (raw mode has no ISIG)
|
||||
and the guest's line discipline signals its own foreground job. The
|
||||
run refuses to start without a terminal on stdin and stdout, and
|
||||
`--capture` is refused with `-t` (the payload's streams cannot land in
|
||||
files and on the terminal at once).
|
||||
|
||||
`-i` without `-t`: pipes. libkrun still owns the process stdio, and
|
||||
its non-tty streams surface in the guest as virtio console ports
|
||||
(`krun-stdin`, `krun-stdout`, `krun-stderr` under `/dev/virtio-ports`);
|
||||
the guest init re-points the payload's stdio onto whichever port nodes
|
||||
exist, so `echo hi | ecrou run -i alpine -- cat` crosses the machine.
|
||||
A stream the host left on a tty has no port node and stays on the
|
||||
console. There is no QEMU monitor to mux; `--verbose` maps to
|
||||
`krun_set_log_level`.
|
||||
|
||||
**Networking.** TSI, libkrun's default: the guest has no interface, and
|
||||
its socket calls are proxied on the host's stack by the VMM process.
|
||||
@@ -232,7 +254,8 @@ absent kernel entries only.
|
||||
```rust
|
||||
pub struct Share { pub host: PathBuf, pub guest: String, pub read_only: bool }
|
||||
pub struct RunOptions {
|
||||
pub image: String, // distro ref or OCI image ref
|
||||
pub image: String, // distro ref, OCI image ref, or local
|
||||
// rootfs archive path (./x.tar.gz)
|
||||
pub arch: String, // empty = host arch
|
||||
pub memory: String, // default "1G"
|
||||
pub workspace: Option<Share>, // the one rw share consumers name
|
||||
@@ -241,6 +264,8 @@ pub struct RunOptions {
|
||||
pub workdir: String, // guest cwd; empty = /
|
||||
pub env: Vec<(String, String)>, // composed envp; host env never inherited
|
||||
pub hostname: String, // empty = generated ecrou-vm-XXXXXX
|
||||
pub interactive: bool, // host stdin feeds the guest
|
||||
pub tty: bool, // console on the terminal (implies interactive)
|
||||
pub log_level: LogLevel, // Quiet | Normal | Verbose
|
||||
pub capture: Option<PathBuf>, // payload stdout/stderr into files
|
||||
}
|
||||
@@ -273,16 +298,24 @@ ecrou provision [OPTIONS] <IMAGE> [-- COMMAND...]
|
||||
ecrou probe
|
||||
```
|
||||
|
||||
The image is a distro name (`alpine`, `ubuntu:noble`), an OCI
|
||||
reference (`ghcr.io/org/image:tag`), or a local rootfs archive named
|
||||
with a path (`./rootfs.tar.gz`, `/tmp/rootfs.tar`): a path reference is
|
||||
read in place on every run, never downloaded and never cached as a
|
||||
pristine entry.
|
||||
|
||||
| Flag | Default | Description |
|
||||
|------|---------|-------------|
|
||||
| `--share <HOST:GUEST[:ro]>` | none | Workspace share; repeatable |
|
||||
| `-i, --interactive` | false | Keep the machine's stdin open: the host's stdin feeds the guest |
|
||||
| `-t, --tty` | false | Console on the terminal: raw mode, the guest sees a tty (implies `-i`; needs a terminal on stdin and stdout) |
|
||||
| `-v, --volume <HOST:GUEST[:ro]>` | none | Share a directory into the machine; the first rw one is the workspace; repeatable |
|
||||
| `-m, --memory <size>` | 1G | Guest memory |
|
||||
| `--workdir <guest path>` | `/` | Guest cwd |
|
||||
| `-w, --workdir <guest path>` | `/` | Guest cwd |
|
||||
| `-e, --env <KEY=VALUE>` | none | Guest env; repeatable |
|
||||
| `--quiet` | false | Suppress kernel log on the console |
|
||||
| `--capture <DIR>` | none | Payload stdout/stderr into `<DIR>/stdout` and `<DIR>/stderr` |
|
||||
| `--no-cache` | false | Fresh download, ignore the cache |
|
||||
| `-v, --verbose` | false | Diagnostics; raises the libkrun log level |
|
||||
| `--capture <DIR>` | none | Payload stdout/stderr into `<DIR>/stdout` and `<DIR>/stderr`; incompatible with `-t` |
|
||||
| `--no-cache` | false | Fresh download, ignore the cache (a local archive is always read fresh) |
|
||||
| `--verbose` | false | Diagnostics; raises the libkrun log level |
|
||||
|
||||
`ecrou provision IMAGE -- COMMAND` bakes a command into the image's
|
||||
cache entry: the command runs in a machine over a freshly prepared
|
||||
|
||||
Reference in new issue
Block a user