Problem / motivation
We need isolated, throwaway k8s clusters for CI (e.g. testing the MooseFS operator) that never touch production. Lima boots local Linux VMs (k3s template available) and is the natural fit — but Jetpack has no provisioner for it, forcing a delegate_to: localhost + limactl shell-out workaround that violates the "no workarounds" principle. A native provisioner makes a Lima VM a first-class inventory host Jetpack can create, SSH into, converge, and destroy.
Proposed provisioner
Implement the Provisioner trait (src/provisioners/mod.rs:341-382) — exists, ensure_exists, get_ip, stop, destroy — as src/provisioners/lima.rs, and register "lima" in get_provisioner (src/provisioners/mod.rs:386-389) + pub mod lima; (src/provisioners/mod.rs:36-38).
There is no shared exec helper — use std::process::Command inline, synchronously, returning Result<_, String> (matching the trait), exactly as src/dns/mod.rs:22 and src/inventory/loading.rs:28 do. (The async/tokio dance in the Proxmox provisioners exists only because reqwest is async; limactl is a plain subprocess — skip tokio entirely.)
Trait method → limactl mapping (lima 2.1.3; always pass --tty=false for automation)
| Method |
limactl |
exists |
limactl list <name> (exit code) |
ensure_exists |
limactl start --name=<name> template:<template> — start is create-if-absent + boot, idempotent |
get_ip |
parse limactl list <name> --format json → SSHLocalPort/SSHAddress (127.0.0.1:<port>); return 127.0.0.1, inventory sets the port. For host-routable IP, create with --network lima:shared and read Networks[] (stable DHCP by name-derived MAC) |
stop |
limactl stop <name> |
destroy |
limactl delete -f <name> |
Templates: template:k3s (also k8s, k0s). Config fields map onto existing ProvisionConfig generics: hostname, memory, cores (Strings), ssh_user, wait_for_host, wait_*. Lima-specific (template, disk) ride in the extra: HashMap (src/provisioners/mod.rs:150-151).
Changes this surfaces
ProvisionConfig.cluster is required but Lima has no hypervisor host. Relax cluster: String → Option<String> (src/provisioners/mod.rs:64). Blast radius is contained: both Proxmox provisioners add a guard in get_cluster_connection (proxmox_vm.rs:136-141, proxmox_lxc.rs:198-203) and ~20 test literals move to Some(...)/None (e.g. proxmox_lxc.rs:1782-1816, mod.rs:551,569,583,608,638,663). Lima leaves it None.
- Lima SSH needs a non-default key + port. Today no provisioner sets
jet_ssh_private_key_file (only jet_ssh_hostname is written, src/provisioners/mod.rs:471-482 / src/playbooks/traversal.rs:589-619). The Lima provisioner must also write jet_ssh_private_key_file (~/.lima/_config/user), jet_ssh_port (the forwarded SSHLocalPort), and jet_ssh_user (host username) into host vars — the framework already consumes all of these at src/playbooks/context.rs:367-435.
wait_for_ssh probes port 22 (src/provisioners/mod.rs:459), but Lima forwards a random localhost port → either make the probe honor jet_ssh_port, or the Lima provisioner sets wait_for_host: false and performs its own readiness (limactl shell <name> -- true). Flag for decision.
k3s image loading (for the downstream CI playbook, not the provisioner itself)
The k3s template disables Lima's own containerd; k3s bundles its own at /run/k3s/containerd/containerd.sock. Load images with sudo k3s ctr images import <tar> (neither Lima's nerdctl/ctr nor docker are the path on the k3s template).
Tests (TDD)
- Unit-test argv construction for each method (mirror
proxmox_lxc's templating-only tests).
- Integration:
ensure_exists a throwaway template:k3s instance, assert get_ip returns 127.0.0.1 + Status: Running, then destroy. Gate behind a #[ignore]-by-default test so CI without Lima/HVF/qemu can opt out.
Problem / motivation
We need isolated, throwaway k8s clusters for CI (e.g. testing the MooseFS operator) that never touch production. Lima boots local Linux VMs (k3s template available) and is the natural fit — but Jetpack has no provisioner for it, forcing a
delegate_to: localhost+limactlshell-out workaround that violates the "no workarounds" principle. A native provisioner makes a Lima VM a first-class inventory host Jetpack can create, SSH into, converge, and destroy.Proposed provisioner
Implement the
Provisionertrait (src/provisioners/mod.rs:341-382) —exists,ensure_exists,get_ip,stop,destroy— assrc/provisioners/lima.rs, and register"lima"inget_provisioner(src/provisioners/mod.rs:386-389) +pub mod lima;(src/provisioners/mod.rs:36-38).There is no shared exec helper — use
std::process::Commandinline, synchronously, returningResult<_, String>(matching the trait), exactly assrc/dns/mod.rs:22andsrc/inventory/loading.rs:28do. (The async/tokio dance in the Proxmox provisioners exists only becausereqwestis async;limactlis a plain subprocess — skip tokio entirely.)Trait method → limactl mapping (lima 2.1.3; always pass
--tty=falsefor automation)existslimactl list <name>(exit code)ensure_existslimactl start --name=<name> template:<template>—startis create-if-absent + boot, idempotentget_iplimactl list <name> --format json→SSHLocalPort/SSHAddress(127.0.0.1:<port>); return127.0.0.1, inventory sets the port. For host-routable IP, create with--network lima:sharedand readNetworks[](stable DHCP by name-derived MAC)stoplimactl stop <name>destroylimactl delete -f <name>Templates:
template:k3s(alsok8s,k0s). Config fields map onto existingProvisionConfiggenerics:hostname,memory,cores(Strings),ssh_user,wait_for_host,wait_*. Lima-specific (template, disk) ride in theextra: HashMap(src/provisioners/mod.rs:150-151).Changes this surfaces
ProvisionConfig.clusteris required but Lima has no hypervisor host. Relaxcluster: String→Option<String>(src/provisioners/mod.rs:64). Blast radius is contained: both Proxmox provisioners add a guard inget_cluster_connection(proxmox_vm.rs:136-141,proxmox_lxc.rs:198-203) and ~20 test literals move toSome(...)/None(e.g.proxmox_lxc.rs:1782-1816,mod.rs:551,569,583,608,638,663). Lima leaves itNone.jet_ssh_private_key_file(onlyjet_ssh_hostnameis written,src/provisioners/mod.rs:471-482/src/playbooks/traversal.rs:589-619). The Lima provisioner must also writejet_ssh_private_key_file(~/.lima/_config/user),jet_ssh_port(the forwardedSSHLocalPort), andjet_ssh_user(host username) into host vars — the framework already consumes all of these atsrc/playbooks/context.rs:367-435.wait_for_sshprobes port 22 (src/provisioners/mod.rs:459), but Lima forwards a random localhost port → either make the probe honorjet_ssh_port, or the Lima provisioner setswait_for_host: falseand performs its own readiness (limactl shell <name> -- true). Flag for decision.k3s image loading (for the downstream CI playbook, not the provisioner itself)
The
k3stemplate disables Lima's own containerd; k3s bundles its own at/run/k3s/containerd/containerd.sock. Load images withsudo k3s ctr images import <tar>(neither Lima'snerdctl/ctrnordockerare the path on the k3s template).Tests (TDD)
proxmox_lxc's templating-only tests).ensure_existsa throwawaytemplate:k3sinstance, assertget_ipreturns127.0.0.1+Status: Running, thendestroy. Gate behind a#[ignore]-by-default test so CI without Lima/HVF/qemu can opt out.