pub mod client;
pub mod cmd;
pub mod config;
pub mod exit;
pub mod model;
pub mod output;
use std::path::PathBuf;
use clap::{Parser, Subcommand};
use crate::client::CellosClient;
use crate::exit::{CtlError, CtlResult};
use crate::output::OutputFormat;
#[derive(Parser, Debug)]
#[command(
name = "cellctl",
version,
about = "kubectl-style CLI for CellOS",
long_about = None,
)]
struct Cli {
#[arg(long, global = true, env = "CELLCTL_SERVER")]
server: Option<String>,
#[arg(long, global = true, env = "CELLCTL_TOKEN", hide_env_values = true)]
token: Option<String>,
#[command(subcommand)]
cmd: Cmd,
}
#[derive(Subcommand, Debug)]
enum Cmd {
Apply {
#[arg(short = 'f', long = "file")]
file: PathBuf,
},
Get {
#[command(subcommand)]
what: GetWhat,
},
Describe {
#[command(subcommand)]
what: DescribeWhat,
},
Delete {
#[command(subcommand)]
what: DeleteWhat,
},
Logs {
cell: String,
#[arg(long, short = 'f')]
follow: bool,
#[arg(long)]
tail: Option<usize>,
},
Events {
#[arg(long)]
formation: Option<String>,
#[arg(long, short = 'f')]
follow: bool,
},
Rollout {
#[command(subcommand)]
what: RolloutWhat,
},
Diff {
#[arg(short = 'f', long = "file")]
file: PathBuf,
},
Config {
#[command(subcommand)]
what: ConfigWhat,
},
Version,
Webui {
#[arg(long)]
open: bool,
#[arg(long, value_enum, default_value = "auto")]
bind: cmd::webui::BindMode,
},
}
#[derive(Subcommand, Debug)]
enum GetWhat {
Formations {
#[arg(long, short = 'o', default_value = "table")]
output: String,
},
Cells {
#[arg(long)]
formation: Option<String>,
#[arg(long, short = 'o', default_value = "table")]
output: String,
},
}
#[derive(Subcommand, Debug)]
enum DescribeWhat {
Formation { name: String },
Cell { name: String },
}
#[derive(Subcommand, Debug)]
enum DeleteWhat {
Formation {
name: String,
#[arg(long, short = 'y')]
yes: bool,
},
}
#[derive(Subcommand, Debug)]
enum RolloutWhat {
Status {
name: String,
#[arg(long)]
timeout: Option<u64>,
},
}
#[derive(Subcommand, Debug)]
enum ConfigWhat {
SetServer { url: String },
SetToken { token: String },
Show,
}
pub fn run() {
if std::env::var_os("RUST_LOG").is_some() {
let _ = tracing_subscriber::fmt()
.with_env_filter(tracing_subscriber::EnvFilter::from_default_env())
.with_writer(std::io::stderr)
.try_init();
}
let cli = Cli::parse();
let rt = match tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
{
Ok(rt) => rt,
Err(e) => CtlError::usage(format!("init tokio runtime: {e}")).exit(),
};
match rt.block_on(dispatch(cli)) {
Ok(()) => {}
Err(e) => e.exit(),
}
}
async fn dispatch(cli: Cli) -> CtlResult<()> {
let mut cfg = config::load().unwrap_or_default();
if let Some(s) = cli.server {
cfg.server_url = Some(s);
}
if let Some(t) = cli.token {
cfg.api_token = Some(t);
}
match cli.cmd {
Cmd::Config { what } => return run_config(what),
Cmd::Version => {
let client = CellosClient::new(&cfg)?;
return cmd::version::run(&client).await;
}
Cmd::Webui { open, bind } => {
return cmd::webui::run(&cfg, open, bind).await;
}
_ => {}
}
let client = CellosClient::new(&cfg)?;
match cli.cmd {
Cmd::Apply { file } => cmd::apply::run(&client, &file).await,
Cmd::Get { what } => match what {
GetWhat::Formations { output } => {
let fmt: OutputFormat = output.parse()?;
cmd::get::formations(&client, fmt).await
}
GetWhat::Cells { formation, output } => {
let fmt: OutputFormat = output.parse()?;
cmd::get::cells(&client, formation.as_deref(), fmt).await
}
},
Cmd::Describe { what } => match what {
DescribeWhat::Formation { name } => cmd::describe::formation(&client, &name).await,
DescribeWhat::Cell { name } => cmd::describe::cell(&client, &name).await,
},
Cmd::Delete { what } => match what {
DeleteWhat::Formation { name, yes } => {
cmd::delete::formation(&client, &name, yes).await
}
},
Cmd::Logs { cell, follow, tail } => cmd::logs::run(&client, &cell, follow, tail).await,
Cmd::Events { formation, follow } => {
cmd::events::run(&client, formation.as_deref(), follow).await
}
Cmd::Rollout { what } => match what {
RolloutWhat::Status { name, timeout } => {
cmd::rollout::status(&client, &name, timeout).await
}
},
Cmd::Diff { file } => cmd::diff::run(&client, &file).await,
Cmd::Config { .. } | Cmd::Version | Cmd::Webui { .. } => {
unreachable!("handled above")
}
}
}
fn run_config(what: ConfigWhat) -> CtlResult<()> {
match what {
ConfigWhat::SetServer { url } => cmd::config_cmd::set_server(&url),
ConfigWhat::SetToken { token } => cmd::config_cmd::set_token(&token),
ConfigWhat::Show => cmd::config_cmd::show(),
}
}