hyperhive/hive-c0re/src/main.rs

101 lines
3.1 KiB
Rust

use std::path::PathBuf;
use std::sync::Arc;
use anyhow::{Result, bail};
use clap::{Parser, Subcommand};
use hive_sh4re::{HostRequest, HostResponse};
mod agent_server;
mod approvals;
mod broker;
mod client;
mod coordinator;
mod lifecycle;
mod manager_server;
mod server;
use coordinator::Coordinator;
#[derive(Parser)]
#[command(name = "hive-c0re", about = "hyperhive coordinator daemon and CLI")]
struct Cli {
/// Path to the host admin socket.
#[arg(long, global = true, default_value = "/run/hyperhive/host.sock")]
socket: PathBuf,
#[command(subcommand)]
cmd: Cmd,
}
#[derive(Subcommand)]
enum Cmd {
/// Run the coordinator daemon.
Serve {
/// URL of the hyperhive flake. Inlined into each per-agent
/// `flake.nix` as the `hyperhive` input.
#[arg(long, default_value = "/etc/hyperhive")]
hyperhive_flake: String,
/// Path to the sqlite message store.
#[arg(long, default_value = "/var/lib/hyperhive/broker.sqlite")]
db: PathBuf,
},
/// Spawn a new agent container (`hive-agent-<name>`).
Spawn { name: String },
/// Stop a managed container (graceful).
Kill { name: String },
/// Apply pending config to a managed container.
Rebuild { name: String },
/// List managed containers.
List,
/// List pending approval requests submitted by the manager.
Pending,
/// Approve a pending request by id; the action runs immediately.
Approve { id: i64 },
/// Deny a pending request by id.
Deny { id: i64 },
}
#[tokio::main]
async fn main() -> Result<()> {
tracing_subscriber::fmt()
.with_env_filter(
tracing_subscriber::EnvFilter::try_from_default_env()
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
)
.init();
let cli = Cli::parse();
match cli.cmd {
Cmd::Serve {
hyperhive_flake,
db,
} => {
let coord = Arc::new(Coordinator::open(&db, hyperhive_flake)?);
manager_server::start(coord.clone())?;
server::serve(&cli.socket, coord).await
}
Cmd::Spawn { name } => {
render(client::request(&cli.socket, HostRequest::Spawn { name }).await?)
}
Cmd::Kill { name } => {
render(client::request(&cli.socket, HostRequest::Kill { name }).await?)
}
Cmd::Rebuild { name } => {
render(client::request(&cli.socket, HostRequest::Rebuild { name }).await?)
}
Cmd::List => render(client::request(&cli.socket, HostRequest::List).await?),
Cmd::Pending => render(client::request(&cli.socket, HostRequest::Pending).await?),
Cmd::Approve { id } => {
render(client::request(&cli.socket, HostRequest::Approve { id }).await?)
}
Cmd::Deny { id } => render(client::request(&cli.socket, HostRequest::Deny { id }).await?),
}
}
fn render(resp: HostResponse) -> Result<()> {
println!("{}", serde_json::to_string_pretty(&resp)?);
if !resp.ok {
bail!(resp.error.unwrap_or_else(|| "request failed".to_owned()));
}
Ok(())
}