feat(#2290): ConditionPathIsDirectory= in container service drop-in
Add a [Unit] section to hyperhive-limits.conf (the drop-in written by write_resource_limits) with: ConditionPathIsDirectory=/run/hyperhive/agents/<name> When this condition is not met, systemd skips the unit with result "condition" — NOT a failure, so the start-limit counter is not incremented. Belt-and-braces on top of the tmpfiles.d fix (subtask 2): if a dir is somehow absent at start time, the container idles instead of restart-looping into start-limit-hit. Also promote AGENT_RUNTIME_ROOT to a module-level const (was duplicated inside two functions) and remove the duplicates.
This commit is contained in:
parent
f32fba0238
commit
14c2c6d4a5
1 changed files with 29 additions and 3 deletions
|
|
@ -33,6 +33,10 @@ use tokio::process::Command;
|
||||||
/// Root of the per-agent unix-socket dirs on the host.
|
/// Root of the per-agent unix-socket dirs on the host.
|
||||||
const SOCKET_DIR_ROOT: &str = "/run/hive-agent";
|
const SOCKET_DIR_ROOT: &str = "/run/hive-agent";
|
||||||
|
|
||||||
|
/// Root of the per-agent MCP socket dirs on the host.
|
||||||
|
/// Matches `coordinator::AGENT_RUNTIME_ROOT` in hive-c0re.
|
||||||
|
const AGENT_RUNTIME_ROOT: &str = "/run/hyperhive/agents";
|
||||||
|
|
||||||
#[tokio::main]
|
#[tokio::main]
|
||||||
async fn main() -> Result<()> {
|
async fn main() -> Result<()> {
|
||||||
tracing_subscriber::fmt()
|
tracing_subscriber::fmt()
|
||||||
|
|
@ -422,17 +426,40 @@ fn chmod_socket_dir(agent_name: &str, mode: u32) -> Result<(String, String)> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `WriteResourceLimits` — drop a systemd `MemoryMax`/`CPUQuota`
|
/// `WriteResourceLimits` — drop a systemd `MemoryMax`/`CPUQuota`
|
||||||
/// override into the container service's drop-in dir.
|
/// override into the container service's drop-in dir, together with a
|
||||||
|
/// `ConditionPathIsDirectory=` guard on the agent's MCP runtime dir.
|
||||||
|
///
|
||||||
|
/// The condition causes systemd to *skip* (not *fail*) the unit when the
|
||||||
|
/// bind-mount source dir is absent — result is `condition`, which does not
|
||||||
|
/// increment the start-limit counter. This is belt-and-braces on top of
|
||||||
|
/// the tmpfiles.d entries written by `SyncAgentTmpfiles`: in the unlikely
|
||||||
|
/// event the dir is missing at start time, the unit idles rather than
|
||||||
|
/// restart-looping into `start-limit-hit`.
|
||||||
fn write_resource_limits(
|
fn write_resource_limits(
|
||||||
container: &str,
|
container: &str,
|
||||||
memory_max: &str,
|
memory_max: &str,
|
||||||
cpu_quota: &str,
|
cpu_quota: &str,
|
||||||
) -> Result<(String, String)> {
|
) -> Result<(String, String)> {
|
||||||
validate_container_system_name(container)?;
|
validate_container_system_name(container)?;
|
||||||
|
// Derive the logical agent name (strip h- prefix) to form the runtime
|
||||||
|
// dir path. Falls back to the full container name for infra containers
|
||||||
|
// that don't use the h- prefix.
|
||||||
|
let logical = container.strip_prefix(AGENT_PREFIX).unwrap_or(container);
|
||||||
|
let runtime_dir = format!("{AGENT_RUNTIME_ROOT}/{logical}");
|
||||||
let dir = format!("/run/systemd/system/container@{container}.service.d");
|
let dir = format!("/run/systemd/system/container@{container}.service.d");
|
||||||
std::fs::create_dir_all(&dir).with_context(|| format!("create {dir}"))?;
|
std::fs::create_dir_all(&dir).with_context(|| format!("create {dir}"))?;
|
||||||
let path = format!("{dir}/hyperhive-limits.conf");
|
let path = format!("{dir}/hyperhive-limits.conf");
|
||||||
let content = format!("[Service]\nMemoryMax={memory_max}\nCPUQuota={cpu_quota}\n");
|
// [Unit] section: condition checked at start time — skips (not fails)
|
||||||
|
// the unit when the MCP socket dir is absent, avoiding restart loops.
|
||||||
|
// [Service] section: resource caps.
|
||||||
|
let content = format!(
|
||||||
|
"[Unit]\n\
|
||||||
|
ConditionPathIsDirectory={runtime_dir}\n\
|
||||||
|
\n\
|
||||||
|
[Service]\n\
|
||||||
|
MemoryMax={memory_max}\n\
|
||||||
|
CPUQuota={cpu_quota}\n"
|
||||||
|
);
|
||||||
std::fs::write(&path, content).with_context(|| format!("write {path}"))?;
|
std::fs::write(&path, content).with_context(|| format!("write {path}"))?;
|
||||||
Ok((String::new(), String::new()))
|
Ok((String::new(), String::new()))
|
||||||
}
|
}
|
||||||
|
|
@ -1512,7 +1539,6 @@ fn write_bridge_dns_marker(container: &str, isolation: Option<&NetworkIsolation>
|
||||||
/// as `/run/hive`)
|
/// as `/run/hive`)
|
||||||
/// - `/run/hive-agent/<name>` (web socket dir, bind-mounted into container)
|
/// - `/run/hive-agent/<name>` (web socket dir, bind-mounted into container)
|
||||||
const TMPFILES_PATH: &str = "/etc/tmpfiles.d/hyperhive-agents.conf";
|
const TMPFILES_PATH: &str = "/etc/tmpfiles.d/hyperhive-agents.conf";
|
||||||
const AGENT_RUNTIME_ROOT: &str = "/run/hyperhive/agents";
|
|
||||||
|
|
||||||
async fn sync_agent_tmpfiles(agents: &[String]) -> Result<(String, String)> {
|
async fn sync_agent_tmpfiles(agents: &[String]) -> Result<(String, String)> {
|
||||||
for name in agents {
|
for name in agents {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue