libvirt
The libvirt module provisions cloud-init guests and exposes them as
VFS contexts over SSH. It uses an unprivileged
qemu:///session connection, passt networking, and a host-loopback SSH port
forward by default.
The host needs virsh, qemu-img, passt, cloud-localds, ssh, and
ssh-keygen. image: may be a local path or an HTTP(S) URL; externally
compressed images (.gz, .xz, etc.) are decompressed into the durable image
store — see Caching Between Runs for what that means
for CI.
The domain disk defaults to 20G. Set disk_size: to another qemu-img
size when needed. Cloud images with a first-boot growfs service expand their
partition and filesystem into the additional space.
One-Shot Guest
with waits for SSH, installs Do into the guest (see Installing
Do), waits for boot-time commands and the remote
dolang-vfs, enters the VFS for the block, then stops and undefines the
guest even if the block throws.
Persistent Guest
let domain = libvirt.create
image: freebsd.qcow2.xz
os: :FREEBSD:
name: freebsd-build
echo $domain.name
A later process can recover the Domain object by name.
Call .destroy() to force-stop a running domain and .undefine() to unregister
it. For domains created by this module, undefine also removes the ephemeral
overlay after validating the ownership metadata embedded in the domain XML.
Use upload and download to copy individual files:
To preserve a configured disk, gracefully stop the domain and flatten its backing chain into a standalone qcow2 image:
shutdown waits indefinitely for the domain to reach :SHUTOFF:. Use
time.timeout around it when a time limit is needed. export_disk requires
that the domain have exactly one file-backed disk and refuses to overwrite its
destination.
Running a Script in a Domain
The bundled libvirt entrypoint compiles a local script and runs it through an
existing Do-created domain's VFS:
Every argument after the script path is passed through unchanged. Inside the
target script, shell.program is the local script path and shell.args
contains only those trailing arguments.
--cd sets the initial remote working directory; repeat --env to set or
inherit (a bare NAME, with no =, inherits the local value) an
environment variable; --unset-env unsets one:
Use --connect-uri when it is registered with a connection other than
qemu:///session:
Target Platform
os: is required, and arch: defaults to the host architecture. Both use the
same symbol vocabulary as
sys.os_info().os/sys.cpu_info().arch.
The domain is defined for KVM with a host-passthrough CPU, so arch: must be
the host architecture; a foreign one is rejected rather than emulated.
Installing Do
dolang: controls whether and how Do (particularly dolang-vfs) gets
installed into the guest before it's considered ready:
| Value | Behavior |
|---|---|
false |
Skip installation. The domain won't support with, upload, or download. |
true (default) |
Best-effort: fetch the release matching the running interpreter's version (shell.VERSION) for os:/arch:, silently skipping if none exists for that platform. |
a version tag, e.g. "v0.1.1" |
Fetch that release's artifact for os:/arch:. Throws an error if unavailable. |
a Path |
Use a local archive directly. |
a Url |
Fetch that archive directly, bypassing release resolution. |
{archive: path_or_url, :digest?} |
Explicit form that allows pinning a digest. |
Except for the implicit true default, a failed fetch always throws an error.
Provisioning
packages: installs explicit guest packages. add: adds a file to the
filesystem, while run: runs a block within the VM VFS before creation is
considered complete; these may be repeated.
init: configures boot-time (cloud-init) provisioning, supporting add: and
run: keys; however, run: only supports raw commands as strings or string
arrays.
Both forms of add: take chmod: as an Int mode
such as 0o644.
let vm = libvirt.create
image: freebsd.qcow2.xz
os: :FREEBSD:
packages:
- git
init:
add:
target: /etc/example.conf
chmod: 0o644
content: |
enabled=yes
run: mkdir -p /opt/build
run: do
cd /opt/build do
git clone https://github.com/dolang-org/dolang
Caching Between Runs
Everything this module persists lives under two directories, scoped by
app: (default: derived from shell.program).
$XDG_DATA_HOME/<app>/libvirt/:
| Path | Contents |
|---|---|
ssh/ |
SSH keys |
seeds/ |
Cloud-init seed ISOs |
images/ |
Base disk images |
domains/ |
Per-domain working state |
$XDG_CACHE_HOME/<app>/:
| Path | Contents |
|---|---|
transfer/ |
Download cache |
Of these, it is recommended that you retain ssh/ and seeds/ in
$XDG_DATA_HOME and the entire cache directory in $XDG_CACHE_HOME. SSH
public keys are baked into seed ISOs, so losing the private keys will
necessitate regenerating them.
Pass an explicit app: if you need to use the same cache from multiple
scripts, as the default is derived from shell.program.