From ebc93dc6d05c1e11cbcd63025c76d9f57f6e80db Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Fri, 12 Jan 2024 18:40:05 +0100 Subject: [PATCH] Relative paths for included charts and oci support --- examples/use/charts/vaultwarden.yaml | 6 +- helmule.yaml | 8 +++ src/config/extension.rs | 2 +- src/config/include.rs | 92 +++++++++++++++++++++++----- src/config/patch.rs | 56 ++++++++++++++++- src/source/helm.rs | 51 ++++++++++++++- 6 files changed, 188 insertions(+), 27 deletions(-) diff --git a/examples/use/charts/vaultwarden.yaml b/examples/use/charts/vaultwarden.yaml index 9dec3cc..59ee196 100644 --- a/examples/use/charts/vaultwarden.yaml +++ b/examples/use/charts/vaultwarden.yaml @@ -4,14 +4,14 @@ version: latest extensions: - name: Add virtual service to the chartc target_dir: templates/extensions - source_dir: ./examples/extensions/vaultwarden + source_dir: ../../extensions/vaultwarden patches: - name: Git patch 1 git: - path: ./examples/patches/git/patch.diff + path: ../../patches/git/patch.diff - name: Git patch 2 git: - path: ./examples/patches/git/patch-2.diff + path: ../../patches/git/patch-2.diff - name: yaml-fmt custom_command: commands: diff --git a/helmule.yaml b/helmule.yaml index e1d7f2e..0c7a237 100644 --- a/helmule.yaml +++ b/helmule.yaml @@ -3,6 +3,9 @@ include: - kind: Charts path: ./examples/use/charts/vaultwardens.yaml repositories: + - name: bitnami-oci + helm: + url: oci://registry-1.docker.io/bitnamicharts/ - name: metrics-server helm: url: https://kubernetes-sigs.github.io/metrics-server/ @@ -18,6 +21,11 @@ repositories: helm: url: https://fluxcd-community.github.io/helm-charts charts: + - name: postgresql + repository: bitnami-oci + version: 13.2.29 + mirrors: + - badhouseplants-git - name: flux2 repository: flux-community extensions: diff --git a/src/config/extension.rs b/src/config/extension.rs index 8d50f0e..4525bd5 100644 --- a/src/config/extension.rs +++ b/src/config/extension.rs @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; pub(crate) struct Extension { name: Option, target_dir: String, - source_dir: String, + pub(crate) source_dir: String, } impl Extension { diff --git a/src/config/include.rs b/src/config/include.rs index 6029133..a825ece 100644 --- a/src/config/include.rs +++ b/src/config/include.rs @@ -1,5 +1,5 @@ use log::info; -use serde::{Serialize, Deserialize}; +use serde::{Deserialize, Serialize}; #[derive(Serialize, Deserialize, Eq, Hash, PartialEq, Debug, Clone)] pub(crate) enum IncludeTypes { @@ -23,33 +23,33 @@ pub(crate) fn apply_includes(config: &mut super::Config) -> Result<(), Box { existing_charts.append(&mut charts); existing_charts - }, + } None => charts, }; - config.charts = Some(extended_charts); - }, + config.charts = Some(extended_charts); + } IncludeTypes::Mirrors => { let mut mirrors = include_mirrors(include.path)?; let extended_mirrors = match config.mirrors.clone() { Some(mut existing_mirrors) => { existing_mirrors.append(&mut mirrors); existing_mirrors - }, + } None => mirrors, }; config.mirrors = Some(extended_mirrors); - }, + } IncludeTypes::Repositories => { let mut repositories = include_repositories(include.path)?; let extended_repositories = match config.repositories.clone() { Some(mut existing_repositories) => { existing_repositories.append(&mut repositories); existing_repositories - }, + } None => repositories, }; config.repositories = Some(extended_repositories); - }, + } }; } Ok(()) @@ -58,17 +58,74 @@ pub(crate) fn apply_includes(config: &mut super::Config) -> Result<(), Box Result, Box> { info!("trying to include chart from {}", path.clone()); let file = std::fs::File::open(path.clone())?; - let charts: Vec = match serde_yaml::from_reader(file) { + + let chart_dir = match std::path::Path::new(&path).parent() { + Some(dir) => match dir.to_str() { + Some(dir) => dir.to_string(), + None => { + return Err(Box::from(format!( + "chart parrent dir not found for {}", + path + ))); + } + }, + None => { + return Err(Box::from(format!( + "chart parrent dir not found for {}", + path + ))); + } + }; + + let mut charts: Vec = match serde_yaml::from_reader(file) { Ok(res) => res, Err(_) => { let file = std::fs::File::open(path.clone())?; let chart: super::Chart = serde_yaml::from_reader(file)?; - vec!(chart) - }, + vec![chart] + } }; + + charts.iter_mut().for_each(|chart| { + match chart.extensions { + Some(ref mut extensions) => extensions.iter_mut().for_each(|extension| { + if is_path_relative(extension.source_dir.clone()) { + let clean_path = match extension.source_dir.clone().starts_with("./") { + true => extension.source_dir.clone().replacen("./", "", 1), + false => extension.source_dir.clone(), + }; + if is_path_relative(clean_path.clone()) { + let new_path = format!("{}/{}", chart_dir, clean_path); + extension.source_dir = new_path; + } + } + }), + None => info!("no extensions set, nothing to update"), + }; + match chart.patches { + Some(ref mut patches) => patches.iter_mut().for_each(| patch| { + if is_path_relative(patch.get_path().clone()) { + let clean_path = match patch.get_path().clone().starts_with("./") { + true => patch.get_path().clone().replacen("./", "", 1), + false => patch.get_path().clone(), + }; + if is_path_relative(clean_path.clone()) { + let new_path = format!("{}/{}", chart_dir, clean_path); + patch.set_path(new_path); + } + } + }), + None => info!("no patch set, nothing to update"), + }; + + }); Ok(charts) } +fn is_path_relative(path: String) -> bool { + !path.starts_with("/") +} + fn include_mirrors(path: String) -> Result, Box> { info!("trying to include chart from {}", path.clone()); let file = std::fs::File::open(path.clone())?; @@ -77,13 +134,15 @@ fn include_mirrors(path: String) -> Result, Box { let file = std::fs::File::open(path.clone())?; let chart: super::Mirror = serde_yaml::from_reader(file)?; - vec!(chart) - }, + vec![chart] + } }; Ok(mirrors) } -fn include_repositories(path: String) -> Result, Box> { +fn include_repositories( + path: String, +) -> Result, Box> { info!("trying to include chart from {}", path.clone()); let file = std::fs::File::open(path.clone())?; let repositories: Vec = match serde_yaml::from_reader(file) { @@ -91,9 +150,8 @@ fn include_repositories(path: String) -> Result, Box { let file = std::fs::File::open(path.clone())?; let chart: super::Repository = serde_yaml::from_reader(file)?; - vec!(chart) - }, + vec![chart] + } }; Ok(repositories) } - diff --git a/src/config/patch.rs b/src/config/patch.rs index db99573..6eec37a 100644 --- a/src/config/patch.rs +++ b/src/config/patch.rs @@ -52,10 +52,25 @@ impl Patch { let patch_action = patch_action_from_definition(self.clone())?; patch_action.apply(chart_local_path) } + pub(crate) fn get_path(&self) -> String { + let patch_action = patch_action_from_definition(self.clone()).unwrap(); + patch_action.get_path() + } + pub(crate) fn set_path(&mut self, path: String) { + if let Some(ref mut regexp) = self.regexp { + regexp.path = path; + } else if let Some(ref mut git) = self.git { + git.path = path; + } else if let Some(ref mut yq) = self.yq { + yq.file = path + } + } } trait PatchInterface { fn apply(&self, chart_local_path: String) -> Result<(), Box>; + fn get_path(&self) -> String; + fn set_path(&mut self, new_path: String); } impl PatchInterface for YqPatch { @@ -86,6 +101,14 @@ impl PatchInterface for YqPatch { cli_exec_from_dir(cmd, chart_local_path)?; Ok(()) } + + fn get_path(&self) -> String { + self.file.clone() + } + + fn set_path(&mut self, new_path: String) { + self.file = new_path + } } impl PatchInterface for RegexpPatch { @@ -145,6 +168,14 @@ impl PatchInterface for RegexpPatch { } Ok(()) } + + fn get_path(&self) -> String { + self.path.clone() + } + + fn set_path(&mut self, new_path: String) { + self.path = new_path + } } impl PatchInterface for GitPatch { @@ -157,6 +188,14 @@ impl PatchInterface for GitPatch { remove_dir_all(chart_local_path + "/.git")?; Ok(()) } + + fn get_path(&self) -> String { + self.path.clone() + } + + fn set_path(&mut self, new_path: String) { + self.path = new_path + } } impl PatchInterface for CustomCommandPatch { @@ -166,6 +205,15 @@ impl PatchInterface for CustomCommandPatch { } Ok(()) } + + fn get_path(&self) -> String { + // Empty stings, cause cc patch doesn't have a path + "".to_string() + } + + fn set_path(&mut self, _new_path: String) { + () + } } fn patch_action_from_definition( @@ -176,9 +224,11 @@ fn patch_action_from_definition( } else if let Some(git) = patch.git { return Ok(Box::new(GitPatch { path: { - let path = PathBuf::from(git.path); - let can_path = fs::canonicalize(path).ok().unwrap(); - can_path.into_os_string().into_string().ok().unwrap() + let path = PathBuf::from(git.path.clone()); + match fs::canonicalize(path).ok(){ + Some(can_path) => can_path.into_os_string().into_string().ok().unwrap(), + None => git.path.clone(), + } }, })); } else if let Some(custom_command) = patch.custom_command { diff --git a/src/source/helm.rs b/src/source/helm.rs index 0375d23..3ff7dc8 100644 --- a/src/source/helm.rs +++ b/src/source/helm.rs @@ -46,6 +46,53 @@ impl Helm { } } + fn pull_oci( + &self, + workdir_path: String, + vars: HashMap, + ) -> Result> { + let args = match self.version.as_str() { + LATEST_VERSION => "".to_string(), + _ => format!("--version {}", self.version.clone()), + }; + let repo = match self.repository_url.ends_with("/"){ + true => { + let mut repo = self.repository_url.clone(); + repo.pop(); + repo + }, + false => self.repository_url.clone(), + }; + let cmd = format!( + "helm pull {}/{} {} --destination {} --untar", + repo, &self.chart, args, workdir_path + ); + cli_exec(cmd)?; + // Get the version + let cmd = format!("helm show chart {}/{}", workdir_path, &self.chart); + let helm_stdout = cli_exec(cmd)?; + let old_dir_name = format!("{}/{}", workdir_path, &self.chart); + let new_dir_name: String; + match serde_yaml::from_str::(&helm_stdout) { + Ok(res) => { + new_dir_name = format!("{}-{}", old_dir_name, res.version); + rename(old_dir_name, new_dir_name.clone())?; + } + Err(err) => return Err(Box::from(err)), + }; + + + let cmd = "helm show chart . | yq '.version'".to_string(); + let version = cli_exec_from_dir(cmd, new_dir_name.clone())?; + Ok(ChartLocal { + name: self.chart.clone(), + version, + path: new_dir_name, + repo_url: self.repository_url.clone(), + vars, + }) + } + fn pull_default( &self, workdir_path: String, @@ -105,9 +152,7 @@ impl Repo for Helm { let repository_kind = self.repo_kind_from_url()?; let path = match repository_kind { RepoKind::Default => self.pull_default(workdir_path, vars)?, - RepoKind::Oci => { - todo!() - } + RepoKind::Oci => self.pull_oci(workdir_path, vars)?, }; Ok(path) }