From 1c29b32407c6d0fb388af5a62096ad662ba3d7b5 Mon Sep 17 00:00:00 2001 From: Nikolai Rodionov Date: Sun, 5 Mar 2023 22:28:46 +0100 Subject: [PATCH] fix: helmfile and argocd are working again - The tool was totally broken, not it works again - Development has been moved to my Gitea server - A basic test for helmfile is added - A basic drone pipeline is added --- .drone.yml | 26 ++++++++++++++++++++ Cargo.lock | 41 +++++++++++++++++++++++++++++++ Cargo.toml | 2 ++ examples/argocd/application.yaml | 18 ++++++++++++++ examples/helmfile.yaml | 19 ++++++++------- src/connectors/argo.rs | 4 +-- src/connectors/helmfile.rs | 42 +++++++++++++++++++++++++++++++- src/main.rs | 22 +++++++++++------ src/types/mod.rs | 7 +++++- 9 files changed, 160 insertions(+), 21 deletions(-) create mode 100644 .drone.yml create mode 100644 examples/argocd/application.yaml diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..5074759 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,26 @@ +--- +kind: pipeline +type: kubernetes +name: Tests + +trigger: + event: + - push + +steps: + - name: Prepare helm and helmfile + image: ghcr.io/helmfile/helmfile:canary + commands: + - mkdir -p bin + - cp $(which helm) ./bin/helm + - cp $(which helmfile) ./bin/helmfile + + - name: Unit tests + image: rust:slim + environment: + CARGO_BUILD_JOBS: 1 + commands: + - export PATH=$PWD/bin:$PATH + - helm + - helmfile + - cargo test diff --git a/Cargo.lock b/Cargo.lock index bdd2b39..760318a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -57,6 +57,7 @@ dependencies = [ "serde_json", "serde_yaml", "tabled", + "tempfile", "version-compare", ] @@ -175,6 +176,15 @@ dependencies = [ "libc", ] +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "fnv" version = "1.0.7" @@ -242,6 +252,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "io-lifetimes" version = "1.0.2" @@ -406,6 +425,15 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_syscall" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +dependencies = [ + "bitflags", +] + [[package]] name = "regex" version = "1.6.0" @@ -539,6 +567,19 @@ dependencies = [ "syn", ] +[[package]] +name = "tempfile" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys", +] + [[package]] name = "termcolor" version = "1.1.3" diff --git a/Cargo.toml b/Cargo.toml index f690782..f827973 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,3 +17,5 @@ tabled = "0.10.0" handlebars = "4.3.1" clap_complete = "4.0.6" +[dev-dependencies] +tempfile = "3.4.0" diff --git a/examples/argocd/application.yaml b/examples/argocd/application.yaml new file mode 100644 index 0000000..cd2cc2a --- /dev/null +++ b/examples/argocd/application.yaml @@ -0,0 +1,18 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: sealed-secrets + namespace: argocd +spec: + project: default + source: + chart: argo-cd + repoURL: https://argoproj.github.io/argo-helm + targetRevision: 5.23.3 + helm: + releaseName: sealed-secrets + syncPolicy: + automated: {} + destination: + server: "https://kubernetes.default.svc" + namespace: argocd diff --git a/examples/helmfile.yaml b/examples/helmfile.yaml index 271e7f2..4add955 100644 --- a/examples/helmfile.yaml +++ b/examples/helmfile.yaml @@ -1,14 +1,15 @@ repositories: - - name: keel - url: https://charts.keel.sh + - name: argo + url: https://argoproj.github.io/argo-helm releases: - # ----------------------------- - # -- Keel - # ----------------------------- - - name: keel + - name: argocd installed: true - namespace: keel-system + namespace: argocd createNamespace: true - chart: keel/keel - version: 0.9.10 + chart: argo/argo-cd + version: 5.23.3 + values: + - server: + extraArgs: + - --insecure diff --git a/src/connectors/argo.rs b/src/connectors/argo.rs index ecd2ed0..3bc7fb9 100644 --- a/src/connectors/argo.rs +++ b/src/connectors/argo.rs @@ -15,7 +15,7 @@ impl Connector for Argo { type ConnectorType = Argo; fn get_app(&self) -> Result> { - let cmd: String = "argocd app list -o json | jq '[.[] | {chart: .spec.source.chart, version: .spec.source.targetRevision}]'".to_string(); + let cmd: String = "argocd app list -o json | jq '[.[] | {chart: .spec.source.chart, version: .spec.source.targetRevision, name: .spec.source.helm.releaseName}]'".to_string(); debug!("executing '${}'", cmd); let output = Command::new("bash") @@ -27,7 +27,7 @@ impl Connector for Argo { match from_str::>(Borrow::borrow(&helm_stdout)) { Ok(mut charts) => { - charts.dedup(); + charts.iter_mut().for_each(|chart| {chart.chart = Some(format!("{}/{}", chart.chart.clone().unwrap(), chart.chart.clone().unwrap()))}); Ok(charts) } Err(err) => Err(err.into()), diff --git a/src/connectors/helmfile.rs b/src/connectors/helmfile.rs index ca495ae..524c2ed 100644 --- a/src/connectors/helmfile.rs +++ b/src/connectors/helmfile.rs @@ -14,7 +14,7 @@ pub(crate) struct Helmfile { impl Connector for Helmfile { fn get_app(&self) -> Result> { let cmd: String = format!( - "helmfile -f {} -e {} list --output json | jq '[.[] | {{chart: .name, version: .version}}]'", + "helmfile -f {} -e {} list --output json | jq '[.[] | {{chart: .chart, version: .version, name: .name}}]'", self.path, self.env ) @@ -48,8 +48,48 @@ impl Connector for Helmfile { type ConnectorType = Helmfile; } + impl Helmfile { pub(crate) fn init(path: String, env: String) -> Self { Self {path, env} } } + +#[cfg(test)] +mod tests { + use tempfile::NamedTempFile; + use std::io::Write; + use crate::connectors::{Helmfile, Connector}; + use crate::types; + + static HELMFILE_EXAMPLE: &str = "repositories:\n + - name: argo\n + url: https://argoproj.github.io/argo-helm\n +releases:\n + - name: argocd\n + installed: true\n + namespace: argocd\n + createNamespace: true\n + chart: argo/argo-cd\n + version: 5.23.3\n + values:\n + - server:\n + extraArgs:\n + - --insecure"; + + #[test] + fn test_helmfile() { + let mut file = NamedTempFile::new().unwrap(); + writeln!(file, "{}", HELMFILE_EXAMPLE.clone()).unwrap(); + let path = file.into_temp_path(); + let helmfile_app = Helmfile::init(path.to_string_lossy().to_string(), "default".to_string()).get_app().unwrap(); + let app = types::HelmChart{ + chart: Some("argo/argo-cd".to_string()), + name: Some("argocd".to_string()), + version: Some("5.23.3".to_string()), + }; + let apps: Vec = vec![app]; + assert_eq!(apps, helmfile_app); + path.close().unwrap(); + } +} diff --git a/src/main.rs b/src/main.rs index 7939b39..99648b0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -109,6 +109,7 @@ fn main() { } charts.iter().for_each(|a| { + debug!("{:?}", a); check_chart(&mut result, a).unwrap(); }); @@ -128,9 +129,9 @@ fn main() { } fn check_chart(result: &mut Vec, local_chart: &types::HelmChart) -> Result<()> { - if local_chart.name.is_some() { + if local_chart.chart.is_some() { let version = local_chart.version.clone().unwrap(); - let chart = local_chart.name.clone().unwrap(); + let chart = local_chart.chart.clone().unwrap(); return match version.is_empty() { true => { warn!( @@ -142,8 +143,8 @@ fn check_chart(result: &mut Vec, local_chart: &types::HelmChart) -> false => { info!("checking {} - {}", chart, version); let cmd = format!( - "helm search repo {}/{} --versions --output json", - chart, chart + "helm search repo {} --versions --output json", + chart, ); debug!("executing '${}'", cmd); let output = Command::new("bash") @@ -156,6 +157,8 @@ fn check_chart(result: &mut Vec, local_chart: &types::HelmChart) -> // Remove "v" from version definitions let mut versions: Vec = from_str(helm_stdout.borrow()).unwrap(); versions.iter_mut().for_each(|f| { + f.name = local_chart.name.clone(); + f.chart = local_chart.chart.clone(); if f.version.is_some() { f.version = Some(f.version.as_ref().unwrap().replace('v', "")); } @@ -172,8 +175,10 @@ fn check_chart(result: &mut Vec, local_chart: &types::HelmChart) -> ); } let remote = Version::from(current_version.as_str()).unwrap(); + debug!("{:?}", versions); let status: Status = if versions.contains(&HelmChart { - name: Some(format!("{}/{}", chart.clone(), chart.clone())), + chart: Some(chart.clone()), + name: local_chart.name.clone(), version: Some(version.clone()), }) { match local.compare(remote.clone()) { @@ -187,6 +192,7 @@ fn check_chart(result: &mut Vec, local_chart: &types::HelmChart) -> }; result.push(ExecResult::new( + local_chart.name.clone().unwrap(), chart.clone(), current_version.clone(), version.clone(), @@ -214,21 +220,21 @@ fn handle_result( } for r in result.clone() { match r.status { - Status::Uptodate => info!("{} is up-to-date", r.name), + Status::Uptodate => info!("{} is up-to-date", r.chart), Status::Outdated => { if outdated_fail { failed = true } warn!( "{} is outdated. Current version is {}, but the latest is {}", - r.name, r.current_version, r.latest_version + r.chart, r.current_version, r.latest_version ); } Status::Missing => { failed = true; error!( "{} is broken. Current version is {}, but it can't be found in the repo", - r.name, r.current_version + r.chart, r.current_version ); } } diff --git a/src/types/mod.rs b/src/types/mod.rs index ca312a6..254f160 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -5,8 +5,10 @@ use tabled::Tabled; /// Struct for parsing charts info from helmfile #[derive(Serialize, Deserialize, Debug, PartialEq, Eq)] pub(crate) struct HelmChart { - #[serde(alias = "name", alias = "chart")] + // #[serde(alias = "name", alias = "chart")] pub(crate) name: Option, + // #[serde(alias = "name", alias = "chart")] + pub(crate) chart: Option, pub(crate) version: Option, } @@ -36,6 +38,7 @@ impl fmt::Display for Status { #[derive(Clone, Tabled, Serialize, Deserialize)] pub(crate) struct ExecResult { pub(crate) name: String, + pub(crate) chart: String, pub(crate) latest_version: String, pub(crate) current_version: String, pub(crate) status: Status, @@ -44,12 +47,14 @@ pub(crate) struct ExecResult { impl ExecResult { pub(crate) fn new( name: String, + chart: String, latest_version: String, current_version: String, status: Status, ) -> Self { Self { name, + chart, latest_version, current_version, status,