wip: Report harmony execution outcome

This commit is contained in:
Ian Letourneau 2025-09-09 17:59:14 -04:00
parent 11481b16cd
commit ceafabf430
16 changed files with 217 additions and 190 deletions

View File

@ -34,6 +34,7 @@ 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 {
@ -64,6 +65,7 @@ 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"),
} }
} }
} }
@ -82,6 +84,7 @@ 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 {
@ -89,6 +92,7 @@ impl Outcome {
Self { Self {
status: InterpretStatus::NOOP, status: InterpretStatus::NOOP,
message: String::new(), message: String::new(),
details: vec![],
} }
} }
@ -96,6 +100,23 @@ 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![],
} }
} }
} }

View File

@ -69,17 +69,14 @@ impl DhcpInterpret {
dhcp_server.set_pxe_options(pxe_options).await?; dhcp_server.set_pxe_options(pxe_options).await?;
Ok(Outcome::new( Ok(Outcome::success(format!(
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
), )))
))
} }
} }
@ -122,8 +119,7 @@ impl<T: Topology + DhcpServer> Interpret<T> for DhcpInterpret {
topology.commit_config().await?; topology.commit_config().await?;
Ok(Outcome::new( Ok(Outcome::success(
InterpretStatus::SUCCESS,
"Dhcp Interpret execution successful".to_string(), "Dhcp Interpret execution successful".to_string(),
)) ))
} }
@ -197,10 +193,10 @@ impl DhcpHostBindingInterpret {
} }
} }
Ok(Outcome::new( Ok(Outcome::success(format!(
InterpretStatus::SUCCESS, "Dhcp Interpret registered {} entries",
format!("Dhcp Interpret registered {} entries", number_new_entries), number_new_entries
)) )))
} }
} }
@ -236,12 +232,9 @@ impl<T: DhcpServer> Interpret<T> for DhcpHostBindingInterpret {
topology.commit_config().await?; topology.commit_config().await?;
Ok(Outcome::new( Ok(Outcome::success(format!(
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()
), )))
))
} }
} }

View File

@ -55,8 +55,7 @@ impl DnsInterpret {
dns.register_dhcp_leases(register).await?; dns.register_dhcp_leases(register).await?;
} }
Ok(Outcome::new( Ok(Outcome::success(
InterpretStatus::SUCCESS,
"DNS Interpret execution successfull".to_string(), "DNS Interpret execution successfull".to_string(),
)) ))
} }
@ -68,13 +67,10 @@ 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::new( Ok(Outcome::success(format!(
InterpretStatus::SUCCESS,
format!(
"DnsInterpret registered {} hosts successfully", "DnsInterpret registered {} hosts successfully",
entries.len() entries.len()
), )))
))
} }
} }
@ -111,8 +107,7 @@ impl<T: Topology + DnsServer> Interpret<T> for DnsInterpret {
topology.commit_config().await?; topology.commit_config().await?;
Ok(Outcome::new( Ok(Outcome::success(
InterpretStatus::SUCCESS,
"Dns Interpret execution successful".to_string(), "Dns Interpret execution successful".to_string(),
)) ))
} }

View File

@ -197,13 +197,10 @@ impl<T: Topology + HelmCommand> Interpret<T> for HelmChartInterpret {
self.score.release_name, ns self.score.release_name, ns
); );
return Ok(Outcome::new( return Ok(Outcome::success(format!(
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.",
@ -228,18 +225,18 @@ impl<T: Topology + HelmCommand> Interpret<T> for HelmChartInterpret {
}; };
match status { match status {
helm_wrapper_rs::HelmDeployStatus::Deployed => Ok(Outcome::new( helm_wrapper_rs::HelmDeployStatus::Deployed => Ok(Outcome::success(format!(
InterpretStatus::SUCCESS, "Helm Chart {} deployed",
format!("Helm Chart {} deployed", self.score.release_name), self.score.release_name
)), ))),
helm_wrapper_rs::HelmDeployStatus::PendingInstall => Ok(Outcome::new( helm_wrapper_rs::HelmDeployStatus::PendingInstall => Ok(Outcome::running(format!(
InterpretStatus::RUNNING, "Helm Chart {} pending install...",
format!("Helm Chart {} pending install...", self.score.release_name), self.score.release_name
)), ))),
helm_wrapper_rs::HelmDeployStatus::PendingUpgrade => Ok(Outcome::new( helm_wrapper_rs::HelmDeployStatus::PendingUpgrade => Ok(Outcome::running(format!(
InterpretStatus::RUNNING, "Helm Chart {} pending upgrade...",
format!("Helm Chart {} pending upgrade...", self.score.release_name), 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

View File

@ -133,10 +133,9 @@ impl<T: Topology> Interpret<T> for DiscoverInventoryAgentInterpret {
}, },
) )
.await; .await;
Ok(Outcome { Ok(Outcome::success(
status: InterpretStatus::SUCCESS, "Discovery process completed successfully".to_string(),
message: "Discovery process completed successfully".to_string(), ))
})
} }
fn get_name(&self) -> InterpretName { fn get_name(&self) -> InterpretName {

View File

@ -1,11 +1,15 @@
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::{
interpret::Interpret, data::Version,
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory,
score::Score, score::Score,
topology::{K8sclient, Topology}, topology::{K8sclient, Topology},
}; };
@ -57,7 +61,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 => format!("\"default\""), None => "\"default\"".to_string(),
}; };
let ingress = json!( let ingress = json!(
@ -97,11 +101,12 @@ 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 {
score: K8sResourceScore::single( Box::new(K8sIngressInterpret {
ingress.clone(), ingress,
self.namespace.clone().map(|f| f.to_string()), service: self.name.to_string(),
), namespace: self.namespace.clone().map(|f| f.to_string()),
host: self.host.clone(),
}) })
} }
@ -109,3 +114,59 @@ 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![]
}
}

View File

@ -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, LaunchDiscoverInventoryAgentScore}, modules::inventory::DiscoverHostForRoleScore,
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,12 +109,9 @@ When you can dig them, confirm to continue.
.await?; .await?;
} }
Ok(Outcome::new( Ok(Outcome::success(format!(
InterpretStatus::SUCCESS,
format!(
"Found and assigned bootstrap node: {}", "Found and assigned bootstrap node: {}",
bootstrap_host.unwrap().summary() bootstrap_host.unwrap().summary()
), )))
))
} }
} }

View File

@ -1,25 +1,13 @@
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},
@ -28,6 +16,15 @@ 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).
@ -313,7 +310,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 confirmation = inquire::Confirm::new( let _ = 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()
@ -379,9 +376,6 @@ 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::new( Ok(Outcome::success("Bootstrap phase complete".into()))
InterpretStatus::SUCCESS,
"Bootstrap phase complete".into(),
))
} }
} }

View File

@ -1,11 +1,3 @@
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,
@ -19,6 +11,12 @@ 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.
@ -269,8 +267,7 @@ 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::new( Ok(Outcome::success(
InterpretStatus::SUCCESS,
"Control plane provisioning has been successfully initiated.".into(), "Control plane provisioning has been successfully initiated.".into(),
)) ))
} }

View File

