fix: bring back to TUI
All checks were successful
Run Check Script / check (pull_request) Successful in 1m20s

This commit is contained in:
Ian Letourneau 2025-08-15 12:47:36 -04:00
parent 8bb4a9d3f6
commit 610ce84280
7 changed files with 143 additions and 80 deletions

Binary file not shown.

View File

@ -8,7 +8,6 @@ use harmony::{
hardware::{FirewallGroup, HostCategory, Location, PhysicalHost, SwitchGroup}, hardware::{FirewallGroup, HostCategory, Location, PhysicalHost, SwitchGroup},
infra::opnsense::OPNSenseManagementInterface, infra::opnsense::OPNSenseManagementInterface,
inventory::Inventory, inventory::Inventory,
maestro::Maestro,
modules::{ modules::{
http::StaticFilesHttpScore, http::StaticFilesHttpScore,
ipxe::IpxeScore, ipxe::IpxeScore,
@ -130,16 +129,21 @@ async fn main() {
"./data/watchguard/pxe-http-files".to_string(), "./data/watchguard/pxe-http-files".to_string(),
)); ));
let ipxe_score = IpxeScore::new(); let ipxe_score = IpxeScore::new();
let mut maestro = Maestro::initialize(inventory, topology).await.unwrap();
maestro.register_all(vec![ harmony_tui::run(
Box::new(dns_score), inventory,
Box::new(bootstrap_dhcp_score), topology,
Box::new(bootstrap_load_balancer_score), vec![
Box::new(load_balancer_score), Box::new(dns_score),
Box::new(tftp_score), Box::new(bootstrap_dhcp_score),
Box::new(http_score), Box::new(bootstrap_load_balancer_score),
Box::new(ipxe_score), Box::new(load_balancer_score),
Box::new(dhcp_score), Box::new(tftp_score),
]); Box::new(http_score),
harmony_tui::init(maestro).await.unwrap(); Box::new(ipxe_score),
Box::new(dhcp_score),
],
)
.await
.unwrap();
} }

View File

