hive-c0re: nixos-container lifecycle (spawn/kill/rebuild/list)
This commit is contained in:
parent
0ec54ecf89
commit
90798b936e
3 changed files with 88 additions and 15 deletions
60
hive-c0re/src/lifecycle.rs
Normal file
60
hive-c0re/src/lifecycle.rs
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
//! Thin async wrappers over `nixos-container`.
|
||||||
|
|
||||||
|
use anyhow::{Context, Result, bail};
|
||||||
|
use tokio::process::Command;
|
||||||
|
|
||||||
|
pub const AGENT_PREFIX: &str = "hive-agent-";
|
||||||
|
pub const HIVE_PREFIX: &str = "hive-";
|
||||||
|
|
||||||
|
pub fn container_name(name: &str) -> String {
|
||||||
|
format!("{AGENT_PREFIX}{name}")
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn spawn(name: &str, agent_flake: &str) -> Result<()> {
|
||||||
|
let container = container_name(name);
|
||||||
|
run(&["create", &container, "--flake", agent_flake]).await?;
|
||||||
|
run(&["start", &container]).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn kill(name: &str) -> Result<()> {
|
||||||
|
let container = container_name(name);
|
||||||
|
run(&["stop", &container]).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn rebuild(name: &str, agent_flake: &str) -> Result<()> {
|
||||||
|
let container = container_name(name);
|
||||||
|
run(&["update", &container, "--flake", agent_flake]).await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn list() -> Result<Vec<String>> {
|
||||||
|
let out = Command::new("nixos-container")
|
||||||
|
.arg("list")
|
||||||
|
.output()
|
||||||
|
.await
|
||||||
|
.context("invoke nixos-container list")?;
|
||||||
|
if !out.status.success() {
|
||||||
|
bail!(
|
||||||
|
"nixos-container list exited with status {}: {}",
|
||||||
|
out.status,
|
||||||
|
String::from_utf8_lossy(&out.stderr).trim()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Ok(String::from_utf8_lossy(&out.stdout)
|
||||||
|
.lines()
|
||||||
|
.map(str::trim)
|
||||||
|
.filter(|line| line.starts_with(HIVE_PREFIX))
|
||||||
|
.map(str::to_owned)
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn run(args: &[&str]) -> Result<()> {
|
||||||
|
let status = Command::new("nixos-container")
|
||||||
|
.args(args)
|
||||||
|
.status()
|
||||||
|
.await
|
||||||
|
.with_context(|| format!("invoke nixos-container {}", args.join(" ")))?;
|
||||||
|
if !status.success() {
|
||||||
|
bail!("nixos-container {} exited with {status}", args.join(" "));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,7 @@ use clap::{Parser, Subcommand};
|
||||||
use hive_sh4re::{HostRequest, HostResponse};
|
use hive_sh4re::{HostRequest, HostResponse};
|
||||||
|
|
||||||
mod client;
|
mod client;
|
||||||
|
mod lifecycle;
|
||||||
mod server;
|
mod server;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ use hive_sh4re::{HostRequest, HostResponse};
|
||||||
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
|
||||||
use tokio::net::{UnixListener, UnixStream};
|
use tokio::net::{UnixListener, UnixStream};
|
||||||
|
|
||||||
|
use crate::lifecycle;
|
||||||
|
|
||||||
pub async fn serve(socket: &Path, agent_flake: &str) -> Result<()> {
|
pub async fn serve(socket: &Path, agent_flake: &str) -> Result<()> {
|
||||||
if let Some(parent) = socket.parent() {
|
if let Some(parent) = socket.parent() {
|
||||||
std::fs::create_dir_all(parent)
|
std::fs::create_dir_all(parent)
|
||||||
|
|
@ -51,20 +53,30 @@ async fn handle(stream: UnixStream, agent_flake: &str) -> Result<()> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn dispatch(req: &HostRequest, _agent_flake: &str) -> HostResponse {
|
async fn dispatch(req: &HostRequest, agent_flake: &str) -> HostResponse {
|
||||||
match req {
|
let result: anyhow::Result<HostResponse> = async {
|
||||||
|
Ok(match req {
|
||||||
HostRequest::Spawn { name } => {
|
HostRequest::Spawn { name } => {
|
||||||
tracing::info!(%name, "spawn (stub)");
|
tracing::info!(%name, "spawn");
|
||||||
HostResponse::error("spawn: nixos-container integration pending")
|
lifecycle::spawn(name, agent_flake).await?;
|
||||||
|
HostResponse::success()
|
||||||
}
|
}
|
||||||
HostRequest::Kill { name } => {
|
HostRequest::Kill { name } => {
|
||||||
tracing::info!(%name, "kill (stub)");
|
tracing::info!(%name, "kill");
|
||||||
HostResponse::error("kill: nixos-container integration pending")
|
lifecycle::kill(name).await?;
|
||||||
|
HostResponse::success()
|
||||||
}
|
}
|
||||||
HostRequest::Rebuild { name } => {
|
HostRequest::Rebuild { name } => {
|
||||||
tracing::info!(%name, "rebuild (stub)");
|
tracing::info!(%name, "rebuild");
|
||||||
HostResponse::error("rebuild: nixos-container integration pending")
|
lifecycle::rebuild(name, agent_flake).await?;
|
||||||
|
HostResponse::success()
|
||||||
}
|
}
|
||||||
HostRequest::List => HostResponse::list(Vec::new()),
|
HostRequest::List => HostResponse::list(lifecycle::list().await?),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.await;
|
||||||
|
match result {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => HostResponse::error(format!("{e:#}")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue