Files
zosbuilder/components/zinit/src/main.rs

282 lines
10 KiB
Rust

extern crate zinit;
use anyhow::Result;
use clap::{App, Arg, SubCommand};
use git_version::git_version;
use tokio_stream::StreamExt;
use zinit::app;
const GIT_VERSION: &str = git_version!(args = ["--tags", "--always", "--dirty=-modified"]);
#[tokio::main]
async fn main() -> Result<()> {
let matches = App::new("zinit")
.author("ThreeFold Tech, https://github.com/threefoldtech")
.version(GIT_VERSION)
.about("A runit replacement")
.arg(Arg::with_name("socket").value_name("SOCKET").short("s").long("socket").default_value("/tmp/zinit.sock").help("path to unix socket"))
.arg(Arg::with_name("debug").short("d").long("debug").help("run in debug mode"))
.subcommand(
SubCommand::with_name("init")
.arg(
Arg::with_name("config")
.value_name("DIR")
.short("c")
.long("config")
.help("service configurations directory"),
)
.arg(
Arg::with_name("buffer")
.value_name("BUFFER")
.short("b")
.long("buffer")
.help("buffer size (in lines) to keep services logs")
.default_value("2000")
)
.arg(Arg::with_name("container").long("container").help("run in container mode, shutdown on signal"))
.about("run in init mode, start and maintain configured services"),
)
.subcommand(
SubCommand::with_name("list")
.about("quick view of current known services and their status"),
)
.subcommand(
SubCommand::with_name("shutdown")
.about("stop all services and power off"),
)
.subcommand(
SubCommand::with_name("reboot")
.about("stop all services and reboot"),
)
.subcommand(
SubCommand::with_name("status")
.arg(
Arg::with_name("service")
.value_name("SERVICE")
.required(true)
.help("service name"),
)
.about("show detailed service status"),
)
.subcommand(
SubCommand::with_name("stop")
.arg(
Arg::with_name("service")
.value_name("SERVICE")
.required(true)
.help("service name"),
)
.about("stop service"),
)
.subcommand(
SubCommand::with_name("start")
.arg(
Arg::with_name("service")
.value_name("SERVICE")
.required(true)
.help("service name"),
)
.about("start service. has no effect if the service is already running"),
)
.subcommand(
SubCommand::with_name("forget")
.arg(
Arg::with_name("service")
.value_name("SERVICE")
.required(true)
.help("service name"),
)
.about("forget a service. you can only forget a stopped service"),
)
.subcommand(
SubCommand::with_name("monitor")
.arg(
Arg::with_name("service")
.value_name("SERVICE")
.required(true)
.help("service name"),
)
.about("start monitoring a service. configuration is loaded from server config directory"),
)
.subcommand(
SubCommand::with_name("log")
.arg(
Arg::with_name("snapshot")
.short("s")
.long("snapshot")
.required(false)
.help("if set log prints current buffer without following")
)
.arg(
Arg::with_name("filter")
.value_name("FILTER")
.required(false)
.help("an optional 'exact' service name")
)
.about("view services logs from zinit ring buffer"),
)
.subcommand(
SubCommand::with_name("kill")
.arg(
Arg::with_name("service")
.value_name("SERVICE")
.required(true)
.help("service name"),
)
.arg(
Arg::with_name("signal")
.value_name("SIGNAL")
.required(true)
.default_value("SIGTERM")
.help("signal name (example: SIGTERM)"),
)
.about("send a signal to a running service."),
)
.subcommand(
SubCommand::with_name("restart")
.arg(
Arg::with_name("service")
.value_name("SERVICE")
.required(true)
.help("service name"),
)
.about("restart a service."),
)
.subcommand(
SubCommand::with_name("stats")
.arg(
Arg::with_name("service")
.value_name("SERVICE")
.required(true)
.help("service name"),
)
.about("show memory and CPU usage statistics for a service"),
)
.subcommand(
SubCommand::with_name("proxy")
.arg(
Arg::with_name("address")
.value_name("ADDRESS")
.short("a")
.long("address")
.default_value("127.0.0.1:8080")
.help("address to bind the HTTP/RPC server to"),
)
.about("start an HTTP/RPC proxy for Zinit API"),
)
.get_matches();
use dirs; // Add this import
let socket = matches.value_of("socket").unwrap();
let debug = matches.is_present("debug");
let config_path = if let Some(config_arg) = matches.value_of("config") {
config_arg.to_string()
} else {
#[cfg(target_os = "macos")]
{
let home_dir = dirs::home_dir()
.ok_or_else(|| anyhow::anyhow!("Could not determine home directory"))?;
home_dir
.join("hero")
.join("cfg")
.join("zinit")
.to_str()
.ok_or_else(|| anyhow::anyhow!("Invalid path for config directory"))?
.to_string()
}
#[cfg(not(target_os = "macos"))]
{
"/etc/zinit/".to_string()
}
};
let result = match matches.subcommand() {
("init", Some(matches)) => {
let _server = app::init(
matches.value_of("buffer").unwrap().parse().unwrap(),
&config_path, // Use the determined config_path
socket,
matches.is_present("container"),
debug,
)
.await?;
tokio::signal::ctrl_c().await?;
Ok(())
}
("list", _) => app::list(socket).await,
("shutdown", _) => app::shutdown(socket).await,
("reboot", _) => app::reboot(socket).await,
// ("log", Some(matches)) => app::log(matches.value_of("filter")),
("status", Some(matches)) => {
app::status(socket, matches.value_of("service").unwrap().to_string()).await
}
("stop", Some(matches)) => {
app::stop(socket, matches.value_of("service").unwrap().to_string()).await
}
("start", Some(matches)) => {
app::start(socket, matches.value_of("service").unwrap().to_string()).await
}
("forget", Some(matches)) => {
app::forget(socket, matches.value_of("service").unwrap().to_string()).await
}
("monitor", Some(matches)) => {
app::monitor(socket, matches.value_of("service").unwrap().to_string()).await
}
("kill", Some(matches)) => {
app::kill(
socket,
matches.value_of("service").unwrap().to_string(),
matches.value_of("signal").unwrap().to_string(),
)
.await
}
("log", Some(matches)) => {
let mut stream = app::logs(
socket,
matches.value_of("filter").map(|s| s.to_string()),
!matches.is_present("snapshot"),
)
.await?;
loop {
tokio::select! {
item = stream.next() => {
match item {
Some(log_entry) => {
println!("{}", log_entry);
},
None => break
}
}
_ = tokio::signal::ctrl_c() => {
break
}
}
}
Ok(())
}
("restart", Some(matches)) => {
app::restart(socket, matches.value_of("service").unwrap().to_string()).await
}
("stats", Some(matches)) => {
app::stats(socket, matches.value_of("service").unwrap().to_string()).await
}
("proxy", Some(matches)) => {
app::proxy(socket, matches.value_of("address").unwrap().to_string()).await
}
_ => app::list(socket).await, // default command
};
match result {
Ok(_) => Ok(()),
Err(e) => {
eprintln!("{:#}", e);
std::process::exit(1);
}
}
}