@ -8,7 +8,6 @@ use harmony::{
hardware::{FirewallGroup, HostCategory, Location, PhysicalHost, SwitchGroup}, hardware::{FirewallGroup, HostCategory, Location, PhysicalHost, SwitchGroup},
infra::opnsense::OPNSenseManagementInterface, infra::opnsense::OPNSenseManagementInterface,
inventory::Inventory, inventory::Inventory,
maestro::Maestro,
modules::{ modules::{
dummy::{ErrorScore, PanicScore, SuccessScore}, dummy::{ErrorScore, PanicScore, SuccessScore},
http::StaticFilesHttpScore, http::StaticFilesHttpScore,
@ -84,20 +83,25 @@ async fn main() {
let http_score = StaticFilesHttpScore::new(Url::LocalFolder( let http_score = StaticFilesHttpScore::new(Url::LocalFolder(
"./data/watchguard/pxe-http-files".to_string(), "./data/watchguard/pxe-http-files".to_string(),
)); ));
let mut maestro = Maestro::initialize(inventory, topology).await.unwrap();
maestro.register_all(vec![ harmony_tui::run(
Box::new(dns_score), inventory,
Box::new(dhcp_score), topology,
Box::new(load_balancer_score), vec![
Box::new(tftp_score), Box::new(dns_score),
Box::new(http_score), Box::new(dhcp_score),
Box::new(OPNsenseShellCommandScore { Box::new(load_balancer_score),
opnsense: opnsense.get_opnsense_config(), Box::new(tftp_score),
command: "touch /tmp/helloharmonytouching".to_string(), Box::new(http_score),
}), Box::new(OPNsenseShellCommandScore {
Box::new(SuccessScore {}), opnsense: opnsense.get_opnsense_config(),
Box::new(ErrorScore {}), command: "touch /tmp/helloharmonytouching".to_string(),
Box::new(PanicScore {}), }),
]); Box::new(SuccessScore {}),
harmony_tui::init(maestro).await.unwrap(); Box::new(ErrorScore {}),
Box::new(PanicScore {}),
],
)
.await
.unwrap();
} }

View File

@ -2,7 +2,6 @@ use std::net::{SocketAddr, SocketAddrV4};
use harmony::{ use harmony::{
inventory::Inventory, inventory::Inventory,
maestro::Maestro,
modules::{ modules::{
dns::DnsScore, dns::DnsScore,
dummy::{ErrorScore, PanicScore, SuccessScore}, dummy::{ErrorScore, PanicScore, SuccessScore},
@ -16,18 +15,19 @@ use harmony_macros::ipv4;
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
let inventory = Inventory::autoload(); harmony_tui::run(
let topology = DummyInfra {}; Inventory::autoload(),
let mut maestro = Maestro::initialize(inventory, topology).await.unwrap(); DummyInfra {},
vec![
maestro.register_all(vec![ Box::new(SuccessScore {}),
Box::new(SuccessScore {}), Box::new(ErrorScore {}),
Box::new(ErrorScore {}), Box::new(PanicScore {}),
Box::new(PanicScore {}), Box::new(DnsScore::new(vec![], None)),
Box::new(DnsScore::new(vec![], None)), Box::new(build_large_score()),
Box::new(build_large_score()), ],
]); )
harmony_tui::init(maestro).await.unwrap(); .await
.unwrap();
} }
fn build_large_score() -> LoadBalancerScore { fn build_large_score() -> LoadBalancerScore {

View File

@ -241,7 +241,7 @@ pub struct DummyInfra;
#[async_trait] #[async_trait]
impl Topology for DummyInfra { impl Topology for DummyInfra {
fn name(&self) -> &str { fn name(&self) -> &str {
todo!() "DummyInfra"
} }
async fn ensure_ready(&self) -> Result<PreparationOutcome, PreparationError> { async fn ensure_ready(&self) -> Result<PreparationOutcome, PreparationError> {

View File

@ -90,13 +90,37 @@ pub async fn run<T: Topology + Send + Sync + 'static>(
topology: T, topology: T,
scores: Vec<Box<dyn Score<T>>>, scores: Vec<Box<dyn Score<T>>>,
args_struct: Option<Args>, args_struct: Option<Args>,
) -> Result<(), Box<dyn std::error::Error>> {
let args = match args_struct {
Some(args) => args,
None => Args::parse(),
};
#[cfg(not(feature = "tui"))]
if args.interactive {
return Err("Not compiled with interactive support".into());
}
#[cfg(feature = "tui")]
if args.interactive {
return harmony_tui::run(inventory, topology, scores).await;
}
run_cli(inventory, topology, scores, args).await
}
pub async fn run_cli<T: Topology + Send + Sync + 'static>(
inventory: Inventory,
topology: T,
scores: Vec<Box<dyn Score<T>>>,
args: Args,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let cli_logger_handle = cli_logger::init(); let cli_logger_handle = cli_logger::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);
let result = init(maestro, args_struct).await; let result = init(maestro, args).await;
instrumentation::instrument(instrumentation::HarmonyEvent::HarmonyFinished).unwrap(); instrumentation::instrument(instrumentation::HarmonyEvent::HarmonyFinished).unwrap();
let _ = tokio::try_join!(cli_logger_handle); let _ = tokio::try_join!(cli_logger_handle);
@ -105,23 +129,8 @@ pub async fn run<T: Topology + Send + Sync + 'static>(
async fn init<T: Topology + Send + Sync + 'static>( async fn init<T: Topology + Send + Sync + 'static>(
maestro: harmony::maestro::Maestro<T>, maestro: harmony::maestro::Maestro<T>,
args_struct: Option<Args>, args: Args,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
let args = match args_struct {
Some(args) => args,
None => Args::parse(),
};
#[cfg(feature = "tui")]
if args.interactive {
return harmony_tui::init(maestro).await;
}
#[cfg(not(feature = "tui"))]
if args.interactive {
return Err("Not compiled with interactive support".into());
}
let _ = env_logger::builder().try_init(); let _ = env_logger::builder().try_init();
let scores_vec = maestro_scores_filter(&maestro, args.all, args.filter, args.number); let scores_vec = maestro_scores_filter(&maestro, args.all, args.filter, args.number);
@ -193,14 +202,14 @@ mod tests {
let maestro = init_test_maestro(); let maestro = init_test_maestro();
let res = crate::init( let res = crate::init(
maestro, maestro,
Some(crate::Args { crate::Args {
yes: true, yes: true,
filter: Some("SuccessScore".to_owned()), filter: Some("SuccessScore".to_owned()),
interactive: false, interactive: false,
all: true, all: true,
number: 0, number: 0,
list: false, list: false,
}), },
) )
.await; .await;
@ -213,14 +222,14 @@ mod tests {
let res = crate::init( let res = crate::init(
maestro, maestro,
Some(crate::Args { crate::Args {
yes: true, yes: true,
filter: Some("ErrorScore".to_owned()), filter: Some("ErrorScore".to_owned()),
interactive: false, interactive: false,
all: true, all: true,
number: 0, number: 0,
list: false, list: false,
}), },
) )
.await; .await;
@ -233,14 +242,14 @@ mod tests {
let res = crate::init( let res = crate::init(
maestro, maestro,
Some(crate::Args { crate::Args {
yes: true, yes: true,
filter: None, filter: None,
interactive: false, interactive: false,
all: false, all: false,
number: 0, number: 0,
list: false, list: false,
}), },
) )
.await; .await;

View File

@ -9,7 +9,13 @@ use widget::{help::HelpWidget, score::ScoreListWidget};
use std::{panic, sync::Arc, time::Duration}; use std::{panic, sync::Arc, time::Duration};
use crossterm::event::{Event, EventStream, KeyCode, KeyEventKind}; use crossterm::event::{Event, EventStream, KeyCode, KeyEventKind};
use harmony::{maestro::Maestro, score::Score, topology::Topology}; use harmony::{
instrumentation::{self, HarmonyEvent},
inventory::Inventory,
maestro::Maestro,
score::Score,
topology::Topology,
};
use ratatui::{ use ratatui::{
self, Frame, self, Frame,
layout::{Constraint, Layout, Position}, layout::{Constraint, Layout, Position},
@ -39,22 +45,62 @@ pub mod tui {
/// ///
/// #[tokio::main] /// #[tokio::main]
/// async fn main() { /// async fn main() {
/// let inventory = Inventory::autoload(); /// harmony_tui::run(
/// let topology = HAClusterTopology::autoload(); /// Inventory::autoload(),
/// let mut maestro = Maestro::new_without_initialization(inventory, topology); /// HAClusterTopology::autoload(),
/// /// vec![
/// maestro.register_all(vec![ /// Box::new(SuccessScore {}),
/// Box::new(SuccessScore {}), /// Box::new(ErrorScore {}),
/// Box::new(ErrorScore {}), /// Box::new(PanicScore {}),
/// Box::new(PanicScore {}), /// ]
/// ]); /// ).await.unwrap();
/// harmony_tui::init(maestro).await.unwrap();
/// } /// }
/// ``` /// ```
pub async fn init<T: Topology + Send + Sync + 'static>( pub async fn run<T: Topology + Send + Sync + 'static>(
inventory: Inventory,
topology: T,
scores: Vec<Box<dyn Score<T>>>,
) -> Result<(), Box<dyn std::error::Error>> {
let handle = init_instrumentation().await;
let mut maestro = Maestro::initialize(inventory, topology).await.unwrap();
maestro.register_all(scores);
let result = init(maestro).await;
let _ = tokio::try_join!(handle);
result
}
async fn init<T: Topology + Send + Sync + 'static>(
maestro: Maestro<T>, maestro: Maestro<T>,
) -> Result<(), Box<dyn std::error::Error>> { ) -> Result<(), Box<dyn std::error::Error>> {
HarmonyTUI::new(maestro).init().await let result = HarmonyTUI::new(maestro).init().await;
instrumentation::instrument(HarmonyEvent::HarmonyFinished).unwrap();
result
}
async fn init_instrumentation() -> tokio::task::JoinHandle<()> {
let handle = tokio::spawn(handle_harmony_events());
loop {
if instrumentation::instrument(HarmonyEvent::HarmonyStarted).is_ok() {
break;
}
}
handle
}
async fn handle_harmony_events() {
instrumentation::subscribe("Harmony TUI Logger", async |event| {
if let HarmonyEvent::HarmonyFinished = event {
return false;
};
true
})
.await;
} }
pub struct HarmonyTUI<T: Topology> { pub struct HarmonyTUI<T: Topology> {