fix(dashboard): correct container-load cgroup path (always-empty LOAD tab)
The dashboard LOAD tab always showed "no running agent containers": container_stats::gather() looked for each agent's cgroup at machine.slice/machine-h\x2d<name>.scope, but that path never exists. nixos-container runs `systemd-nspawn --keep-unit` with `Slice = "machine.slice"` (nixpkgs virtualisation/nixos-containers.nix), so --keep-unit means no separate machined `machine-<name>.scope` is created — the container's cgroup IS the launching service unit, `container@<machine>.service`, under machine.slice. systemd-machined still logs "New machine <name>" (registration), which is what made the scope-path assumption look plausible, but the cgroup stays on the unit. Fix scope_dir to machine.slice/container@<machine>.service and drop the \x2d escaping (the service unit name is used verbatim in the cgroup dir; the journal shows the literal `container@h-<agent>.service`). Adds a unit test pinning the path.
This commit is contained in:
parent
878f95205b
commit
6c3a83ffa1
1 changed files with 41 additions and 10 deletions
|
|
@ -3,7 +3,11 @@
|
||||||
//! machine directly — same privsep-clean posture as the turn-stats
|
//! machine directly — same privsep-clean posture as the turn-stats
|
||||||
//! sqlite reads (`cpu.stat` / `memory.*` are world-readable; no
|
//! sqlite reads (`cpu.stat` / `memory.*` are world-readable; no
|
||||||
//! `hive-priv` needed). Runs host-side in hive-c0re, where
|
//! `hive-priv` needed). Runs host-side in hive-c0re, where
|
||||||
//! `/sys/fs/cgroup/machine.slice/` holds the nspawn machine scopes.
|
//! `/sys/fs/cgroup/machine.slice/` holds the per-container cgroups. Because
|
||||||
|
//! nixos-container runs `systemd-nspawn --keep-unit` (with
|
||||||
|
//! `Slice = "machine.slice"`), each container's cgroup is its launching
|
||||||
|
//! service unit `container@<machine>.service` — not a machined
|
||||||
|
//! `machine-<name>.scope`. See [`scope_dir`].
|
||||||
//!
|
//!
|
||||||
//! No network: agents share the host network namespace
|
//! No network: agents share the host network namespace
|
||||||
//! (`privateNetwork = false`), so there is no per-container net
|
//! (`privateNetwork = false`), so there is no per-container net
|
||||||
|
|
@ -47,16 +51,22 @@ pub struct ContainerResource {
|
||||||
pub mem_max_bytes: Option<u64>,
|
pub mem_max_bytes: Option<u64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// systemd escapes the machine name in the cgroup scope dir
|
/// Cgroup directory for a container's machine.
|
||||||
/// (`machine-<escaped>.scope`). For our machine names — `h-<agent>`,
|
///
|
||||||
/// agent ∈ `[a-z0-9_-]` — the only character systemd escapes is `-`,
|
/// nixos-container runs `systemd-nspawn --keep-unit` with
|
||||||
/// which becomes `\x2d` (verified against `systemd-escape`).
|
/// `Slice = "machine.slice"` (see nixpkgs
|
||||||
fn escape_machine(machine: &str) -> String {
|
/// `virtualisation/nixos-containers.nix`). `--keep-unit` means nspawn does
|
||||||
machine.replace('-', "\\x2d")
|
/// **not** create a separate machined `machine-<name>.scope` — the
|
||||||
}
|
/// container's cgroup *is* the launching service unit,
|
||||||
|
/// `container@<machine>.service`, placed under `machine.slice`.
|
||||||
|
/// systemd-machined still logs "New machine <name>" (registration), but the
|
||||||
|
/// cgroup stays on the service unit. So the path is
|
||||||
|
/// `machine.slice/container@<machine>.service`, and the service unit name is
|
||||||
|
/// used verbatim — no `\x2d` escaping (that only applies when a string is
|
||||||
|
/// converted *into* a scope/slice unit name, not to an already-formed
|
||||||
|
/// instance unit; the journal shows the literal `container@h-<agent>.service`).
|
||||||
fn scope_dir(machine: &str) -> PathBuf {
|
fn scope_dir(machine: &str) -> PathBuf {
|
||||||
PathBuf::from(MACHINE_SLICE).join(format!("machine-{}.scope", escape_machine(machine)))
|
PathBuf::from(MACHINE_SLICE).join(format!("container@{machine}.service"))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a single unsigned integer from a one-line cgroup file.
|
/// Read a single unsigned integer from a one-line cgroup file.
|
||||||
|
|
@ -175,3 +185,24 @@ pub async fn gather() -> Vec<ContainerResource> {
|
||||||
}
|
}
|
||||||
out
|
out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn scope_dir_is_the_keep_unit_service_under_machine_slice() {
|
||||||
|
// nixos-container `--keep-unit` keeps the cgroup on the service
|
||||||
|
// unit `container@<machine>.service` (literal name, no `\x2d`),
|
||||||
|
// under machine.slice — NOT a machined `machine-<name>.scope`.
|
||||||
|
assert_eq!(
|
||||||
|
scope_dir("h-atlas"),
|
||||||
|
PathBuf::from("/sys/fs/cgroup/machine.slice/container@h-atlas.service")
|
||||||
|
);
|
||||||
|
// Underscores in agent names are likewise verbatim.
|
||||||
|
assert_eq!(
|
||||||
|
scope_dir("h-foo_bar"),
|
||||||
|
PathBuf::from("/sys/fs/cgroup/machine.slice/container@h-foo_bar.service")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue