# virtme-ng (vng) Runner Example

Reference scripts for testing compiled Linux kernels in virtme-ng VMs using
cui-cui's exec-prefix runner. This approach mounts an OCI container image as
the VM's rootfs via podman, boots a kernel with vng, and runs the CI job
inside the VM.

## Files

- `cui-cui.toml` -- Example configuration with a Linux build server and a local
  vng test runner
- `vng-prepare.sh` -- Prepare script: pulls and mounts the OCI image, exports
  `$ROOTFS`
- `vng-cleanup.sh` -- Cleanup script: unmounts the OCI image (runs via
  `trap EXIT`)

## Prerequisites

- [podman](https://podman.io/) installed (for OCI image management)
- [virtme-ng](https://github.com/arighi/virtme-ng) installed (`pip install
  virtme-ng`)
- A kernel bzImage to boot (compiled or from a CI artifact)
- KVM support (`/dev/kvm` accessible)

### OCI image requirements

The OCI image used as rootfs must include:

- **`iproute`** (or equivalent) — provides `ip` for network interface
  configuration inside the VM
- **`virtme-ng`** — the guest-side virtme tools are needed for proper
  VM initialization (script execution, filesystem sharing, network setup)

Without these, networking (`--network user`) won't work even though QEMU
adds the virtual NIC — the guest needs userspace tools to bring the
interface up and configure DNS.

## Usage

1. Copy `cui-cui.toml` to your kernel repo as `.cui-cui.toml`
2. Adjust tags and paths for your environment
3. Place the prepare/cleanup scripts where the `@file` references point
4. Run: `cui-cui --dispatch .`

## How it works

### OCI image as rootfs

The prepare script uses `podman image mount` to mount the OCI image's
filesystem without starting a container. This provides the rootfs that vng
uses as the VM's root filesystem. The mount persists until explicitly
unmounted by the cleanup script.

Since `podman image mount` requires a user namespace, all podman and vng
commands run under `$PODMAN_UNSHARE` (set automatically by cui-cui to
`podman unshare` for non-root users, empty for root).

### VM lifecycle

1. The prepare script pulls the OCI image and mounts it, exporting `$ROOTFS`
2. vng boots the kernel with `--root $ROOTFS`, sharing the host's worktree
   at `/mnt` inside the VM (`--rwdir /mnt=$PWD`). We use `/mnt` because the
   mount point must already exist in the OCI image's filesystem — `/mnt` is
   a standard FHS directory present in virtually all Linux images. The
   `guestpath=hostpath` form is required because plain `--rwdir` and
   `--pwd`/`--cwd` reject paths outside the rootfs. The guest CWD is set via
   `--append virtme_chdir=/mnt` (kernel cmdline, no validation).
3. `--overlay-rwdir=/mnt` makes `/mnt` writable inside the VM via an
   overlay, so build artifacts and test output don't modify the host
   worktree
4. `--network user` enables user-mode networking (SLIRP) so the VM has
   DNS resolution and HTTP access (e.g. for downloading packages or
   fetching git repos)
5. `--disable-microvm` forces a full machine type (pc/q35) instead of
   QEMU's lightweight microvm. This is needed when passing PCI devices
   to the VM (e.g. USB passthrough via `--qemu-opts="-usb -device
   usb-host,..."`) since microvm has no PCI bus
6. The job script runs inside the VM as root
7. On exit (success or failure), cleanup unmounts the OCI image

### Variables

All job variables are exported before the prepare script runs:

| Variable | Description |
|----------|-------------|
| `$CI_JOB_IMAGE` | OCI container image to mount as rootfs |
| `$KERNEL` | Path to the kernel bzImage to boot |
| `$PODMAN_UNSHARE` | `podman unshare` for non-root, empty for root (set by cui-cui) |
