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
This commit is contained in:
Nikolai Rodionov 2023-03-05 22:28:46 +01:00 committed by Nikolai Rodionov
parent 7adb5f0a4a
commit 1c29b32407
9 changed files with 160 additions and 21 deletions

26
.drone.yml Normal file
View File

@ -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

41
Cargo.lock generated
View File

@ -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"

View File

@ -17,3 +17,5 @@ tabled = "0.10.0"
handlebars = "4.3.1"
clap_complete = "4.0.6"
[dev-dependencies]
tempfile = "3.4.0"

View File

@ -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

View File

@ -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

View File

@ -15,7 +15,7 @@ impl Connector for Argo {
type ConnectorType = Argo;
fn get_app(&self) -> Result<Vec<types::HelmChart>> {
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::<Vec<types::HelmChart>>(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()),

View File

@ -14,7 +14,7 @@ pub(crate) struct Helmfile {
impl Connector for Helmfile {
fn get_app(&self) -> Result<Vec<types::HelmChart>> {
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<types::HelmChart> = vec![app];
assert_eq!(apps, helmfile_app);
path.close().unwrap();
}
}

View File

@ -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<ExecResult>, 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<ExecResult>, 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<ExecResult>, local_chart: &types::HelmChart) ->
// Remove "v" from version definitions
let mut versions: Vec<HelmChart> = 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<ExecResult>, 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<ExecResult>, 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
);
}
}

View File

@ -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<String>,
// #[serde(alias = "name", alias = "chart")]
pub(crate) chart: Option<String>,
pub(crate) version: Option<String>,
}
@ -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,