feat: phase 2 — automatisation: doctor --watch, schedule, service, orchestration, conteneurs (closes #55 #56 #57 #58 #59)
This commit is contained in:
+71
-1
@@ -84,8 +84,15 @@ pub fn start_with_cwd(app: &App, opts: &StartArgs, cwd: Option<&std::path::Path>
|
||||
if members.is_empty() {
|
||||
bail!("unknown group '{group}'");
|
||||
}
|
||||
// Issue #57: --parallel launches every member back-to-back without
|
||||
// waiting (the OS spawns them concurrently); the default waits each
|
||||
// health check before the next member.
|
||||
for m in members {
|
||||
start_one(app, m, &extra_args, &extra_env, opts.notify, true, cwd)?;
|
||||
if !opts.parallel {
|
||||
// Issue #57: block until healthy before the next member.
|
||||
crate::automation::wait_health(&m.name, m.healthcheck.as_ref(), &app.log)?;
|
||||
}
|
||||
}
|
||||
return Ok(0);
|
||||
}
|
||||
@@ -309,8 +316,18 @@ pub fn restart(app: &App, opts: &StartArgs, force: bool, timeout: Option<u64>) -
|
||||
}
|
||||
|
||||
/// run: execute the agent command directly, no process management.
|
||||
pub fn run(app: &App, target: &str, model: Option<&str>, extra: &[OsString]) -> Result<i32> {
|
||||
pub fn run(
|
||||
app: &App,
|
||||
target: &str,
|
||||
model: Option<&str>,
|
||||
container: bool,
|
||||
extra: &[OsString],
|
||||
) -> Result<i32> {
|
||||
let agent = require_agent(app, target)?;
|
||||
// Issue #58: --container runs the agent inside its container profile.
|
||||
if container {
|
||||
return run_container(app, agent, extra);
|
||||
}
|
||||
let mut extra_args: Vec<String> = extra
|
||||
.iter()
|
||||
.map(|o| o.to_string_lossy().to_string())
|
||||
@@ -353,6 +370,59 @@ pub fn run(app: &App, target: &str, model: Option<&str>, extra: &[OsString]) ->
|
||||
Ok(code)
|
||||
}
|
||||
|
||||
/// Issue #58: run the agent inside its container profile. Detects the
|
||||
/// runtime (podman > docker), mounts state/log dirs and the profile
|
||||
/// volumes/network/env, then execs the container in the foreground.
|
||||
fn run_container(app: &App, agent: &crate::config::AgentDef, extra: &[OsString]) -> Result<i32> {
|
||||
let Some(spec) = &agent.container else {
|
||||
bail!(
|
||||
"agent '{}' has no container profile — add 'container: {{image: ...}}' to its definition",
|
||||
agent.name
|
||||
);
|
||||
};
|
||||
let Some(runtime) = crate::automation::detect_runtime() else {
|
||||
bail!(
|
||||
"no container runtime found (docker or podman) — install one or drop --container"
|
||||
);
|
||||
};
|
||||
let state_dir = app.paths.state_file.parent().unwrap_or(std::path::Path::new("."));
|
||||
let log_dir = &app.paths.log_dir;
|
||||
let mut cmd = crate::automation::container_command(
|
||||
&runtime,
|
||||
&agent.name,
|
||||
spec,
|
||||
state_dir,
|
||||
log_dir,
|
||||
)?;
|
||||
// The agent's run command + extra args become the container command.
|
||||
let exec = resolve_exec(app, agent, &[], &BTreeMap::new())?;
|
||||
cmd.push(exec.program);
|
||||
cmd.extend(exec.args.iter().cloned());
|
||||
cmd.extend(extra.iter().map(|o| o.to_string_lossy().to_string()));
|
||||
if app.dry_run() {
|
||||
app.log.dry(&format!("would run: {}", cmd.join(" ")));
|
||||
return Ok(0);
|
||||
}
|
||||
app.log.info(&format!(
|
||||
"running '{}' in a container ({runtime})",
|
||||
agent.name
|
||||
));
|
||||
let status = Command::new(&cmd[0])
|
||||
.args(&cmd[1..])
|
||||
.status()
|
||||
.with_context(|| format!("failed to run {}", cmd.join(" ")))?;
|
||||
let code = status.code().unwrap_or(1);
|
||||
app.emit(
|
||||
&Event::now(EventKind::Run)
|
||||
.with_agent(agent.name.clone())
|
||||
.with_reason(format!("container ({runtime})"))
|
||||
.with_args(cmd.clone())
|
||||
.with_exit_code(code),
|
||||
);
|
||||
let _ = app.state.touch(&agent.name);
|
||||
Ok(code)
|
||||
}
|
||||
|
||||
/// Current directory as a string (None when unavailable).
|
||||
fn cwd_string() -> Option<String> {
|
||||
std::env::current_dir().ok().map(|p| p.display().to_string())
|
||||
|
||||
Reference in New Issue
Block a user