use std::path::{Path, PathBuf};
use std::process::Command;
use anyhow::{Context, Result, bail};
use tonin_plugin::Plan;
use super::generate::resolve_service_dir;
#[derive(clap::Args, Clone)]
pub struct CommonArgs {
#[arg(long)]
pub path: Option<PathBuf>,
#[arg(long)]
pub env: Option<String>,
#[arg(long)]
pub chart: Option<PathBuf>,
}
#[derive(clap::Args)]
pub struct UpgradeArgs {
#[command(flatten)]
pub common: CommonArgs,
#[arg(long)]
pub atomic: bool,
#[arg(long)]
pub wait: bool,
#[arg(long, default_value = "10m0s")]
pub timeout: String,
#[arg(last = true)]
pub extra: Vec<String>,
}
#[derive(clap::Args)]
pub struct SimpleArgs {
#[command(flatten)]
pub common: CommonArgs,
#[arg(last = true)]
pub extra: Vec<String>,
}
pub fn run_upgrade(args: UpgradeArgs) -> Result<()> {
let (plan, env, chart_dir) = resolve_context(&args.common)?;
let mut cmd = helm();
cmd.args(["upgrade", "--install", &plan.name])
.arg(&chart_dir)
.args(["-f", &values_file(&chart_dir, "values.yaml")])
.args([
"-f",
&values_file(&chart_dir, &format!("values-{env}.yaml")),
])
.args(["--namespace", &plan.namespace])
.arg("--create-namespace");
if args.atomic {
cmd.arg("--atomic");
}
if args.wait {
cmd.arg("--wait");
}
cmd.args(["--timeout", &args.timeout]);
cmd.args(&args.extra);
exec_helm(cmd)
}
pub fn run_template(args: SimpleArgs) -> Result<()> {
let (plan, env, chart_dir) = resolve_context(&args.common)?;
let mut cmd = helm();
cmd.args(["template", &plan.name])
.arg(&chart_dir)
.args(["-f", &values_file(&chart_dir, "values.yaml")])
.args([
"-f",
&values_file(&chart_dir, &format!("values-{env}.yaml")),
])
.args(["--namespace", &plan.namespace])
.args(&args.extra);
exec_helm(cmd)
}
pub fn run_diff(args: SimpleArgs) -> Result<()> {
let (plan, env, chart_dir) = resolve_context(&args.common)?;
let mut cmd = helm();
cmd.args(["diff", "upgrade", &plan.name])
.arg(&chart_dir)
.args(["-f", &values_file(&chart_dir, "values.yaml")])
.args([
"-f",
&values_file(&chart_dir, &format!("values-{env}.yaml")),
])
.args(["--namespace", &plan.namespace])
.args(&args.extra);
exec_helm(cmd)
}
pub fn run_status(args: SimpleArgs) -> Result<()> {
let (plan, env, _chart_dir) = resolve_context(&args.common)?;
let mut cmd = helm();
cmd.args(["status", &plan.name])
.args(["--namespace", &plan.namespace])
.args(&args.extra);
let _ = env;
exec_helm(cmd)
}
pub fn run_rollback(args: SimpleArgs) -> Result<()> {
let (plan, env, _chart_dir) = resolve_context(&args.common)?;
let mut cmd = helm();
cmd.args(["rollback", &plan.name])
.args(["--namespace", &plan.namespace])
.args(&args.extra);
let _ = env;
exec_helm(cmd)
}
pub fn run_uninstall(args: SimpleArgs) -> Result<()> {
let (plan, env, _chart_dir) = resolve_context(&args.common)?;
let mut cmd = helm();
cmd.args(["uninstall", &plan.name])
.args(["--namespace", &plan.namespace])
.args(&args.extra);
let _ = env;
exec_helm(cmd)
}
pub fn run_history(args: SimpleArgs) -> Result<()> {
let (plan, env, _chart_dir) = resolve_context(&args.common)?;
let mut cmd = helm();
cmd.args(["history", &plan.name])
.args(["--namespace", &plan.namespace])
.args(&args.extra);
let _ = env;
exec_helm(cmd)
}
pub fn run_raw(extra: Vec<String>) -> Result<()> {
let mut cmd = helm();
cmd.args(&extra);
exec_helm(cmd)
}
fn resolve_context(common: &CommonArgs) -> Result<(Plan, String, PathBuf)> {
let service_dir = resolve_service_dir(common.path.as_deref())?;
let env = tonin_plugin::select_env(common.env.as_deref());
let plan = Plan::load_with_env(&service_dir.join("tonin.toml"), &env)
.with_context(|| format!("loading tonin.toml in {}", service_dir.display()))?;
let chart_dir = common
.chart
.clone()
.unwrap_or_else(|| service_dir.join("chart"));
Ok((plan, env, chart_dir))
}
fn helm() -> Command {
Command::new("helm")
}
fn values_file(chart_dir: &Path, name: &str) -> String {
chart_dir.join(name).to_string_lossy().into_owned()
}
#[cfg(unix)]
fn exec_helm(mut cmd: Command) -> Result<()> {
use std::os::unix::process::CommandExt;
let err = cmd.exec();
bail!("failed to exec helm: {err}")
}
#[cfg(not(unix))]
fn exec_helm(mut cmd: Command) -> Result<()> {
let status = cmd.status().context("failed to spawn helm")?;
std::process::exit(status.code().unwrap_or(1));
}