Structural changes (the biggest items from the review):
- `HostConfigurationProvider` split into five narrower capabilities:
`HostReachable`, `PackageInstaller`, `FileDelivery`,
`UnixUserManager`, `SystemdManager`. Each implementation now only
implements what it can actually deliver — a future cloud-init /
ignition / podman-agent backend can pick a subset without
inheriting systemd assumptions it can't honour. Added an umbrella
trait `LinuxHostConfiguration` blanket-impl'd for any type that
has all five, so Scores keep a single bound.
- New `VirtualMachineHost` capability in domain/topology/: `list_vms`
/ `ensure_vm` / `delete_vm` / `get_vm_info`, with generic
`VirtualMachineSpec` carrying a typed optional `VmFirstBootConfig`
(hostname, admin user, authorized keys). `KvmHost` trait and
`KvmHostTopology` deleted; `KvmVirtualMachineHost` is the
concrete libvirt implementation. Cloud-init stays a KVM-impl
detail — callers never see it.
- `KvmVmScore` + `CloudInitVmConfig` deleted; replaced by a generic
`ProvisionVmScore` in `modules::iot::vm_score` bound to
`T: VirtualMachineHost`. The Score itself has no knowledge of the
hypervisor or its first-boot delivery mechanism.
- `IotDeviceSetupConfig.device_id` is now `harmony_types:🆔:Id`
(timestamp-prefixed, sortable-by-creation, collision-safe).
- `ensure_ready` on `KvmVirtualMachineHost` is a Noop with a TODO
pointing at ROADMAP/12-code-review-april-2026.md §12.1 (phased
topology). Captures the concern about eagerly probing the
hypervisor even when the current run doesn't need KVM.
Code quality fixes from the line-level comments:
- `render_toml` / `render_systemd_unit` / `render_user_data`
rewritten as `format!` with raw-string templates (no more
push_str chains).
- Every `Command::new(…).arg().arg().arg()` chain in the touched
files converted to `.args([…])`.
- Ansible module args are now typed Rust structs (`AptArgs`,
`AnsibleFileArgs`, `AnsibleUserArgs`, `AnsibleCopyArgs`,
`AnsibleSystemdArgs`, `AnsibleCommandArgs`, `AnsibleStatArgs`)
serialized via `serde_json::to_value`. No more `json!` macros
with ad-hoc string keys.
- `ensure_linger`: no more shell sentinel. Uses
`ansible.builtin.stat` on `/var/lib/systemd/linger/<user>` for
the idempotent change-state check, then `ansible.builtin.command
loginctl enable-linger` only on miss. `loginctl` is required
(not just `file state=touch`) because systemd-logind needs the
dbus signal to actually start the user manager; a plain file
touch doesn't wake it up and every subsequent `systemctl --user
…` fails with "Failed to connect to bus". Documented in-place.
- `ensure_user_unit_active`: picks up the user's UID first via
`ansible.builtin.command id -u <user>` and wraps the
`systemctl --user enable --now <unit>` invocation in `env
XDG_RUNTIME_DIR=/run/user/<UID>`. The systemd module's
task-level `environment:` keyword isn't available in ad-hoc
mode; this is the cleanest equivalent. Documented the
inline-playbook path as a future when we get more task-level-
env callsites.
- `ensure_package` comment clarified: distro dispatch is this
function's job; Debian-family is the first concrete target and
extending to RHEL/Fedora/Alpine is an implementation detail,
not a capability change.
- Kubespray line removed.
Verified: from a primed `$HARMONY_DATA_DIR/iot/`, smoke-a3.sh
still completes all 5 phases (bootstrap + provision + 9 setup
changes + initial NATS status + power-cycle recovery).
19 lines
399 B
TOML
19 lines
399 B
TOML
[package]
|
|
name = "example_iot_vm_setup"
|
|
version.workspace = true
|
|
edition = "2024"
|
|
license.workspace = true
|
|
|
|
[[bin]]
|
|
name = "iot_vm_setup"
|
|
path = "src/main.rs"
|
|
|
|
[dependencies]
|
|
harmony = { path = "../../harmony", features = ["kvm"] }
|
|
harmony_types = { path = "../../harmony_types" }
|
|
tokio.workspace = true
|
|
log.workspace = true
|
|
env_logger.workspace = true
|
|
anyhow.workspace = true
|
|
clap.workspace = true
|