diff --git a/src/helm/cli.rs b/src/helm/cli.rs index 78e6d4c..4c1ae4a 100644 --- a/src/helm/cli.rs +++ b/src/helm/cli.rs @@ -1,36 +1,24 @@ -use std::{ - path::PathBuf, - process::{ExitStatus, Output}, -}; +use std::path::PathBuf; -use tokio::process::Command; -use tracing::{debug, error, instrument, Level}; +use tracing::{debug, error, instrument}; -use crate::domain::{App, Chart}; +use crate::{ + cmd::CommandRunner, + domain::{App, Chart}, +}; use super::{HelmClient, Result}; -macro_rules! log_output { - ($lvl:ident, $output:expr) => {{ - let output = String::from_utf8_lossy(&$output); - for line in output.lines() { - $lvl!("helm: {line}"); - } - }}; -} - #[derive(Debug, thiserror::Error)] pub enum Error { - #[error("helm {0}")] - Command(ExitStatus), - #[error("invalid unicode")] - InvalidUnicode, - #[error("i/o error: {0}")] - Io( + #[error("{0}")] + Command( #[from] #[source] - std::io::Error, + crate::cmd::Error, ), + #[error("invalid unicode")] + InvalidUnicode, } #[derive(clap::Args, Clone, Debug, Eq, PartialEq)] @@ -61,61 +49,58 @@ impl Default for CliHelmClientArgs { } } -pub struct CliHelmClient(CliHelmClientArgs); - -impl CliHelmClient { - pub fn new(args: CliHelmClientArgs) -> Self { - Self(args) - } +pub struct CliHelmClient { + args: CliHelmClientArgs, + runner: R, +} - fn handle_output(output: Output) -> Result { - if tracing::enabled!(Level::DEBUG) { - log_output!(debug, output.stdout); - } - if output.status.success() { - if tracing::enabled!(Level::DEBUG) { - log_output!(debug, output.stderr); - } - Ok(()) - } else { - log_output!(error, output.stderr); - Err(Error::Command(output.status).into()) - } +impl CliHelmClient { + pub fn new(args: CliHelmClientArgs, runner: R) -> Self { + Self { args, runner } } } -impl HelmClient for CliHelmClient { +impl HelmClient for CliHelmClient { #[instrument("helm_uninstall", skip(self, name, app), fields(app.name = name, app.namespace = app.spec.namespace))] async fn uninstall(&self, name: &str, app: &App) -> Result { debug!("running helm uninstall"); - let output = Command::new(&self.0.bin) - .arg("uninstall") - .arg("-n") - .arg(&app.spec.namespace) - .arg("--ignore-not-found") - .arg(name) - .output() + self.runner + .run( + "helm", + &[ + "uninstall", + "-n", + &app.spec.namespace, + "--ignore-not-found", + name, + ], + ) .await?; - Self::handle_output(output) + Ok(()) } #[instrument("helm_upgrade", skip(self, app, filepaths), fields(app.chart = %app.spec.chart, app.name = name, app.namespace = app.spec.namespace))] async fn upgrade(&self, name: &str, app: &App, filepaths: &[PathBuf]) -> Result { let chart = match &app.spec.chart { - Chart::BuiltIn {} => self.0.chart_path.to_str().ok_or(Error::InvalidUnicode)?, + Chart::BuiltIn {} => self.args.chart_path.to_str().ok_or(Error::InvalidUnicode)?, }; - let mut cmd = Command::new(&self.0.bin); - cmd.arg("upgrade") - .arg("-n") - .arg(&app.spec.namespace) - .arg("--create-namespace") - .arg("--install"); + let mut args = vec![ + "upgrade", + "-n", + &app.spec.namespace, + "--create-namespace", + "--install", + ]; for path in filepaths { - cmd.arg("--values").arg(path); + let path = path.to_str().ok_or(Error::InvalidUnicode)?; + args.push("--values"); + args.push(path); } debug!("running helm upgrade"); - let output = cmd.arg(name).arg(chart).output().await?; - Self::handle_output(output) + args.push(name); + args.push(chart); + self.runner.run("helm", &args).await?; + Ok(()) } } @@ -125,8 +110,8 @@ impl From for super::Error { } } -impl From for super::Error { - fn from(err: std::io::Error) -> Self { - Error::Io(err).into() +impl From for super::Error { + fn from(err: crate::cmd::Error) -> Self { + Error::Command(err).into() } } diff --git a/src/main.rs b/src/main.rs index 498e5f0..478ada0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use std::{ use ::kube::CustomResourceExt; use api::{start_api, ApiContext}; use clap::{Parser, Subcommand}; +use cmd::default::DefaultCommandRunner; use deploy::helm::{HelmDeployer, HelmDeployerArgs}; use domain::{App, Invitation, Role, User}; use helm::cli::{CliHelmClient, CliHelmClientArgs}; @@ -37,6 +38,7 @@ use tracing_subscriber::{ }; mod api; +mod cmd; mod deploy; mod domain; mod helm; @@ -63,7 +65,7 @@ async fn main() -> anyhow::Result<()> { Command::Crd { cmd } => cmd.print(), Command::Op(args) => { let kube = ::kube::Client::try_default().await?; - let helm = CliHelmClient::new(args.helm); + let helm = CliHelmClient::new(args.helm, DefaultCommandRunner); let ctx = OpContext { deployer: HelmDeployer::new(args.deployer, helm), kube: ApiKubeClient::new(kube.clone()),