feat: add cert-manager module and helm repo support
- Implemented a new `cert-manager` module for deploying cert-manager. - Added support for specifying a Helm repository in module configurations. - Introduced `cert_manager` module in `modules/mod.rs`. - Created `src/modules/cert_manager` directory and its associated code. - Implemented `add_repo` function in `src/modules/helm.rs` for adding Helm repositories. - Updated `LAMPInterpret` and `lamp.rs` to integrate the new module. - Added logging for Helm command execution. - Updated k8s deployment file to remove unused DeepMerge dependency.
This commit is contained in:
parent
78e9893341
commit
4be008556e
@ -1,6 +1,11 @@
|
||||
use derive_new::new;
|
||||
use k8s_openapi::NamespaceResourceScope;
|
||||
use kube::{Api, Client, Error, Resource, api::PostParams};
|
||||
use kube::{
|
||||
Api, Client, Config, Error, Resource,
|
||||
api::PostParams,
|
||||
config::{KubeConfigOptions, Kubeconfig},
|
||||
};
|
||||
use log::error;
|
||||
use serde::de::DeserializeOwned;
|
||||
|
||||
#[derive(new)]
|
||||
@ -57,4 +62,22 @@ impl K8sClient {
|
||||
}
|
||||
todo!("")
|
||||
}
|
||||
|
||||
pub(crate) async fn from_kubeconfig(path: &str) -> Option<K8sClient> {
|
||||
let k = match Kubeconfig::read_from(path) {
|
||||
Ok(k) => k,
|
||||
Err(e) => {
|
||||
error!("Failed to load kubeconfig from {path} : {e}");
|
||||
return None;
|
||||
}
|
||||
};
|
||||
Some(K8sClient::new(
|
||||
Client::try_from(
|
||||
Config::from_custom_kubeconfig(k, &KubeConfigOptions::default())
|
||||
.await
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,12 +17,13 @@ use super::{HelmCommand, K8sclient, Topology, k8s::K8sClient};
|
||||
|
||||
struct K8sState {
|
||||
client: Arc<K8sClient>,
|
||||
_source: K8sSource,
|
||||
source: K8sSource,
|
||||
message: String,
|
||||
}
|
||||
|
||||
enum K8sSource {
|
||||
LocalK3d,
|
||||
Kubeconfig,
|
||||
}
|
||||
|
||||
pub struct K8sAnywhereTopology {
|
||||
@ -75,7 +76,7 @@ impl K8sAnywhereTopology {
|
||||
}
|
||||
|
||||
async fn try_load_kubeconfig(&self, path: &str) -> Option<K8sClient> {
|
||||
todo!("Use kube-rs to load kubeconfig at path {path}");
|
||||
K8sClient::from_kubeconfig(path).await
|
||||
}
|
||||
|
||||
fn get_k3d_installation_score(&self) -> K3DInstallationScore {
|
||||
@ -109,8 +110,18 @@ impl K8sAnywhereTopology {
|
||||
|
||||
if let Some(kubeconfig) = k8s_anywhere_config.kubeconfig {
|
||||
match self.try_load_kubeconfig(&kubeconfig).await {
|
||||
Some(_client) => todo!(),
|
||||
None => todo!(),
|
||||
Some(client) => {
|
||||
return Ok(Some(K8sState {
|
||||
client: Arc::new(client),
|
||||
source: K8sSource::Kubeconfig,
|
||||
message: format!("Loaded k8s client from kubeconfig {kubeconfig}"),
|
||||
}));
|
||||
}
|
||||
None => {
|
||||
return Err(InterpretError::new(format!(
|
||||
"Failed to load kubeconfig from {kubeconfig}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -142,7 +153,7 @@ impl K8sAnywhereTopology {
|
||||
let state = match k3d.get_client().await {
|
||||
Ok(client) => K8sState {
|
||||
client: Arc::new(K8sClient::new(client)),
|
||||
_source: K8sSource::LocalK3d,
|
||||
source: K8sSource::LocalK3d,
|
||||
message: "Successfully installed K3D cluster and acquired client".to_string(),
|
||||
},
|
||||
Err(_) => todo!(),
|
||||
|
||||
46
harmony/src/modules/cert_manager/helm.rs
Normal file
46
harmony/src/modules/cert_manager/helm.rs
Normal file
@ -0,0 +1,46 @@
|
||||
use std::{collections::HashMap, str::FromStr};
|
||||
|
||||
use non_blank_string_rs::NonBlankString;
|
||||
use serde::Serialize;
|
||||
use url::Url;
|
||||
|
||||
use crate::{
|
||||
modules::helm::chart::{HelmChartScore, HelmRepository},
|
||||
score::Score,
|
||||
topology::{HelmCommand, Topology},
|
||||
};
|
||||
|
||||
#[derive(Debug, Serialize, Clone)]
|
||||
pub struct CertManagerHelmScore {}
|
||||
|
||||
impl<T: Topology + HelmCommand> Score<T> for CertManagerHelmScore {
|
||||
fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret<T>> {
|
||||
let mut values_overrides = HashMap::new();
|
||||
values_overrides.insert(
|
||||
NonBlankString::from_str("crds.enabled").unwrap(),
|
||||
"true".to_string(),
|
||||
);
|
||||
let values_overrides = Some(values_overrides);
|
||||
|
||||
HelmChartScore {
|
||||
namespace: Some(NonBlankString::from_str("cert-manager").unwrap()),
|
||||
release_name: NonBlankString::from_str("cert-manager").unwrap(),
|
||||
chart_name: NonBlankString::from_str("jetstack/cert-manager").unwrap(),
|
||||
chart_version: None,
|
||||
values_overrides,
|
||||
values_yaml: None,
|
||||
create_namespace: true,
|
||||
install_only: true,
|
||||
repository: Some(HelmRepository::new(
|
||||
"jetstack".to_string(),
|
||||
Url::parse("https://charts.jetstack.io").unwrap(),
|
||||
true,
|
||||
)),
|
||||
}
|
||||
.create_interpret()
|
||||
}
|
||||
|
||||
fn name(&self) -> String {
|
||||
format!("CertManagerHelmScore")
|
||||
}
|
||||
}
|
||||
2
harmony/src/modules/cert_manager/mod.rs
Normal file
2
harmony/src/modules/cert_manager/mod.rs
Normal file
@ -0,0 +1,2 @@
|
||||
mod helm;
|
||||
pub use helm::*;
|
||||
@ -6,13 +6,31 @@ use crate::topology::{HelmCommand, Topology};
|
||||
use async_trait::async_trait;
|
||||
use helm_wrapper_rs;
|
||||
use helm_wrapper_rs::blocking::{DefaultHelmExecutor, HelmExecutor};
|
||||
use log::info;
|
||||
use log::{debug, error, info, warn};
|
||||
pub use non_blank_string_rs::NonBlankString;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::process::{Command, Output, Stdio};
|
||||
use std::str::FromStr;
|
||||
use temp_file::TempFile;
|
||||
use url::Url;
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct HelmRepository {
|
||||
name: String,
|
||||
url: Url,
|
||||
force_update: bool,
|
||||
}
|
||||
impl HelmRepository {
|
||||
pub(crate) fn new(name: String, url: Url, force_update: bool) -> Self {
|
||||
Self {
|
||||
name,
|
||||
url,
|
||||
force_update,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct HelmChartScore {
|
||||
@ -26,6 +44,7 @@ pub struct HelmChartScore {
|
||||
|
||||
/// Wether to run `helm upgrade --install` under the hood or only install when not present
|
||||
pub install_only: bool,
|
||||
pub repository: Option<HelmRepository>,
|
||||
}
|
||||
|
||||
impl<T: Topology + HelmCommand> Score<T> for HelmChartScore {
|
||||
@ -44,6 +63,77 @@ impl<T: Topology + HelmCommand> Score<T> for HelmChartScore {
|
||||
pub struct HelmChartInterpret {
|
||||
pub score: HelmChartScore,
|
||||
}
|
||||
impl HelmChartInterpret {
|
||||
fn add_repo(&self) -> Result<(), InterpretError> {
|
||||
let repo = match &self.score.repository {
|
||||
Some(repo) => repo,
|
||||
None => {
|
||||
info!("No Helm repository specified in the score. Skipping repository setup.");
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
info!(
|
||||
"Ensuring Helm repository exists: Name='{}', URL='{}', ForceUpdate={}",
|
||||
repo.name, repo.url, repo.force_update
|
||||
);
|
||||
|
||||
let mut add_args = vec!["repo", "add", &repo.name, repo.url.as_str()];
|
||||
if repo.force_update {
|
||||
add_args.push("--force-update");
|
||||
}
|
||||
|
||||
let add_output = run_helm_command(&add_args)?;
|
||||
let full_output = format!(
|
||||
"{}\n{}",
|
||||
String::from_utf8_lossy(&add_output.stdout),
|
||||
String::from_utf8_lossy(&add_output.stderr)
|
||||
);
|
||||
|
||||
match add_output.status.success() {
|
||||
true => {
|
||||
return Ok(());
|
||||
}
|
||||
false => {
|
||||
return Err(InterpretError::new(format!(
|
||||
"Failed to add helm repository!\n{full_output}"
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn run_helm_command(args: &[&str]) -> Result<Output, InterpretError> {
|
||||
let command_str = format!("helm {}", args.join(" "));
|
||||
debug!("Running Helm command: `{}`", command_str);
|
||||
|
||||
let output = Command::new("helm")
|
||||
.args(args)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.output()
|
||||
.map_err(|e| {
|
||||
InterpretError::new(format!(
|
||||
"Failed to execute helm command '{}': {}. Is helm installed and in PATH?",
|
||||
command_str, e
|
||||
))
|
||||
})?;
|
||||
|
||||
if !output.status.success() {
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
let stderr = String::from_utf8_lossy(&output.stderr);
|
||||
warn!(
|
||||
"Helm command `{}` failed with status: {}\nStdout:\n{}\nStderr:\n{}",
|
||||
command_str, output.status, stdout, stderr
|
||||
);
|
||||
} else {
|
||||
debug!(
|
||||
"Helm command `{}` finished successfully. Status: {}",
|
||||
command_str, output.status
|
||||
);
|
||||
}
|
||||
|
||||
Ok(output)
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Topology + HelmCommand> Interpret<T> for HelmChartInterpret {
|
||||
@ -67,6 +157,8 @@ impl<T: Topology + HelmCommand> Interpret<T> for HelmChartInterpret {
|
||||
None => None,
|
||||
};
|
||||
|
||||
self.add_repo()?;
|
||||
|
||||
let helm_executor = DefaultHelmExecutor::new();
|
||||
|
||||
let mut helm_options = Vec::new();
|
||||
|
||||
@ -179,6 +179,7 @@ impl LAMPInterpret {
|
||||
create_namespace: true,
|
||||
install_only: true,
|
||||
values_yaml: None,
|
||||
repository: None,
|
||||
};
|
||||
|
||||
score.create_interpret().execute(inventory, topology).await
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
pub mod cert_manager;
|
||||
pub mod dhcp;
|
||||
pub mod dns;
|
||||
pub mod dummy;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user