initial helm support/scaffolding

This commit is contained in:
Taha Hawa 2025-04-23 14:09:20 -04:00
parent 0bc17d1dc7
commit b7dd3b50d9
8 changed files with 102 additions and 37 deletions

24
Cargo.lock generated
View File

@ -1343,12 +1343,14 @@ dependencies = [
"env_logger", "env_logger",
"harmony_macros", "harmony_macros",
"harmony_types", "harmony_types",
"helm-wrapper-rs",
"http 1.3.1", "http 1.3.1",
"inquire", "inquire",
"k8s-openapi", "k8s-openapi",
"kube", "kube",
"libredfish", "libredfish",
"log", "log",
"non-blank-string-rs",
"opnsense-config", "opnsense-config",
"opnsense-config-xml", "opnsense-config-xml",
"reqwest 0.11.27", "reqwest 0.11.27",
@ -1453,6 +1455,19 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "helm-wrapper-rs"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc9253a7bbf4ba8ff6052d5ab7ddc6e2ca17cd8481d15636fb9f64611653880c"
dependencies = [
"log",
"non-blank-string-rs",
"serde",
"serde_json",
"thiserror 1.0.69",
]
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.3.9" version = "0.3.9"
@ -2328,6 +2343,15 @@ dependencies = [
"unicode-segmentation", "unicode-segmentation",
] ]
[[package]]
name = "non-blank-string-rs"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a05a02248b2e70f1943a59af287a28df78ef9adfc72ee5dc443381d3a1a1a5c"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "num-bigint" name = "num-bigint"
version = "0.4.6" version = "0.4.6"

View File

@ -19,6 +19,7 @@ async fn main() {
harmony_cli::init(maestro, None).await.unwrap(); harmony_cli::init(maestro, None).await.unwrap();
} }
#[allow(unused)]
use assert_cmd::Command; use assert_cmd::Command;
#[test] #[test]

View File

@ -31,3 +31,5 @@ serde_yaml = { workspace = true }
http = { workspace = true } http = { workspace = true }
serde-value = { workspace = true } serde-value = { workspace = true }
inquire.workspace = true inquire.workspace = true
helm-wrapper-rs = "0.4.0"
non-blank-string-rs = "1.0.4"

View File

@ -0,0 +1 @@
pub trait HelmCommand {}

View File

@ -20,6 +20,9 @@ pub use network::*;
use serde::Serialize; use serde::Serialize;
pub use tftp::*; pub use tftp::*;
mod helm_command;
pub use helm_command::*;
use std::net::IpAddr; use std::net::IpAddr;
use super::interpret::{InterpretError, Outcome}; use super::interpret::{InterpretError, Outcome};

View File

@ -370,10 +370,13 @@ mod tests {
let result = get_servers_for_backend(&backend, &haproxy); let result = get_servers_for_backend(&backend, &haproxy);
// Check the result // Check the result
assert_eq!(result, vec![BackendServer { assert_eq!(
address: "192.168.1.1".to_string(), result,
port: 80, vec![BackendServer {
},]); address: "192.168.1.1".to_string(),
port: 80,
},]
);
} }
#[test] #[test]
fn test_get_servers_for_backend_no_linked_servers() { fn test_get_servers_for_backend_no_linked_servers() {
@ -430,15 +433,18 @@ mod tests {
// Call the function // Call the function
let result = get_servers_for_backend(&backend, &haproxy); let result = get_servers_for_backend(&backend, &haproxy);
// Check the result // Check the result
assert_eq!(result, vec![ assert_eq!(
BackendServer { result,
address: "some-hostname.test.mcd".to_string(), vec![
port: 80, BackendServer {
}, address: "some-hostname.test.mcd".to_string(),
BackendServer { port: 80,
address: "192.168.1.2".to_string(), },
port: 8080, BackendServer {
}, address: "192.168.1.2".to_string(),
]); port: 8080,
},
]
);
} }
} }

View File

@ -1 +1 @@
pub mod resource; pub mod resource;

View File

@ -2,51 +2,79 @@ use crate::data::{Id, Version};
use crate::interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}; use crate::interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome};
use crate::inventory::Inventory; use crate::inventory::Inventory;
use crate::score::Score; use crate::score::Score;
use crate::topology::Topology; use crate::topology::{HelmCommand, Topology};
use async_trait::async_trait; use async_trait::async_trait;
use helm_wrapper_rs; use helm_wrapper_rs;
use helm_wrapper_rs::blocking::{DefaultHelmExecutor, HelmExecutor};
use non_blank_string_rs::NonBlankString;
use serde::Serialize; use serde::Serialize;
use serde::de::DeserializeOwned;
use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize)] #[derive(Debug, Clone, Serialize)]
pub struct HelmResourceScore { pub struct HelmChartScore {
pub namespace: String, pub namespace: NonBlankString,
pub release_name: String, pub release_name: NonBlankString,
pub chart_name: String, pub chart_name: NonBlankString,
pub chart_version: String, pub chart_version: NonBlankString,
pub values_overrides: String, pub values_overrides: Option<HashMap<NonBlankString, String>>,
pub values_file: String,
pub helm_options: String,
} }
impl<T: Topology + std::fmt::Debug + Sync + Default + serde::Serialize + 'static + Send + Clone> impl<T: Topology> Score<T> for HelmChartScore {
Score<T> for HelmResourceScore
{
fn create_interpret(&self) -> Box<dyn Interpret<T>> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
todo!() todo!()
} }
fn name(&self) -> String { fn name(&self) -> String {
format!("{}, {}", self.release_name, self.chart_name) "HelmChartScore".to_string()
} }
} }
#[derive(Debug, Serialize)] #[derive(Debug, Serialize)]
pub struct HelmResourceInterpret { pub struct HelmChartInterpret {
pub score: HelmResourceScore, pub score: HelmChartScore,
} }
#[async_trait] #[async_trait]
impl<T: Topology + Clone + std::fmt::Debug + serde::Serialize + Default + Send + Sync> Interpret<T> impl<T: Topology + HelmCommand> Interpret<T> for HelmChartInterpret {
for HelmResourceInterpret
{
async fn execute( async fn execute(
&self, &self,
_inventory: &Inventory, _inventory: &Inventory,
topology: &T, _topology: &T,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
Ok(Outcome::success( let helm_executor = DefaultHelmExecutor::new();
"Successfully applied resource".to_string(), let res = helm_executor.install_or_upgrade(
)) &self.score.namespace,
&self.score.release_name,
&self.score.chart_name,
Some(&self.score.chart_version),
self.score.values_overrides.as_ref(),
None,
None,
);
let status = match res {
Ok(status) => status,
Err(err) => return Err(InterpretError::new(err.to_string())),
};
match status {
helm_wrapper_rs::HelmDeployStatus::Deployed => Ok(Outcome::new(
InterpretStatus::SUCCESS,
"Helm Chart deployed".to_string(),
)),
helm_wrapper_rs::HelmDeployStatus::PendingInstall => Ok(Outcome::new(
InterpretStatus::RUNNING,
"Helm Chart Pending install".to_string(),
)),
helm_wrapper_rs::HelmDeployStatus::PendingUpgrade => Ok(Outcome::new(
InterpretStatus::RUNNING,
"Helm Chart pending upgrade".to_string(),
)),
helm_wrapper_rs::HelmDeployStatus::Failed => Err(InterpretError::new(
"Failed to install helm chart".to_string(),
)),
}
} }
fn get_name(&self) -> InterpretName { fn get_name(&self) -> InterpretName {
todo!() todo!()