use clap::{CommandFactory, Parser, Subcommand};
use clap_complete::{Generator, Shell, generate};
use ubi_rs::{UbiClient, models::ReqClusterCreate, serde_ext::SerdeExt};
fn print_completions<G: Generator>(gene: G, cmd: &mut clap::Command) {
generate(
gene,
cmd,
cmd.get_name().to_string(),
&mut std::io::stdout(),
);
}
#[derive(Parser)]
#[command(version, about, long_about = None)]
struct Cli {
#[arg(short, long, action = clap::ArgAction::Count)]
debug: u8,
#[arg(long = "generate", value_enum)]
generator: Option<Shell>,
#[command(subcommand)]
command: Commands,
#[clap(
long,
short = 'b',
env = "UBI_BASE_URL",
default_value = "https://api.ubicloud.com"
)]
base_url: String,
#[clap(long, short = 'v', env = "UBI_URL_VERSION", default_value = "")]
url_version: String,
#[clap(long, short = 'a', env = "UBI_API_TOKEN")]
token: Option<String>,
}
#[derive(Subcommand)]
enum Commands {
#[command(name = "kc")]
Kc {
#[command(subcommand)]
subcommand: KcCommand,
},
#[command(name = "project")]
Project {
#[command(subcommand)]
subcommand: ProjectCommand,
},
}
#[derive(Debug, Subcommand)]
pub enum KcCommand {
Create {
#[arg(long)]
cluster_name: String,
#[arg(long)]
version: String,
#[arg(long)]
worker_size: String,
#[arg(long)]
cp_nodes: u32,
#[arg(long)]
worker_nodes: u32,
#[arg(long)]
project: String,
#[arg(long)]
location: String,
},
Delete {
#[arg(long)]
cluster_name: String,
#[arg(long)]
project: String,
#[arg(long)]
location: String,
},
List {
#[arg(long)]
project: String,
},
DownloadKubeconfig {
#[arg(long)]
cluster_name: String,
#[arg(long)]
project: String,
#[arg(long)]
location: String,
},
}
#[derive(Debug, Subcommand)]
pub enum ProjectCommand {
List,
Create {
#[arg(long)]
name: String,
},
Delete {
#[arg(long)]
project_id: String,
},
}
fn ensure_input(msg: &str) -> bool {
println!("{}", msg);
let mut input = String::new();
std::io::stdin().read_line(&mut input).unwrap();
input = input.trim().to_string();
if input.to_lowercase() == "y" {
true
} else if input.to_lowercase() == "n" {
false
} else {
println!("Invalid input, please enter y or n");
ensure_input(msg)
}
}
pub async fn run() -> Result<(), Box<dyn std::error::Error>> {
dotenvy::dotenv().ok();
let cli = Cli::parse();
if cli.debug > 0 {
unsafe {
std::env::set_var("RUST_LOG", "debug");
}
} else {
unsafe {
std::env::set_var("RUST_LOG", "info");
}
}
tracing_subscriber::fmt::init();
if let Some(generator) = cli.generator {
print_completions(generator, &mut Cli::command());
return Ok(());
}
assert!(cli.token.is_some(), "API_TOKEN is not set");
let client = UbiClient::new(&cli.base_url, &cli.url_version, &cli.token.unwrap());
let resp: Box<dyn SerdeExt> = match cli.command {
Commands::Kc { subcommand } => match subcommand {
KcCommand::Create {
cluster_name,
version,
worker_size,
cp_nodes,
worker_nodes,
project,
location,
} => {
let payload = ReqClusterCreate {
version: version.clone(),
worker_size,
cp_nodes,
worker_nodes,
};
client
.kc
.create_kubernetes_cluster(&project, &location, &cluster_name, &payload)
.await?
.boxed()
}
KcCommand::Delete {
cluster_name,
project,
location,
} => client
.kc
.delete_kubernetes_cluster(&project, &location, &cluster_name)
.await?
.boxed(),
KcCommand::List { project } => client
.kc
.list_kubernetes_clusters(&project, None)
.await?
.boxed(),
KcCommand::DownloadKubeconfig {
cluster_name,
project,
location,
} => {
let kubeconfig = client
.kc
.download_kc_config(&project, &location, &cluster_name)
.await?;
let file_name = format!("{}.kubeconfig", cluster_name);
tokio::fs::write(&file_name, kubeconfig).await?;
println!("Kubeconfig saved to {}", file_name);
Box::new(()) }
},
Commands::Project { subcommand } => match subcommand {
ProjectCommand::List => client.project.list_projects().await?.boxed(),
ProjectCommand::Create { name } => client.project.create_project(&name).await?.boxed(),
ProjectCommand::Delete { project_id } => {
client.project.delete_project(&project_id).await?.boxed()
}
},
};
resp.pretty_print();
Ok(())
}
pub fn main() {
let rt = tokio::runtime::Runtime::new().unwrap();
if let Err(e) = rt.block_on(run()) {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}