@ -1,33 +1,17 @@
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::{debug, error, info, warn}; use log::info;
use serde::{Deserialize, Serialize}; use serde::Serialize;
use tokio::{fs::File, io::AsyncWriteExt, process::Command};
use crate::{ use crate::{
config::secret::{RedhatSecret, SshKeyPair}, data::Version,
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::{HostRole, Inventory}, inventory::Inventory,
modules::{
dhcp::DhcpHostBindingScore,
http::{IPxeMacBootFileScore, StaticFilesHttpScore},
inventory::LaunchDiscoverInventoryAgentScore,
okd::{
bootstrap_load_balancer::OKDBootstrapLoadBalancerScore,
templates::{BootstrapIpxeTpl, InstallConfigYaml},
},
},
score::Score, score::Score,
topology::{HAClusterTopology, HostBinding}, topology::HAClusterTopology,
}; };
// ------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------
// Step 04: Workers // Step 04: Workers
// - Render per-MAC PXE & ignition for workers; join nodes. // - Render per-MAC PXE & ignition for workers; join nodes.
@ -94,9 +78,6 @@ 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::new( Ok(Outcome::success("Workers provisioned".into()))
InterpretStatus::SUCCESS,
"Workers provisioned".into(),
))
} }
} }

View File

@ -1,33 +1,16 @@
use std::{fmt::Write, path::PathBuf}; use crate::{
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::{debug, error, info, warn}; use log::info;
use serde::{Deserialize, Serialize}; use serde::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.
@ -93,9 +76,6 @@ 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::new( Ok(Outcome::success("Sanity checks passed".into()))
InterpretStatus::SUCCESS,
"Sanity checks passed".into(),
))
} }
} }

View File

@ -1,32 +1,15 @@
// -------------------------------------------------------------------------------------------------
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::{debug, error, info, warn}; use log::info;
use serde::{Deserialize, Serialize}; use serde::Serialize;
use std::{fmt::Write, path::PathBuf};
use tokio::{fs::File, io::AsyncWriteExt, process::Command};
use crate::{ use crate::{
config::secret::{RedhatSecret, SshKeyPair}, data::Version,
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::{HostRole, Inventory}, inventory::Inventory,
modules::{
dhcp::DhcpHostBindingScore,
http::{IPxeMacBootFileScore, StaticFilesHttpScore},
inventory::LaunchDiscoverInventoryAgentScore,
okd::{
bootstrap_load_balancer::OKDBootstrapLoadBalancerScore,
templates::{BootstrapIpxeTpl, InstallConfigYaml},
},
},
score::Score, score::Score,
topology::{HAClusterTopology, HostBinding}, topology::HAClusterTopology,
}; };
// Step 06: Installation Report // Step 06: Installation Report
@ -93,9 +76,6 @@ impl Interpret<HAClusterTopology> for OKDSetup06InstallationReportInterpret {
_topology: &HAClusterTopology, _topology: &HAClusterTopology,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
self.generate().await?; self.generate().await?;
Ok(Outcome::new( Ok(Outcome::success("Installation report generated".into()))
InterpretStatus::SUCCESS,
"Installation report generated".into(),
))
} }
} }

View File

@ -0,0 +1,32 @@
use std::sync::Mutex;
use harmony::instrumentation::{self, HarmonyEvent};
use log::info;
pub fn init() {
let details: Mutex<Vec<String>> = Mutex::new(vec![]);
instrumentation::subscribe("Harmony CLI Reporter", {
move |event| {
let mut details = details.lock().unwrap();
if let HarmonyEvent::InterpretExecutionFinished {
execution_id: _,
topology: _,
interpret: _,
score: _,
outcome: Ok(outcome),
} = event
{
if outcome.status == harmony::interpret::InterpretStatus::SUCCESS {
details.extend(outcome.details.clone());
}
} else if let HarmonyEvent::HarmonyFinished = event {
info!("Here's a summary of what happened:");
for detail in details.iter() {
println!("{detail}");
}
}
}
});
}

View File

@ -8,6 +8,7 @@ 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;
@ -116,6 +117,7 @@ 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);

View File

@ -21,7 +21,6 @@ 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,

View File

@ -5,7 +5,6 @@ use crate::{HarmonyProfile, HarmonyTarget};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum HarmonyComposerEvent { pub enum HarmonyComposerEvent {
HarmonyComposerStarted,
ProjectInitializationStarted, ProjectInitializationStarted,
ProjectInitialized, ProjectInitialized,
ProjectCompilationStarted { ProjectCompilationStarted {