Compare commits
	
		
			No commits in common. "4df451bc419a0ac0aa5136e0cd91b97540c39376" and "b3ae4e6611ef9ec6b8615570b15430cf6de75e7d" have entirely different histories.
		
	
	
		
			4df451bc41
			...
			b3ae4e6611
		
	
		
| @ -34,7 +34,6 @@ pub enum InterpretName { | |||||||
|     CephClusterHealth, |     CephClusterHealth, | ||||||
|     Custom(&'static str), |     Custom(&'static str), | ||||||
|     RHOBAlerting, |     RHOBAlerting, | ||||||
|     K8sIngress, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl std::fmt::Display for InterpretName { | impl std::fmt::Display for InterpretName { | ||||||
| @ -65,7 +64,6 @@ impl std::fmt::Display for InterpretName { | |||||||
|             InterpretName::CephClusterHealth => f.write_str("CephClusterHealth"), |             InterpretName::CephClusterHealth => f.write_str("CephClusterHealth"), | ||||||
|             InterpretName::Custom(name) => f.write_str(name), |             InterpretName::Custom(name) => f.write_str(name), | ||||||
|             InterpretName::RHOBAlerting => f.write_str("RHOBAlerting"), |             InterpretName::RHOBAlerting => f.write_str("RHOBAlerting"), | ||||||
|             InterpretName::K8sIngress => f.write_str("K8sIngress"), |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
| @ -84,15 +82,13 @@ pub trait Interpret<T>: std::fmt::Debug + Send { | |||||||
| pub struct Outcome { | pub struct Outcome { | ||||||
|     pub status: InterpretStatus, |     pub status: InterpretStatus, | ||||||
|     pub message: String, |     pub message: String, | ||||||
|     pub details: Vec<String>, |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl Outcome { | impl Outcome { | ||||||
|     pub fn noop(message: String) -> Self { |     pub fn noop() -> Self { | ||||||
|         Self { |         Self { | ||||||
|             status: InterpretStatus::NOOP, |             status: InterpretStatus::NOOP, | ||||||
|             message, |             message: String::new(), | ||||||
|             details: vec![], |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -100,23 +96,6 @@ impl Outcome { | |||||||
|         Self { |         Self { | ||||||
|             status: InterpretStatus::SUCCESS, |             status: InterpretStatus::SUCCESS, | ||||||
|             message, |             message, | ||||||
|             details: vec![], |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn success_with_details(message: String, details: Vec<String>) -> Self { |  | ||||||
|         Self { |  | ||||||
|             status: InterpretStatus::SUCCESS, |  | ||||||
|             message, |  | ||||||
|             details, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn running(message: String) -> Self { |  | ||||||
|         Self { |  | ||||||
|             status: InterpretStatus::RUNNING, |  | ||||||
|             message, |  | ||||||
|             details: vec![], |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,10 +1,7 @@ | |||||||
| use std::error::Error; |  | ||||||
| 
 |  | ||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| use derive_new::new; |  | ||||||
| use serde::Serialize; | use serde::Serialize; | ||||||
| 
 | 
 | ||||||
| use crate::{executors::ExecutorError, topology::Topology}; | use crate::topology::Topology; | ||||||
| 
 | 
 | ||||||
| /// An ApplicationFeature provided by harmony, such as Backups, Monitoring, MultisiteAvailability,
 | /// An ApplicationFeature provided by harmony, such as Backups, Monitoring, MultisiteAvailability,
 | ||||||
| /// ContinuousIntegration, ContinuousDelivery
 | /// ContinuousIntegration, ContinuousDelivery
 | ||||||
| @ -12,10 +9,7 @@ use crate::{executors::ExecutorError, topology::Topology}; | |||||||
| pub trait ApplicationFeature<T: Topology>: | pub trait ApplicationFeature<T: Topology>: | ||||||
|     std::fmt::Debug + Send + Sync + ApplicationFeatureClone<T> |     std::fmt::Debug + Send + Sync + ApplicationFeatureClone<T> | ||||||
| { | { | ||||||
|     async fn ensure_installed( |     async fn ensure_installed(&self, topology: &T) -> Result<(), String>; | ||||||
|         &self, |  | ||||||
|         topology: &T, |  | ||||||
|     ) -> Result<InstallationOutcome, InstallationError>; |  | ||||||
|     fn name(&self) -> String; |     fn name(&self) -> String; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -46,60 +40,3 @@ impl<T: Topology> Clone for Box<dyn ApplicationFeature<T>> { | |||||||
|         self.clone_box() |         self.clone_box() | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| #[derive(Debug, Clone, PartialEq, Eq)] |  | ||||||
| pub enum InstallationOutcome { |  | ||||||
|     Success { details: Vec<String> }, |  | ||||||
|     Noop, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl InstallationOutcome { |  | ||||||
|     pub fn success() -> Self { |  | ||||||
|         Self::Success { details: vec![] } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn success_with_details(details: Vec<String>) -> Self { |  | ||||||
|         Self::Success { details } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     pub fn noop() -> Self { |  | ||||||
|         Self::Noop |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[derive(Debug, Clone, new)] |  | ||||||
| pub struct InstallationError { |  | ||||||
|     msg: String, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl std::fmt::Display for InstallationError { |  | ||||||
|     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |  | ||||||
|         f.write_str(&self.msg) |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl Error for InstallationError {} |  | ||||||
| 
 |  | ||||||
| impl From<ExecutorError> for InstallationError { |  | ||||||
|     fn from(value: ExecutorError) -> Self { |  | ||||||
|         Self { |  | ||||||
|             msg: format!("InstallationError : {value}"), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl From<kube::Error> for InstallationError { |  | ||||||
|     fn from(value: kube::Error) -> Self { |  | ||||||
|         Self { |  | ||||||
|             msg: format!("InstallationError : {value}"), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| impl From<String> for InstallationError { |  | ||||||
|     fn from(value: String) -> Self { |  | ||||||
|         Self { |  | ||||||
|             msg: format!("PreparationError : {value}"), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ use async_trait::async_trait; | |||||||
| use log::info; | use log::info; | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     modules::application::{ApplicationFeature, InstallationError, InstallationOutcome}, |     modules::application::ApplicationFeature, | ||||||
|     topology::{K8sclient, Topology}, |     topology::{K8sclient, Topology}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| @ -29,10 +29,7 @@ impl Default for PublicEndpoint { | |||||||
| /// For now we only suport K8s ingress, but we will support more stuff at some point
 | /// For now we only suport K8s ingress, but we will support more stuff at some point
 | ||||||
| #[async_trait] | #[async_trait] | ||||||
| impl<T: Topology + K8sclient + 'static> ApplicationFeature<T> for PublicEndpoint { | impl<T: Topology + K8sclient + 'static> ApplicationFeature<T> for PublicEndpoint { | ||||||
|     async fn ensure_installed( |     async fn ensure_installed(&self, _topology: &T) -> Result<(), String> { | ||||||
|         &self, |  | ||||||
|         _topology: &T, |  | ||||||
|     ) -> Result<InstallationOutcome, InstallationError> { |  | ||||||
|         info!( |         info!( | ||||||
|             "Making sure public endpoint is installed for port {}", |             "Making sure public endpoint is installed for port {}", | ||||||
|             self.application_port |             self.application_port | ||||||
|  | |||||||
| @ -1,6 +1,4 @@ | |||||||
| use crate::modules::application::{ | use crate::modules::application::{Application, ApplicationFeature}; | ||||||
|     Application, ApplicationFeature, InstallationError, InstallationOutcome, |  | ||||||
| }; |  | ||||||
| use crate::modules::monitoring::application_monitoring::application_monitoring_score::ApplicationMonitoringScore; | use crate::modules::monitoring::application_monitoring::application_monitoring_score::ApplicationMonitoringScore; | ||||||
| use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::CRDPrometheus; | use crate::modules::monitoring::kube_prometheus::crd::crd_alertmanager_config::CRDPrometheus; | ||||||
| use crate::topology::MultiTargetTopology; | use crate::topology::MultiTargetTopology; | ||||||
| @ -45,10 +43,7 @@ impl< | |||||||
|         + std::fmt::Debug, |         + std::fmt::Debug, | ||||||
| > ApplicationFeature<T> for Monitoring | > ApplicationFeature<T> for Monitoring | ||||||
| { | { | ||||||
|     async fn ensure_installed( |     async fn ensure_installed(&self, topology: &T) -> Result<(), String> { | ||||||
|         &self, |  | ||||||
|         topology: &T, |  | ||||||
|     ) -> Result<InstallationOutcome, InstallationError> { |  | ||||||
|         info!("Ensuring monitoring is available for application"); |         info!("Ensuring monitoring is available for application"); | ||||||
|         let namespace = topology |         let namespace = topology | ||||||
|             .get_tenant_config() |             .get_tenant_config() | ||||||
| @ -108,7 +103,7 @@ impl< | |||||||
|             .await |             .await | ||||||
|             .map_err(|e| e.to_string())?; |             .map_err(|e| e.to_string())?; | ||||||
| 
 | 
 | ||||||
|         Ok(InstallationOutcome::success()) |         Ok(()) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn name(&self) -> String { |     fn name(&self) -> String { | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ use crate::{ | |||||||
|     data::Version, |     data::Version, | ||||||
|     inventory::Inventory, |     inventory::Inventory, | ||||||
|     modules::application::{ |     modules::application::{ | ||||||
|         ApplicationFeature, HelmPackage, InstallationError, InstallationOutcome, OCICompliant, |         ApplicationFeature, HelmPackage, OCICompliant, | ||||||
|         features::{ArgoApplication, ArgoHelmScore}, |         features::{ArgoApplication, ArgoHelmScore}, | ||||||
|     }, |     }, | ||||||
|     score::Score, |     score::Score, | ||||||
| @ -141,10 +141,7 @@ impl< | |||||||
|     T: Topology + HelmCommand + MultiTargetTopology + K8sclient + Ingress + 'static, |     T: Topology + HelmCommand + MultiTargetTopology + K8sclient + Ingress + 'static, | ||||||
| > ApplicationFeature<T> for PackagingDeployment<A> | > ApplicationFeature<T> for PackagingDeployment<A> | ||||||
| { | { | ||||||
|     async fn ensure_installed( |     async fn ensure_installed(&self, topology: &T) -> Result<(), String> { | ||||||
|         &self, |  | ||||||
|         topology: &T, |  | ||||||
|     ) -> Result<InstallationOutcome, InstallationError> { |  | ||||||
|         let image = self.application.image_name(); |         let image = self.application.image_name(); | ||||||
|         let domain = topology |         let domain = topology | ||||||
|             .get_domain(&self.application.name()) |             .get_domain(&self.application.name()) | ||||||
| @ -208,11 +205,7 @@ impl< | |||||||
|                     .unwrap(); |                     .unwrap(); | ||||||
|             } |             } | ||||||
|         }; |         }; | ||||||
| 
 |         Ok(()) | ||||||
|         Ok(InstallationOutcome::success_with_details(vec![format!( |  | ||||||
|             "{}: http://{domain}", |  | ||||||
|             self.application.name() |  | ||||||
|         )])) |  | ||||||
|     } |     } | ||||||
|     fn name(&self) -> String { |     fn name(&self) -> String { | ||||||
|         "ContinuousDelivery".to_string() |         "ContinuousDelivery".to_string() | ||||||
|  | |||||||
| @ -1,8 +1,6 @@ | |||||||
| use std::sync::Arc; | use std::sync::Arc; | ||||||
| 
 | 
 | ||||||
| use crate::modules::application::{ | use crate::modules::application::{Application, ApplicationFeature}; | ||||||
|     Application, ApplicationFeature, InstallationError, InstallationOutcome, |  | ||||||
| }; |  | ||||||
| use crate::modules::monitoring::application_monitoring::application_monitoring_score::ApplicationMonitoringScore; | use crate::modules::monitoring::application_monitoring::application_monitoring_score::ApplicationMonitoringScore; | ||||||
| use crate::modules::monitoring::application_monitoring::rhobs_application_monitoring_score::ApplicationRHOBMonitoringScore; | use crate::modules::monitoring::application_monitoring::rhobs_application_monitoring_score::ApplicationRHOBMonitoringScore; | ||||||
| 
 | 
 | ||||||
| @ -45,10 +43,7 @@ impl< | |||||||
|         + PrometheusApplicationMonitoring<RHOBObservability>, |         + PrometheusApplicationMonitoring<RHOBObservability>, | ||||||
| > ApplicationFeature<T> for Monitoring | > ApplicationFeature<T> for Monitoring | ||||||
| { | { | ||||||
|     async fn ensure_installed( |     async fn ensure_installed(&self, topology: &T) -> Result<(), String> { | ||||||
|         &self, |  | ||||||
|         topology: &T, |  | ||||||
|     ) -> Result<InstallationOutcome, InstallationError> { |  | ||||||
|         info!("Ensuring monitoring is available for application"); |         info!("Ensuring monitoring is available for application"); | ||||||
|         let namespace = topology |         let namespace = topology | ||||||
|             .get_tenant_config() |             .get_tenant_config() | ||||||
| @ -111,7 +106,7 @@ impl< | |||||||
|             .interpret(&Inventory::empty(), topology) |             .interpret(&Inventory::empty(), topology) | ||||||
|             .await |             .await | ||||||
|             .map_err(|e| e.to_string())?; |             .map_err(|e| e.to_string())?; | ||||||
|         Ok(InstallationOutcome::success()) |         Ok(()) | ||||||
|     } |     } | ||||||
|     fn name(&self) -> String { |     fn name(&self) -> String { | ||||||
|         "Monitoring".to_string() |         "Monitoring".to_string() | ||||||
|  | |||||||
| @ -24,8 +24,8 @@ use harmony_types::id::Id; | |||||||
| #[derive(Clone, Debug)] | #[derive(Clone, Debug)] | ||||||
| pub enum ApplicationFeatureStatus { | pub enum ApplicationFeatureStatus { | ||||||
|     Installing, |     Installing, | ||||||
|     Installed { details: Vec<String> }, |     Installed, | ||||||
|     Failed { message: String }, |     Failed { details: String }, | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| pub trait Application: std::fmt::Debug + Send + Sync { | pub trait Application: std::fmt::Debug + Send + Sync { | ||||||
| @ -65,32 +65,27 @@ impl<A: Application, T: Topology + std::fmt::Debug> Interpret<T> for Application | |||||||
|             .unwrap(); |             .unwrap(); | ||||||
| 
 | 
 | ||||||
|             let _ = match feature.ensure_installed(topology).await { |             let _ = match feature.ensure_installed(topology).await { | ||||||
|                 Ok(outcome) => { |                 Ok(()) => { | ||||||
|                     instrumentation::instrument(HarmonyEvent::ApplicationFeatureStateChanged { |                     instrumentation::instrument(HarmonyEvent::ApplicationFeatureStateChanged { | ||||||
|                         topology: topology.name().into(), |                         topology: topology.name().into(), | ||||||
|                         application: self.application.name(), |                         application: self.application.name(), | ||||||
|                         feature: feature.name(), |                         feature: feature.name(), | ||||||
|                         status: ApplicationFeatureStatus::Installed { |                         status: ApplicationFeatureStatus::Installed, | ||||||
|                             details: match outcome { |  | ||||||
|                                 InstallationOutcome::Success { details } => details, |  | ||||||
|                                 InstallationOutcome::Noop => vec![], |  | ||||||
|                             }, |  | ||||||
|                         }, |  | ||||||
|                     }) |                     }) | ||||||
|                     .unwrap(); |                     .unwrap(); | ||||||
|                 } |                 } | ||||||
|                 Err(error) => { |                 Err(msg) => { | ||||||
|                     instrumentation::instrument(HarmonyEvent::ApplicationFeatureStateChanged { |                     instrumentation::instrument(HarmonyEvent::ApplicationFeatureStateChanged { | ||||||
|                         topology: topology.name().into(), |                         topology: topology.name().into(), | ||||||
|                         application: self.application.name(), |                         application: self.application.name(), | ||||||
|                         feature: feature.name(), |                         feature: feature.name(), | ||||||
|                         status: ApplicationFeatureStatus::Failed { |                         status: ApplicationFeatureStatus::Failed { | ||||||
|                             message: error.to_string(), |                             details: msg.clone(), | ||||||
|                         }, |                         }, | ||||||
|                     }) |                     }) | ||||||
|                     .unwrap(); |                     .unwrap(); | ||||||
|                     return Err(InterpretError::new(format!( |                     return Err(InterpretError::new(format!( | ||||||
|                         "Application Interpret failed to install feature : {error}" |                         "Application Interpret failed to install feature : {msg}" | ||||||
|                     ))); |                     ))); | ||||||
|                 } |                 } | ||||||
|             }; |             }; | ||||||
|  | |||||||
| @ -69,14 +69,17 @@ impl DhcpInterpret { | |||||||
| 
 | 
 | ||||||
|         dhcp_server.set_pxe_options(pxe_options).await?; |         dhcp_server.set_pxe_options(pxe_options).await?; | ||||||
| 
 | 
 | ||||||
|         Ok(Outcome::success(format!( |         Ok(Outcome::new( | ||||||
|  |             InterpretStatus::SUCCESS, | ||||||
|  |             format!( | ||||||
|                 "Dhcp Interpret Set next boot to [{:?}], boot_filename to [{:?}], filename to [{:?}], filename64 to [{:?}], filenameipxe to [:{:?}]", |                 "Dhcp Interpret Set next boot to [{:?}], boot_filename to [{:?}], filename to [{:?}], filename64 to [{:?}], filenameipxe to [:{:?}]", | ||||||
|                 self.score.boot_filename, |                 self.score.boot_filename, | ||||||
|                 self.score.boot_filename, |                 self.score.boot_filename, | ||||||
|                 self.score.filename, |                 self.score.filename, | ||||||
|                 self.score.filename64, |                 self.score.filename64, | ||||||
|                 self.score.filenameipxe |                 self.score.filenameipxe | ||||||
|         ))) |             ), | ||||||
|  |         )) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -119,7 +122,8 @@ impl<T: Topology + DhcpServer> Interpret<T> for DhcpInterpret { | |||||||
| 
 | 
 | ||||||
|         topology.commit_config().await?; |         topology.commit_config().await?; | ||||||
| 
 | 
 | ||||||
|         Ok(Outcome::success( |         Ok(Outcome::new( | ||||||
|  |             InterpretStatus::SUCCESS, | ||||||
|             "Dhcp Interpret execution successful".to_string(), |             "Dhcp Interpret execution successful".to_string(), | ||||||
|         )) |         )) | ||||||
|     } |     } | ||||||
| @ -193,10 +197,10 @@ impl DhcpHostBindingInterpret { | |||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         Ok(Outcome::success(format!( |         Ok(Outcome::new( | ||||||
|             "Dhcp Interpret registered {} entries", |             InterpretStatus::SUCCESS, | ||||||
|             number_new_entries |             format!("Dhcp Interpret registered {} entries", number_new_entries), | ||||||
|         ))) |         )) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -232,9 +236,12 @@ impl<T: DhcpServer> Interpret<T> for DhcpHostBindingInterpret { | |||||||
| 
 | 
 | ||||||
|         topology.commit_config().await?; |         topology.commit_config().await?; | ||||||
| 
 | 
 | ||||||
|         Ok(Outcome::success(format!( |         Ok(Outcome::new( | ||||||
|  |             InterpretStatus::SUCCESS, | ||||||
|  |             format!( | ||||||
|                 "Dhcp Host Binding Interpret execution successful on {} hosts", |                 "Dhcp Host Binding Interpret execution successful on {} hosts", | ||||||
|                 self.score.host_binding.len() |                 self.score.host_binding.len() | ||||||
|         ))) |             ), | ||||||
|  |         )) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -55,7 +55,8 @@ impl DnsInterpret { | |||||||
|             dns.register_dhcp_leases(register).await?; |             dns.register_dhcp_leases(register).await?; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         Ok(Outcome::success( |         Ok(Outcome::new( | ||||||
|  |             InterpretStatus::SUCCESS, | ||||||
|             "DNS Interpret execution successfull".to_string(), |             "DNS Interpret execution successfull".to_string(), | ||||||
|         )) |         )) | ||||||
|     } |     } | ||||||
| @ -67,10 +68,13 @@ impl DnsInterpret { | |||||||
|         let entries = &self.score.dns_entries; |         let entries = &self.score.dns_entries; | ||||||
|         dns_server.ensure_hosts_registered(entries.clone()).await?; |         dns_server.ensure_hosts_registered(entries.clone()).await?; | ||||||
| 
 | 
 | ||||||
|         Ok(Outcome::success(format!( |         Ok(Outcome::new( | ||||||
|  |             InterpretStatus::SUCCESS, | ||||||
|  |             format!( | ||||||
|                 "DnsInterpret registered {} hosts successfully", |                 "DnsInterpret registered {} hosts successfully", | ||||||
|                 entries.len() |                 entries.len() | ||||||
|         ))) |             ), | ||||||
|  |         )) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -107,7 +111,8 @@ impl<T: Topology + DnsServer> Interpret<T> for DnsInterpret { | |||||||
| 
 | 
 | ||||||
|         topology.commit_config().await?; |         topology.commit_config().await?; | ||||||
| 
 | 
 | ||||||
|         Ok(Outcome::success( |         Ok(Outcome::new( | ||||||
|  |             InterpretStatus::SUCCESS, | ||||||
|             "Dns Interpret execution successful".to_string(), |             "Dns Interpret execution successful".to_string(), | ||||||
|         )) |         )) | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -197,10 +197,13 @@ impl<T: Topology + HelmCommand> Interpret<T> for HelmChartInterpret { | |||||||
|                     self.score.release_name, ns |                     self.score.release_name, ns | ||||||
|                 ); |                 ); | ||||||
| 
 | 
 | ||||||
|                 return Ok(Outcome::success(format!( |                 return Ok(Outcome::new( | ||||||
|  |                     InterpretStatus::SUCCESS, | ||||||
|  |                     format!( | ||||||
|                         "Helm Chart '{}' already installed to namespace {ns} and install_only=true", |                         "Helm Chart '{}' already installed to namespace {ns} and install_only=true", | ||||||
|                         self.score.release_name |                         self.score.release_name | ||||||
|                 ))); |                     ), | ||||||
|  |                 )); | ||||||
|             } else { |             } else { | ||||||
|                 info!( |                 info!( | ||||||
|                     "Release '{}' not found in namespace '{}'. Proceeding with installation.", |                     "Release '{}' not found in namespace '{}'. Proceeding with installation.", | ||||||
| @ -225,18 +228,18 @@ impl<T: Topology + HelmCommand> Interpret<T> for HelmChartInterpret { | |||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         match status { |         match status { | ||||||
|             helm_wrapper_rs::HelmDeployStatus::Deployed => Ok(Outcome::success(format!( |             helm_wrapper_rs::HelmDeployStatus::Deployed => Ok(Outcome::new( | ||||||
|                 "Helm Chart {} deployed", |                 InterpretStatus::SUCCESS, | ||||||
|                 self.score.release_name |                 format!("Helm Chart {} deployed", self.score.release_name), | ||||||
|             ))), |             )), | ||||||
|             helm_wrapper_rs::HelmDeployStatus::PendingInstall => Ok(Outcome::running(format!( |             helm_wrapper_rs::HelmDeployStatus::PendingInstall => Ok(Outcome::new( | ||||||
|                 "Helm Chart {} pending install...", |                 InterpretStatus::RUNNING, | ||||||
|                 self.score.release_name |                 format!("Helm Chart {} pending install...", self.score.release_name), | ||||||
|             ))), |             )), | ||||||
|             helm_wrapper_rs::HelmDeployStatus::PendingUpgrade => Ok(Outcome::running(format!( |             helm_wrapper_rs::HelmDeployStatus::PendingUpgrade => Ok(Outcome::new( | ||||||
|                 "Helm Chart {} pending upgrade...", |                 InterpretStatus::RUNNING, | ||||||
|                 self.score.release_name |                 format!("Helm Chart {} pending upgrade...", self.score.release_name), | ||||||
|             ))), |             )), | ||||||
|             helm_wrapper_rs::HelmDeployStatus::Failed => Err(InterpretError::new(format!( |             helm_wrapper_rs::HelmDeployStatus::Failed => Err(InterpretError::new(format!( | ||||||
|                 "Helm Chart {} installation failed", |                 "Helm Chart {} installation failed", | ||||||
|                 self.score.release_name |                 self.score.release_name | ||||||
|  | |||||||
| @ -133,9 +133,10 @@ impl<T: Topology> Interpret<T> for DiscoverInventoryAgentInterpret { | |||||||
|             }, |             }, | ||||||
|         ) |         ) | ||||||
|         .await; |         .await; | ||||||
|         Ok(Outcome::success( |         Ok(Outcome { | ||||||
|             "Discovery process completed successfully".to_string(), |             status: InterpretStatus::SUCCESS, | ||||||
|         )) |             message: "Discovery process completed successfully".to_string(), | ||||||
|  |         }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn get_name(&self) -> InterpretName { |     fn get_name(&self) -> InterpretName { | ||||||
|  | |||||||
| @ -1,15 +1,11 @@ | |||||||
| use async_trait::async_trait; |  | ||||||
| use harmony_macros::ingress_path; | use harmony_macros::ingress_path; | ||||||
| use harmony_types::id::Id; |  | ||||||
| use k8s_openapi::api::networking::v1::Ingress; | use k8s_openapi::api::networking::v1::Ingress; | ||||||
| use log::{debug, trace}; | use log::{debug, trace}; | ||||||
| use serde::Serialize; | use serde::Serialize; | ||||||
| use serde_json::json; | use serde_json::json; | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     data::Version, |     interpret::Interpret, | ||||||
|     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, |  | ||||||
|     inventory::Inventory, |  | ||||||
|     score::Score, |     score::Score, | ||||||
|     topology::{K8sclient, Topology}, |     topology::{K8sclient, Topology}, | ||||||
| }; | }; | ||||||
| @ -61,7 +57,7 @@ impl<T: Topology + K8sclient> Score<T> for K8sIngressScore { | |||||||
| 
 | 
 | ||||||
|         let ingress_class = match self.ingress_class_name.clone() { |         let ingress_class = match self.ingress_class_name.clone() { | ||||||
|             Some(ingress_class_name) => ingress_class_name, |             Some(ingress_class_name) => ingress_class_name, | ||||||
|             None => "\"default\"".to_string(), |             None => format!("\"default\""), | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         let ingress = json!( |         let ingress = json!( | ||||||
| @ -101,12 +97,11 @@ impl<T: Topology + K8sclient> Score<T> for K8sIngressScore { | |||||||
|             "Successfully built Ingress for host {:?}", |             "Successfully built Ingress for host {:?}", | ||||||
|             ingress.metadata.name |             ingress.metadata.name | ||||||
|         ); |         ); | ||||||
| 
 |         Box::new(K8sResourceInterpret { | ||||||
|         Box::new(K8sIngressInterpret { |             score: K8sResourceScore::single( | ||||||
|             ingress, |                 ingress.clone(), | ||||||
|             service: self.name.to_string(), |                 self.namespace.clone().map(|f| f.to_string()), | ||||||
|             namespace: self.namespace.clone().map(|f| f.to_string()), |             ), | ||||||
|             host: self.host.clone(), |  | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -114,59 +109,3 @@ impl<T: Topology + K8sclient> Score<T> for K8sIngressScore { | |||||||
|         format!("{} K8sIngressScore", self.name) |         format!("{} K8sIngressScore", self.name) | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 |  | ||||||
| #[derive(std::fmt::Debug)] |  | ||||||
| struct K8sIngressInterpret { |  | ||||||
|     ingress: Ingress, |  | ||||||
|     service: String, |  | ||||||
|     namespace: Option<String>, |  | ||||||
|     host: fqdn::FQDN, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| #[async_trait] |  | ||||||
| impl<T: Topology + K8sclient> Interpret<T> for K8sIngressInterpret { |  | ||||||
|     async fn execute( |  | ||||||
|         &self, |  | ||||||
|         inventory: &Inventory, |  | ||||||
|         topology: &T, |  | ||||||
|     ) -> Result<Outcome, InterpretError> { |  | ||||||
|         let result = K8sResourceInterpret { |  | ||||||
|             score: K8sResourceScore::single(self.ingress.clone(), self.namespace.clone()), |  | ||||||
|         } |  | ||||||
|         .execute(inventory, topology) |  | ||||||
|         .await; |  | ||||||
| 
 |  | ||||||
|         match result { |  | ||||||
|             Ok(outcome) => match outcome.status { |  | ||||||
|                 InterpretStatus::SUCCESS => { |  | ||||||
|                     let details = match &self.namespace { |  | ||||||
|                         Some(namespace) => { |  | ||||||
|                             vec![format!("{} ({namespace}): {}", self.service, self.host)] |  | ||||||
|                         } |  | ||||||
|                         None => vec![format!("{}: {}", self.service, self.host)], |  | ||||||
|                     }; |  | ||||||
| 
 |  | ||||||
|                     Ok(Outcome::success_with_details(outcome.message, details)) |  | ||||||
|                 } |  | ||||||
|                 _ => Ok(outcome), |  | ||||||
|             }, |  | ||||||
|             Err(e) => Err(e), |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn get_name(&self) -> InterpretName { |  | ||||||
|         InterpretName::K8sIngress |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn get_version(&self) -> Version { |  | ||||||
|         Version::from("0.0.1").unwrap() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn get_status(&self) -> InterpretStatus { |  | ||||||
|         todo!() |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     fn get_children(&self) -> Vec<Id> { |  | ||||||
|         vec![] |  | ||||||
|     } |  | ||||||
| } |  | ||||||
|  | |||||||
| @ -68,9 +68,7 @@ impl<T: Topology + PrometheusApplicationMonitoring<CRDPrometheus>> Interpret<T> | |||||||
|                 PreparationOutcome::Success { details: _ } => { |                 PreparationOutcome::Success { details: _ } => { | ||||||
|                     Ok(Outcome::success("Prometheus installed".into())) |                     Ok(Outcome::success("Prometheus installed".into())) | ||||||
|                 } |                 } | ||||||
|                 PreparationOutcome::Noop => { |                 PreparationOutcome::Noop => Ok(Outcome::noop()), | ||||||
|                     Ok(Outcome::noop("Prometheus installation skipped".into())) |  | ||||||
|                 } |  | ||||||
|             }, |             }, | ||||||
|             Err(err) => Err(InterpretError::from(err)), |             Err(err) => Err(InterpretError::from(err)), | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -70,9 +70,7 @@ impl<T: Topology + PrometheusApplicationMonitoring<RHOBObservability>> Interpret | |||||||
|                 PreparationOutcome::Success { details: _ } => { |                 PreparationOutcome::Success { details: _ } => { | ||||||
|                     Ok(Outcome::success("Prometheus installed".into())) |                     Ok(Outcome::success("Prometheus installed".into())) | ||||||
|                 } |                 } | ||||||
|                 PreparationOutcome::Noop => { |                 PreparationOutcome::Noop => Ok(Outcome::noop()), | ||||||
|                     Ok(Outcome::noop("Prometheus installation skipped".into())) |  | ||||||
|                 } |  | ||||||
|             }, |             }, | ||||||
|             Err(err) => Err(InterpretError::from(err)), |             Err(err) => Err(InterpretError::from(err)), | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -113,13 +113,7 @@ impl<T: Topology + HelmCommand + K8sclient + MultiTargetTopology> Interpret<T> f | |||||||
|             .await?; |             .await?; | ||||||
|         info!("user added"); |         info!("user added"); | ||||||
| 
 | 
 | ||||||
|         Ok(Outcome::success_with_details( |         Ok(Outcome::success("Ntfy installed".to_string())) | ||||||
|             "Ntfy installed".to_string(), |  | ||||||
|             vec![format!( |  | ||||||
|                 "Ntfy ({}): http://{}", |  | ||||||
|                 self.score.namespace, self.score.host |  | ||||||
|             )], |  | ||||||
|         )) |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     fn get_name(&self) -> InterpretName { |     fn get_name(&self) -> InterpretName { | ||||||
|  | |||||||
| @ -1,19 +1,19 @@ | |||||||
|  | use async_trait::async_trait; | ||||||
|  | use derive_new::new; | ||||||
|  | use harmony_types::id::Id; | ||||||
|  | use log::{error, info, warn}; | ||||||
|  | use serde::Serialize; | ||||||
|  | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     data::Version, |     data::Version, | ||||||
|     hardware::PhysicalHost, |     hardware::PhysicalHost, | ||||||
|     infra::inventory::InventoryRepositoryFactory, |     infra::inventory::InventoryRepositoryFactory, | ||||||
|     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, |     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, | ||||||
|     inventory::{HostRole, Inventory}, |     inventory::{HostRole, Inventory}, | ||||||
|     modules::inventory::DiscoverHostForRoleScore, |     modules::inventory::{DiscoverHostForRoleScore, LaunchDiscoverInventoryAgentScore}, | ||||||
|     score::Score, |     score::Score, | ||||||
|     topology::HAClusterTopology, |     topology::HAClusterTopology, | ||||||
| }; | }; | ||||||
| use async_trait::async_trait; |  | ||||||
| use derive_new::new; |  | ||||||
| use harmony_types::id::Id; |  | ||||||
| use log::info; |  | ||||||
| use serde::Serialize; |  | ||||||
| 
 |  | ||||||
| // -------------------------------------------------------------------------------------------------
 | // -------------------------------------------------------------------------------------------------
 | ||||||
| // Step 01: Inventory (default PXE + Kickstart in RAM + Rust agent)
 | // Step 01: Inventory (default PXE + Kickstart in RAM + Rust agent)
 | ||||||
| // - This score exposes/ensures the default inventory assets and waits for discoveries.
 | // - This score exposes/ensures the default inventory assets and waits for discoveries.
 | ||||||
| @ -109,9 +109,12 @@ When you can dig them, confirm to continue. | |||||||
|             .await?; |             .await?; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         Ok(Outcome::success(format!( |         Ok(Outcome::new( | ||||||
|  |             InterpretStatus::SUCCESS, | ||||||
|  |             format!( | ||||||
|                 "Found and assigned bootstrap node: {}", |                 "Found and assigned bootstrap node: {}", | ||||||
|                 bootstrap_host.unwrap().summary() |                 bootstrap_host.unwrap().summary() | ||||||
|         ))) |             ), | ||||||
|  |         )) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,13 +1,25 @@ | |||||||
|  | use std::{fmt::Write, path::PathBuf}; | ||||||
|  | 
 | ||||||
|  | use async_trait::async_trait; | ||||||
|  | use derive_new::new; | ||||||
|  | use harmony_secret::SecretManager; | ||||||
|  | use harmony_types::id::Id; | ||||||
|  | use log::{debug, error, info, warn}; | ||||||
|  | use serde::{Deserialize, Serialize}; | ||||||
|  | use tokio::{fs::File, io::AsyncWriteExt, process::Command}; | ||||||
|  | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     config::secret::{RedhatSecret, SshKeyPair}, |     config::secret::{RedhatSecret, SshKeyPair}, | ||||||
|     data::{FileContent, FilePath, Version}, |     data::{FileContent, FilePath, Version}, | ||||||
|     hardware::PhysicalHost, |     hardware::PhysicalHost, | ||||||
|     infra::inventory::InventoryRepositoryFactory, |     infra::inventory::InventoryRepositoryFactory, | ||||||
|  |     instrumentation::{HarmonyEvent, instrument}, | ||||||
|     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, |     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, | ||||||
|     inventory::{HostRole, Inventory}, |     inventory::{HostRole, Inventory}, | ||||||
|     modules::{ |     modules::{ | ||||||
|         dhcp::DhcpHostBindingScore, |         dhcp::DhcpHostBindingScore, | ||||||
|         http::{IPxeMacBootFileScore, StaticFilesHttpScore}, |         http::{IPxeMacBootFileScore, StaticFilesHttpScore}, | ||||||
|  |         inventory::LaunchDiscoverInventoryAgentScore, | ||||||
|         okd::{ |         okd::{ | ||||||
|             bootstrap_load_balancer::OKDBootstrapLoadBalancerScore, |             bootstrap_load_balancer::OKDBootstrapLoadBalancerScore, | ||||||
|             templates::{BootstrapIpxeTpl, InstallConfigYaml}, |             templates::{BootstrapIpxeTpl, InstallConfigYaml}, | ||||||
| @ -16,15 +28,6 @@ use crate::{ | |||||||
|     score::Score, |     score::Score, | ||||||
|     topology::{HAClusterTopology, HostBinding}, |     topology::{HAClusterTopology, HostBinding}, | ||||||
| }; | }; | ||||||
| use async_trait::async_trait; |  | ||||||
| use derive_new::new; |  | ||||||
| use harmony_secret::SecretManager; |  | ||||||
| use harmony_types::id::Id; |  | ||||||
| use log::{debug, info}; |  | ||||||
| use serde::Serialize; |  | ||||||
| use std::path::PathBuf; |  | ||||||
| use tokio::{fs::File, io::AsyncWriteExt, process::Command}; |  | ||||||
| 
 |  | ||||||
| // -------------------------------------------------------------------------------------------------
 | // -------------------------------------------------------------------------------------------------
 | ||||||
| // Step 02: Bootstrap
 | // Step 02: Bootstrap
 | ||||||
| // - Select bootstrap node (from discovered set).
 | // - Select bootstrap node (from discovered set).
 | ||||||
| @ -310,7 +313,7 @@ impl OKDSetup02BootstrapInterpret { | |||||||
|         info!("[Bootstrap] Rebooting bootstrap node via SSH"); |         info!("[Bootstrap] Rebooting bootstrap node via SSH"); | ||||||
|         // TODO reboot programatically, there are some logical checks and refactoring to do such as
 |         // TODO reboot programatically, there are some logical checks and refactoring to do such as
 | ||||||
|         // accessing the bootstrap node config (ip address) from the inventory
 |         // accessing the bootstrap node config (ip address) from the inventory
 | ||||||
|         let _ = inquire::Confirm::new( |         let confirmation = inquire::Confirm::new( | ||||||
|                 "Now reboot the bootstrap node so it picks up its pxe boot file. Press enter when ready.", |                 "Now reboot the bootstrap node so it picks up its pxe boot file. Press enter when ready.", | ||||||
|         ) |         ) | ||||||
|         .prompt() |         .prompt() | ||||||
| @ -376,6 +379,9 @@ impl Interpret<HAClusterTopology> for OKDSetup02BootstrapInterpret { | |||||||
|         self.reboot_target().await?; |         self.reboot_target().await?; | ||||||
|         self.wait_for_bootstrap_complete().await?; |         self.wait_for_bootstrap_complete().await?; | ||||||
| 
 | 
 | ||||||
|         Ok(Outcome::success("Bootstrap phase complete".into())) |         Ok(Outcome::new( | ||||||
|  |             InterpretStatus::SUCCESS, | ||||||
|  |             "Bootstrap phase complete".into(), | ||||||
|  |         )) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,3 +1,11 @@ | |||||||
|  | use std::{fmt::Write, path::PathBuf}; | ||||||
|  | 
 | ||||||
|  | use async_trait::async_trait; | ||||||
|  | use derive_new::new; | ||||||
|  | use harmony_types::id::Id; | ||||||
|  | use log::{debug, info}; | ||||||
|  | use serde::Serialize; | ||||||
|  | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     data::Version, |     data::Version, | ||||||
|     hardware::PhysicalHost, |     hardware::PhysicalHost, | ||||||
| @ -11,12 +19,6 @@ use crate::{ | |||||||
|     score::Score, |     score::Score, | ||||||
|     topology::{HAClusterTopology, HostBinding}, |     topology::{HAClusterTopology, HostBinding}, | ||||||
| }; | }; | ||||||
| use async_trait::async_trait; |  | ||||||
| use derive_new::new; |  | ||||||
| use harmony_types::id::Id; |  | ||||||
| use log::{debug, info}; |  | ||||||
| use serde::Serialize; |  | ||||||
| 
 |  | ||||||
| // -------------------------------------------------------------------------------------------------
 | // -------------------------------------------------------------------------------------------------
 | ||||||
| // Step 03: Control Plane
 | // Step 03: Control Plane
 | ||||||
| // - Render per-MAC PXE & ignition for cp0/cp1/cp2.
 | // - Render per-MAC PXE & ignition for cp0/cp1/cp2.
 | ||||||
| @ -267,7 +269,8 @@ impl Interpret<HAClusterTopology> for OKDSetup03ControlPlaneInterpret { | |||||||
|         // the `wait-for bootstrap-complete` command.
 |         // the `wait-for bootstrap-complete` command.
 | ||||||
|         info!("[ControlPlane] Provisioning initiated. Monitor the cluster convergence manually."); |         info!("[ControlPlane] Provisioning initiated. Monitor the cluster convergence manually."); | ||||||
| 
 | 
 | ||||||
|         Ok(Outcome::success( |         Ok(Outcome::new( | ||||||
|  |             InterpretStatus::SUCCESS, | ||||||
|             "Control plane provisioning has been successfully initiated.".into(), |             "Control plane provisioning has been successfully initiated.".into(), | ||||||
|         )) |         )) | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,17 +1,33 @@ | |||||||
|  | use std::{fmt::Write, path::PathBuf}; | ||||||
|  | 
 | ||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| use derive_new::new; | use derive_new::new; | ||||||
|  | use harmony_secret::SecretManager; | ||||||
| use harmony_types::id::Id; | use harmony_types::id::Id; | ||||||
| use log::info; | use log::{debug, error, info, warn}; | ||||||
| use serde::Serialize; | use serde::{Deserialize, Serialize}; | ||||||
|  | use tokio::{fs::File, io::AsyncWriteExt, process::Command}; | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     data::Version, |     config::secret::{RedhatSecret, SshKeyPair}, | ||||||
|  |     data::{FileContent, FilePath, Version}, | ||||||
|  |     hardware::PhysicalHost, | ||||||
|  |     infra::inventory::InventoryRepositoryFactory, | ||||||
|  |     instrumentation::{HarmonyEvent, instrument}, | ||||||
|     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, |     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, | ||||||
|     inventory::Inventory, |     inventory::{HostRole, Inventory}, | ||||||
|  |     modules::{ | ||||||
|  |         dhcp::DhcpHostBindingScore, | ||||||
|  |         http::{IPxeMacBootFileScore, StaticFilesHttpScore}, | ||||||
|  |         inventory::LaunchDiscoverInventoryAgentScore, | ||||||
|  |         okd::{ | ||||||
|  |             bootstrap_load_balancer::OKDBootstrapLoadBalancerScore, | ||||||
|  |             templates::{BootstrapIpxeTpl, InstallConfigYaml}, | ||||||
|  |         }, | ||||||
|  |     }, | ||||||
|     score::Score, |     score::Score, | ||||||
|     topology::HAClusterTopology, |     topology::{HAClusterTopology, HostBinding}, | ||||||
| }; | }; | ||||||
| 
 |  | ||||||
| // -------------------------------------------------------------------------------------------------
 | // -------------------------------------------------------------------------------------------------
 | ||||||
| // Step 04: Workers
 | // Step 04: Workers
 | ||||||
| // - Render per-MAC PXE & ignition for workers; join nodes.
 | // - Render per-MAC PXE & ignition for workers; join nodes.
 | ||||||
| @ -78,6 +94,9 @@ impl Interpret<HAClusterTopology> for OKDSetup04WorkersInterpret { | |||||||
|         _topology: &HAClusterTopology, |         _topology: &HAClusterTopology, | ||||||
|     ) -> Result<Outcome, InterpretError> { |     ) -> Result<Outcome, InterpretError> { | ||||||
|         self.render_and_reboot().await?; |         self.render_and_reboot().await?; | ||||||
|         Ok(Outcome::success("Workers provisioned".into())) |         Ok(Outcome::new( | ||||||
|  |             InterpretStatus::SUCCESS, | ||||||
|  |             "Workers provisioned".into(), | ||||||
|  |         )) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,16 +1,33 @@ | |||||||
| use crate::{ | use std::{fmt::Write, path::PathBuf}; | ||||||
|     data::Version, | 
 | ||||||
|     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, |  | ||||||
|     inventory::Inventory, |  | ||||||
|     score::Score, |  | ||||||
|     topology::HAClusterTopology, |  | ||||||
| }; |  | ||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| use derive_new::new; | use derive_new::new; | ||||||
|  | use harmony_secret::SecretManager; | ||||||
| use harmony_types::id::Id; | use harmony_types::id::Id; | ||||||
| use log::info; | use log::{debug, error, info, warn}; | ||||||
| use serde::Serialize; | use serde::{Deserialize, Serialize}; | ||||||
|  | use tokio::{fs::File, io::AsyncWriteExt, process::Command}; | ||||||
| 
 | 
 | ||||||
|  | use crate::{ | ||||||
|  |     config::secret::{RedhatSecret, SshKeyPair}, | ||||||
|  |     data::{FileContent, FilePath, Version}, | ||||||
|  |     hardware::PhysicalHost, | ||||||
|  |     infra::inventory::InventoryRepositoryFactory, | ||||||
|  |     instrumentation::{HarmonyEvent, instrument}, | ||||||
|  |     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, | ||||||
|  |     inventory::{HostRole, Inventory}, | ||||||
|  |     modules::{ | ||||||
|  |         dhcp::DhcpHostBindingScore, | ||||||
|  |         http::{IPxeMacBootFileScore, StaticFilesHttpScore}, | ||||||
|  |         inventory::LaunchDiscoverInventoryAgentScore, | ||||||
|  |         okd::{ | ||||||
|  |             bootstrap_load_balancer::OKDBootstrapLoadBalancerScore, | ||||||
|  |             templates::{BootstrapIpxeTpl, InstallConfigYaml}, | ||||||
|  |         }, | ||||||
|  |     }, | ||||||
|  |     score::Score, | ||||||
|  |     topology::{HAClusterTopology, HostBinding}, | ||||||
|  | }; | ||||||
| // -------------------------------------------------------------------------------------------------
 | // -------------------------------------------------------------------------------------------------
 | ||||||
| // Step 05: Sanity Check
 | // Step 05: Sanity Check
 | ||||||
| // - Validate API reachability, ClusterOperators, ingress, and SDN status.
 | // - Validate API reachability, ClusterOperators, ingress, and SDN status.
 | ||||||
| @ -76,6 +93,9 @@ impl Interpret<HAClusterTopology> for OKDSetup05SanityCheckInterpret { | |||||||
|         _topology: &HAClusterTopology, |         _topology: &HAClusterTopology, | ||||||
|     ) -> Result<Outcome, InterpretError> { |     ) -> Result<Outcome, InterpretError> { | ||||||
|         self.run_checks().await?; |         self.run_checks().await?; | ||||||
|         Ok(Outcome::success("Sanity checks passed".into())) |         Ok(Outcome::new( | ||||||
|  |             InterpretStatus::SUCCESS, | ||||||
|  |             "Sanity checks passed".into(), | ||||||
|  |         )) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,15 +1,32 @@ | |||||||
|  | // -------------------------------------------------------------------------------------------------
 | ||||||
| use async_trait::async_trait; | use async_trait::async_trait; | ||||||
| use derive_new::new; | use derive_new::new; | ||||||
|  | use harmony_secret::SecretManager; | ||||||
| use harmony_types::id::Id; | use harmony_types::id::Id; | ||||||
| use log::info; | use log::{debug, error, info, warn}; | ||||||
| use serde::Serialize; | use serde::{Deserialize, Serialize}; | ||||||
|  | use std::{fmt::Write, path::PathBuf}; | ||||||
|  | use tokio::{fs::File, io::AsyncWriteExt, process::Command}; | ||||||
| 
 | 
 | ||||||
| use crate::{ | use crate::{ | ||||||
|     data::Version, |     config::secret::{RedhatSecret, SshKeyPair}, | ||||||
|  |     data::{FileContent, FilePath, Version}, | ||||||
|  |     hardware::PhysicalHost, | ||||||
|  |     infra::inventory::InventoryRepositoryFactory, | ||||||
|  |     instrumentation::{HarmonyEvent, instrument}, | ||||||
|     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, |     interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, | ||||||
|     inventory::Inventory, |     inventory::{HostRole, Inventory}, | ||||||
|  |     modules::{ | ||||||
|  |         dhcp::DhcpHostBindingScore, | ||||||
|  |         http::{IPxeMacBootFileScore, StaticFilesHttpScore}, | ||||||
|  |         inventory::LaunchDiscoverInventoryAgentScore, | ||||||
|  |         okd::{ | ||||||
|  |             bootstrap_load_balancer::OKDBootstrapLoadBalancerScore, | ||||||
|  |             templates::{BootstrapIpxeTpl, InstallConfigYaml}, | ||||||
|  |         }, | ||||||
|  |     }, | ||||||
|     score::Score, |     score::Score, | ||||||
|     topology::HAClusterTopology, |     topology::{HAClusterTopology, HostBinding}, | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| // Step 06: Installation Report
 | // Step 06: Installation Report
 | ||||||
| @ -76,6 +93,9 @@ impl Interpret<HAClusterTopology> for OKDSetup06InstallationReportInterpret { | |||||||
|         _topology: &HAClusterTopology, |         _topology: &HAClusterTopology, | ||||||
|     ) -> Result<Outcome, InterpretError> { |     ) -> Result<Outcome, InterpretError> { | ||||||
|         self.generate().await?; |         self.generate().await?; | ||||||
|         Ok(Outcome::success("Installation report generated".into())) |         Ok(Outcome::new( | ||||||
|  |             InterpretStatus::SUCCESS, | ||||||
|  |             "Installation report generated".into(), | ||||||
|  |         )) | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -178,10 +178,10 @@ fn handle_events() { | |||||||
|                     ApplicationFeatureStatus::Installing => { |                     ApplicationFeatureStatus::Installing => { | ||||||
|                         info!("Installing feature '{feature}' for '{application}'..."); |                         info!("Installing feature '{feature}' for '{application}'..."); | ||||||
|                     } |                     } | ||||||
|                     ApplicationFeatureStatus::Installed { details: _ } => { |                     ApplicationFeatureStatus::Installed => { | ||||||
|                         info!(status = "finished"; "Feature '{feature}' installed"); |                         info!(status = "finished"; "Feature '{feature}' installed"); | ||||||
|                     } |                     } | ||||||
|                     ApplicationFeatureStatus::Failed { message: details } => { |                     ApplicationFeatureStatus::Failed { details } => { | ||||||
|                         error!(status = "failed"; "Feature '{feature}' installation failed: {details}"); |                         error!(status = "failed"; "Feature '{feature}' installation failed: {details}"); | ||||||
|                     } |                     } | ||||||
|                 }, |                 }, | ||||||
|  | |||||||
| @ -1,56 +0,0 @@ | |||||||
| use std::sync::Mutex; |  | ||||||
| 
 |  | ||||||
| use harmony::{ |  | ||||||
|     instrumentation::{self, HarmonyEvent}, |  | ||||||
|     modules::application::ApplicationFeatureStatus, |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| use crate::theme; |  | ||||||
| 
 |  | ||||||
| pub fn init() { |  | ||||||
|     let details: Mutex<Vec<String>> = Mutex::new(vec![]); |  | ||||||
| 
 |  | ||||||
|     instrumentation::subscribe("Harmony CLI Reporter", { |  | ||||||
|         move |event| { |  | ||||||
|             let mut details = details.lock().unwrap(); |  | ||||||
| 
 |  | ||||||
|             match event { |  | ||||||
|                 HarmonyEvent::InterpretExecutionFinished { |  | ||||||
|                     execution_id: _, |  | ||||||
|                     topology: _, |  | ||||||
|                     interpret: _, |  | ||||||
|                     score: _, |  | ||||||
|                     outcome: Ok(outcome), |  | ||||||
|                 } => { |  | ||||||
|                     if outcome.status == harmony::interpret::InterpretStatus::SUCCESS { |  | ||||||
|                         details.extend(outcome.details.clone()); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 HarmonyEvent::ApplicationFeatureStateChanged { |  | ||||||
|                     topology: _, |  | ||||||
|                     application: _, |  | ||||||
|                     feature: _, |  | ||||||
|                     status: |  | ||||||
|                         ApplicationFeatureStatus::Installed { |  | ||||||
|                             details: feature_details, |  | ||||||
|                         }, |  | ||||||
|                 } => { |  | ||||||
|                     details.extend(feature_details.clone()); |  | ||||||
|                 } |  | ||||||
|                 HarmonyEvent::HarmonyFinished => { |  | ||||||
|                     if !details.is_empty() { |  | ||||||
|                         println!( |  | ||||||
|                             "\n{} All done! Here's what's next for you:", |  | ||||||
|                             theme::EMOJI_SUMMARY |  | ||||||
|                         ); |  | ||||||
|                         for detail in details.iter() { |  | ||||||
|                             println!("- {detail}"); |  | ||||||
|                         } |  | ||||||
|                         println!(); |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 _ => {} |  | ||||||
|             }; |  | ||||||
|         } |  | ||||||
|     }); |  | ||||||
| } |  | ||||||
| @ -8,7 +8,6 @@ use inquire::Confirm; | |||||||
| use log::debug; | use log::debug; | ||||||
| 
 | 
 | ||||||
| pub mod cli_logger; // FIXME: Don't make me pub
 | pub mod cli_logger; // FIXME: Don't make me pub
 | ||||||
| mod cli_reporter; |  | ||||||
| pub mod progress; | pub mod progress; | ||||||
| pub mod theme; | pub mod theme; | ||||||
| 
 | 
 | ||||||
| @ -117,7 +116,6 @@ pub async fn run_cli<T: Topology + Send + Sync + 'static>( | |||||||
|     args: Args, |     args: Args, | ||||||
| ) -> Result<(), Box<dyn std::error::Error>> { | ) -> Result<(), Box<dyn std::error::Error>> { | ||||||
|     cli_logger::init(); |     cli_logger::init(); | ||||||
|     cli_reporter::init(); |  | ||||||
| 
 | 
 | ||||||
|     let mut maestro = Maestro::initialize(inventory, topology).await.unwrap(); |     let mut maestro = Maestro::initialize(inventory, topology).await.unwrap(); | ||||||
|     maestro.register_all(scores); |     maestro.register_all(scores); | ||||||
|  | |||||||
| @ -9,7 +9,6 @@ pub static EMOJI_ERROR: Emoji<'_, '_> = Emoji("⚠️", ""); | |||||||
| pub static EMOJI_DEPLOY: Emoji<'_, '_> = Emoji("🚀", ""); | pub static EMOJI_DEPLOY: Emoji<'_, '_> = Emoji("🚀", ""); | ||||||
| pub static EMOJI_TOPOLOGY: Emoji<'_, '_> = Emoji("📦", ""); | pub static EMOJI_TOPOLOGY: Emoji<'_, '_> = Emoji("📦", ""); | ||||||
| pub static EMOJI_SCORE: Emoji<'_, '_> = Emoji("🎶", ""); | pub static EMOJI_SCORE: Emoji<'_, '_> = Emoji("🎶", ""); | ||||||
| pub static EMOJI_SUMMARY: Emoji<'_, '_> = Emoji("🚀", ""); |  | ||||||
| 
 | 
 | ||||||
| lazy_static! { | lazy_static! { | ||||||
|     pub static ref SECTION_STYLE: ProgressStyle = ProgressStyle::default_spinner() |     pub static ref SECTION_STYLE: ProgressStyle = ProgressStyle::default_spinner() | ||||||
|  | |||||||
| @ -21,6 +21,7 @@ pub fn handle_events() { | |||||||
| 
 | 
 | ||||||
|     instrumentation::subscribe("Harmony Composer Logger", { |     instrumentation::subscribe("Harmony Composer Logger", { | ||||||
|         move |event| match event { |         move |event| match event { | ||||||
|  |             HarmonyComposerEvent::HarmonyComposerStarted => {} | ||||||
|             HarmonyComposerEvent::ProjectInitializationStarted => { |             HarmonyComposerEvent::ProjectInitializationStarted => { | ||||||
|                 progress_tracker.add_section( |                 progress_tracker.add_section( | ||||||
|                     SETUP_SECTION, |                     SETUP_SECTION, | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ use crate::{HarmonyProfile, HarmonyTarget}; | |||||||
| 
 | 
 | ||||||
| #[derive(Debug, Clone)] | #[derive(Debug, Clone)] | ||||||
| pub enum HarmonyComposerEvent { | pub enum HarmonyComposerEvent { | ||||||
|  |     HarmonyComposerStarted, | ||||||
|     ProjectInitializationStarted, |     ProjectInitializationStarted, | ||||||
|     ProjectInitialized, |     ProjectInitialized, | ||||||
|     ProjectCompilationStarted { |     ProjectCompilationStarted { | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user