diff --git a/examples/cert_manager/Cargo.toml b/examples/cert_manager/Cargo.toml new file mode 100644 index 0000000..a67fcf8 --- /dev/null +++ b/examples/cert_manager/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "cert_manager" +edition = "2024" +version.workspace = true +readme.workspace = true +license.workspace = true +publish = false + +[dependencies] +harmony = { path = "../../harmony" } +harmony_cli = { path = "../../harmony_cli" } +harmony_types = { path = "../../harmony_types" } +cidr = { workspace = true } +tokio = { workspace = true } +harmony_macros = { path = "../../harmony_macros" } +log = { workspace = true } +env_logger = { workspace = true } +url = { workspace = true } +assert_cmd = "2.0.16" diff --git a/examples/cert_manager/src/main.rs b/examples/cert_manager/src/main.rs new file mode 100644 index 0000000..ee0a203 --- /dev/null +++ b/examples/cert_manager/src/main.rs @@ -0,0 +1,26 @@ +use harmony::{ + inventory::Inventory, + modules::{ + cert_manager::{ + capability::CertificateManagementConfig, score_k8s::CertificateManagementScore, + }, + postgresql::{PostgreSQLScore, capability::PostgreSQLConfig}, + }, + topology::K8sAnywhereTopology, +}; + +#[tokio::main] +async fn main() { + let cert_manager = CertificateManagementScore { + config: CertificateManagementConfig {}, + }; + + harmony_cli::run( + Inventory::autoload(), + K8sAnywhereTopology::from_env(), + vec![Box::new(cert_manager)], + None, + ) + .await + .unwrap(); +} diff --git a/harmony/src/domain/topology/k8s_anywhere/k8s_anywhere.rs b/harmony/src/domain/topology/k8s_anywhere/k8s_anywhere.rs index 22dfaad..c259eff 100644 --- a/harmony/src/domain/topology/k8s_anywhere/k8s_anywhere.rs +++ b/harmony/src/domain/topology/k8s_anywhere/k8s_anywhere.rs @@ -17,6 +17,10 @@ use crate::{ interpret::InterpretStatus, inventory::Inventory, modules::{ + cert_manager::{ + capability::{CertificateManagement, CertificateManagementConfig}, + operator::CertManagerOperatorScore, + }, k3d::K3DInstallationScore, k8s::ingress::{K8sIngressScore, PathType}, monitoring::{ @@ -359,6 +363,27 @@ impl Serialize for K8sAnywhereTopology { } } +#[async_trait] +impl CertificateManagement for K8sAnywhereTopology { + async fn install( + &self, + config: &CertificateManagementConfig, + ) -> Result { + let cert_management_operator = CertManagerOperatorScore::default(); + + cert_management_operator + .interpret(&Inventory::empty(), self) + .await + .map_err(|e| PreparationError { msg: e.to_string() })?; + Ok(PreparationOutcome::Success { + details: format!( + "Installed cert-manager into ns: {}", + cert_management_operator.namespace + ), + }) + } +} + impl K8sAnywhereTopology { pub fn from_env() -> Self { Self { diff --git a/harmony/src/modules/cert_manager/capability.rs b/harmony/src/modules/cert_manager/capability.rs new file mode 100644 index 0000000..fffe537 --- /dev/null +++ b/harmony/src/modules/cert_manager/capability.rs @@ -0,0 +1,18 @@ +use async_trait::async_trait; +use serde::Serialize; + +use crate::{ + interpret::Outcome, + topology::{PreparationError, PreparationOutcome}, +}; + +#[async_trait] +pub trait CertificateManagement: Send + Sync { + async fn install( + &self, + config: &CertificateManagementConfig, + ) -> Result; +} + +#[derive(Debug, Clone, Serialize)] +pub struct CertificateManagementConfig {} diff --git a/harmony/src/modules/cert_manager/mod.rs b/harmony/src/modules/cert_manager/mod.rs index 032439e..3e4da4f 100644 --- a/harmony/src/modules/cert_manager/mod.rs +++ b/harmony/src/modules/cert_manager/mod.rs @@ -1,3 +1,6 @@ +pub mod capability; pub mod cluster_issuer; mod helm; +pub mod operator; +pub mod score_k8s; pub use helm::*; diff --git a/harmony/src/modules/cert_manager/operator.rs b/harmony/src/modules/cert_manager/operator.rs new file mode 100644 index 0000000..e4112db --- /dev/null +++ b/harmony/src/modules/cert_manager/operator.rs @@ -0,0 +1,64 @@ +use kube::api::ObjectMeta; +use serde::Serialize; + +use crate::{ + interpret::Interpret, + modules::k8s::{ + apps::crd::{Subscription, SubscriptionSpec}, + resource::K8sResourceScore, + }, + score::Score, + topology::{K8sclient, Topology, k8s::K8sClient}, +}; + +/// Install the Cert-Manager Operator via RedHat Community Operators registry.redhat.io/redhat/community-operator-index:v4.19 +/// This Score creates a Subscription CR in the specified namespace + +#[derive(Debug, Clone, Serialize)] +pub struct CertManagerOperatorScore { + pub namespace: String, + pub channel: String, + pub install_plan_approval: String, + pub source: String, + pub source_namespace: String, +} + +impl Default for CertManagerOperatorScore { + fn default() -> Self { + Self { + namespace: "openshift-operators".to_string(), + channel: "stable".to_string(), + install_plan_approval: "Automatic".to_string(), + source: "community-operators".to_string(), + source_namespace: "openshift-marketplace".to_string(), + } + } +} + +impl Score for CertManagerOperatorScore { + fn name(&self) -> String { + "CertManagerOperatorScore".to_string() + } + + fn create_interpret(&self) -> Box> { + let metadata = ObjectMeta { + name: Some("cert-manager".to_string()), + namespace: Some(self.namespace.clone()), + ..ObjectMeta::default() + }; + + let spec = SubscriptionSpec { + channel: Some(self.channel.clone()), + config: None, + install_plan_approval: Some(self.install_plan_approval.clone()), + name: "cert-manager".to_string(), + source: self.source.clone(), + source_namespace: self.source_namespace.clone(), + starting_csv: None, + }; + + let subscription = Subscription { metadata, spec }; + + K8sResourceScore::single(subscription, Some(self.namespace.clone())).create_interpret() + } +} diff --git a/harmony/src/modules/cert_manager/score_k8s.rs b/harmony/src/modules/cert_manager/score_k8s.rs new file mode 100644 index 0000000..18e0826 --- /dev/null +++ b/harmony/src/modules/cert_manager/score_k8s.rs @@ -0,0 +1,66 @@ +use async_trait::async_trait; +use harmony_types::id::Id; +use serde::Serialize; + +use crate::{ + data::Version, + interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, + inventory::Inventory, + modules::cert_manager::capability::{CertificateManagement, CertificateManagementConfig}, + score::Score, + topology::Topology, +}; + +#[derive(Debug, Clone, Serialize)] +pub struct CertificateManagementScore { + pub config: CertificateManagementConfig, +} + +impl Score for CertificateManagementScore { + fn name(&self) -> String { + "CertificateManagementScore".to_string() + } + + fn create_interpret(&self) -> Box> { + Box::new(CertificateManagementInterpret { + config: self.config.clone(), + }) + } +} + +#[derive(Debug)] +struct CertificateManagementInterpret { + config: CertificateManagementConfig, +} + +#[async_trait] +impl Interpret for CertificateManagementInterpret { + async fn execute( + &self, + inventory: &Inventory, + topology: &T, + ) -> Result { + let cert_management = topology + .install(&self.config) + .await + .map_err(|e| InterpretError::new(e.to_string()))?; + + Ok(Outcome::success(format!("Installed CertificateManagement"))) + } + + fn get_name(&self) -> InterpretName { + InterpretName::Custom("CertificateManagementInterpret") + } + + fn get_version(&self) -> Version { + todo!() + } + + fn get_status(&self) -> InterpretStatus { + todo!() + } + + fn get_children(&self) -> Vec { + todo!() + } +}