From b7aee6289dd7ab0d49285052659de37bbe1e86e8 Mon Sep 17 00:00:00 2001 From: tahahawa Date: Thu, 8 May 2025 01:00:29 -0400 Subject: [PATCH] build up our own HelmExecutor --- harmony/src/modules/helm/chart.rs | 2 +- harmony/src/modules/helm/command.rs | 225 +++++++++++++++++++++++----- 2 files changed, 187 insertions(+), 40 deletions(-) diff --git a/harmony/src/modules/helm/chart.rs b/harmony/src/modules/helm/chart.rs index ec90cd9..76c2b45 100644 --- a/harmony/src/modules/helm/chart.rs +++ b/harmony/src/modules/helm/chart.rs @@ -42,7 +42,7 @@ pub struct HelmChartScore { pub values_yaml: Option, pub create_namespace: bool, - /// Wether to run `helm upgrade --install` under the hood or only install when not present + /// Whether to run `helm upgrade --install` under the hood or only install when not present pub install_only: bool, pub repository: Option, } diff --git a/harmony/src/modules/helm/command.rs b/harmony/src/modules/helm/command.rs index b9a9f11..84c13b7 100644 --- a/harmony/src/modules/helm/command.rs +++ b/harmony/src/modules/helm/command.rs @@ -1,68 +1,215 @@ use std::collections::HashMap; +use std::ffi::OsStr; +use std::io::ErrorKind; use std::path::{Path, PathBuf}; use std::process::{Command, Output}; -pub struct HelmCommand { +#[derive(Clone)] +pub struct HelmCommandExecutor { pub env: HashMap, pub path: PathBuf, pub args: Vec, - pub api_versions: Vec, + pub api_versions: Option>, pub kube_version: String, - pub debug: bool, + pub debug: Option, pub globals: HelmGlobals, pub chart: HelmChart, } +#[derive(Clone)] pub struct HelmGlobals { chart_home: PathBuf, config_home: PathBuf, } -enum MergeMode { - Merge, - Override, - Replace, -} - +#[derive(Clone)] pub struct HelmChart { - name: String, - version: String, - repo: String, - release_name: String, - namespace: String, - additional_values_files: Vec, - values_file: PathBuf, - values_inline: serde_yaml::Mapping, - values_merge: MergeMode, - include_crds: bool, - skip_hooks: bool, - api_versions: Vec, - kube_version: String, - name_template: String, - skip_tests: bool, - debug: bool, + pub name: String, + pub version: Option, + pub repo: Option, + pub release_name: Option, + pub namespace: Option, + pub additional_values_files: Vec, + pub values_file: Option, + pub values_inline: HashMap, + pub include_crds: Option, + pub skip_hooks: Option, + pub api_versions: Option>, + pub kube_version: Option, + pub name_template: String, + pub skip_tests: Option, + pub debug: Option, } -impl HelmCommand { - pub fn generate(self) -> Result { - self.run_command(vec![]) +impl HelmCommandExecutor { + pub fn generate(&self) -> Result { + if self + .clone() + .chart + .clone() + .chart_exists_locally(self.clone().globals.chart_home) + .is_none() + { + if self.chart.repo.is_none() { + return Err(std::io::Error::new( + ErrorKind::Other, + "Chart doesn't exist locally and no repo specified", + )); + } + self.clone().run_command( + self.chart + .clone() + .pull_command(self.globals.chart_home.clone()), + )?; + } + + self.clone().run_command( + self.chart + .clone() + .helm_args(self.globals.chart_home.clone()), + ) } pub fn version(self) -> Result { - self.run_command(vec!["version", "-c", "--short"]) + self.run_command(vec![ + "version".to_string(), + "-c".to_string(), + "--short".to_string(), + ]) } - pub fn run_command(self, mut args: Vec<&str>) -> Result { - if self.debug { - args.push("--debug"); + pub fn run_command(mut self, mut args: Vec) -> Result { + if let Some(d) = self.debug { + if d { + args.push("--debug".to_string()); + } } - Command::new(self.path) - .envs(self.env) - .env("HELM_CONFIG_HOME", &self.globals.config_home) - .env("HELM_CACHE_HOME", &self.globals.config_home) - .env("HELM_DATA_HOME", &self.globals.config_home) - .args(args) - .output() + self.env.insert( + "HELM_CONFIG_HOME".to_string(), + self.globals.config_home.to_str().unwrap().to_string(), + ); + self.env.insert( + "HELM_CACHE_HOME".to_string(), + self.globals.config_home.to_str().unwrap().to_string(), + ); + self.env.insert( + "HELM_DATA_HOME".to_string(), + self.globals.config_home.to_str().unwrap().to_string(), + ); + + Command::new(self.path).envs(self.env).args(args).output() + } +} + +impl HelmChart { + pub fn chart_exists_locally(self, chart_home: PathBuf) -> Option { + let chart_path = + PathBuf::from(chart_home.to_str().unwrap().to_string() + "/" + &self.name.to_string()); + + if chart_path.exists() { + Some(chart_path) + } else { + None + } + } + + pub fn pull_command(self, chart_home: PathBuf) -> Vec { + let mut args = vec![ + "pull".to_string(), + "--untar".to_string(), + "--untardir".to_string(), + chart_home.to_str().unwrap().to_string(), + ]; + + match self.repo { + Some(r) => { + if r.starts_with("oci://") { + args.push(String::from( + r.trim_end_matches("/").to_string() + "/" + self.name.clone().as_str(), + )); + } else { + args.push("--repo".to_string()); + args.push(r); + + args.push(self.name); + } + } + None => args.push(self.name), + }; + + match self.version { + Some(v) => { + args.push("--version".to_string()); + args.push(v); + } + None => (), + } + + args + } + + pub fn helm_args(self, chart_home: PathBuf) -> Vec { + let mut args = vec![]; + + match self.release_name { + Some(rn) => args.push(rn), + None => args.push("--generate-name".to_string()), + } + + args.push( + PathBuf::from(chart_home.to_str().unwrap().to_string() + "/" + self.name.as_str()) + .to_str() + .unwrap() + .to_string(), + ); + + if let Some(n) = self.namespace { + args.push("--namespace".to_string()); + args.push(n); + } + + if let Some(f) = self.values_file { + args.push("-f".to_string()); + args.push(f.to_str().unwrap().to_string()); + } + + if let Some(vv) = self.api_versions { + for v in vv { + args.push("--api-versions".to_string()); + args.push(v); + } + } + + if let Some(kv) = self.kube_version { + args.push("--kube-version".to_string()); + args.push(kv); + } + + if let Some(crd) = self.include_crds { + if crd { + args.push("--include-crds".to_string()); + } + } + + if let Some(st) = self.skip_tests { + if st { + args.push("--skip-tests".to_string()); + } + } + + if let Some(sh) = self.skip_hooks { + if sh { + args.push("--no-hooks".to_string()); + } + } + + if let Some(d) = self.debug { + if d { + args.push("--debug".to_string()); + } + } + + args } }