feat(tui): add panic logging and improve event handling
- Integrate `log_panics` for better error tracking in TUI. - Enhance score interpretation result handling with async task management. - Improve layout consistency in the UI rendering process.
This commit is contained in:
parent
0ade6209bb
commit
134f2b78d6
25
harmony-rs/Cargo.lock
generated
25
harmony-rs/Cargo.lock
generated
@ -826,6 +826,21 @@ dependencies = [
|
|||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "example-tui"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cidr",
|
||||||
|
"env_logger",
|
||||||
|
"harmony",
|
||||||
|
"harmony_macros",
|
||||||
|
"harmony_tui",
|
||||||
|
"harmony_types",
|
||||||
|
"log",
|
||||||
|
"tokio",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "eyre"
|
name = "eyre"
|
||||||
version = "0.6.12"
|
version = "0.6.12"
|
||||||
@ -1151,6 +1166,7 @@ dependencies = [
|
|||||||
"env_logger",
|
"env_logger",
|
||||||
"harmony",
|
"harmony",
|
||||||
"log",
|
"log",
|
||||||
|
"log-panics",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
@ -1850,6 +1866,15 @@ version = "0.4.22"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "log-panics"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68f9dd8546191c1850ecf67d22f5ff00a935b890d0e84713159a55495cc2ac5f"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lru"
|
name = "lru"
|
||||||
version = "0.12.5"
|
version = "0.12.5"
|
||||||
|
@ -10,9 +10,7 @@ use harmony::{
|
|||||||
inventory::Inventory,
|
inventory::Inventory,
|
||||||
maestro::Maestro,
|
maestro::Maestro,
|
||||||
modules::{
|
modules::{
|
||||||
http::HttpScore,
|
dummy::{ErrorScore, PanicScore, SuccessScore}, http::HttpScore, okd::{dhcp::OKDDhcpScore, dns::OKDDnsScore}, tftp::TftpScore
|
||||||
okd::{dhcp::OKDDhcpScore, dns::OKDDnsScore},
|
|
||||||
tftp::TftpScore,
|
|
||||||
},
|
},
|
||||||
topology::{LogicalHost, UnmanagedRouter, Url},
|
topology::{LogicalHost, UnmanagedRouter, Url},
|
||||||
};
|
};
|
||||||
@ -88,6 +86,9 @@ async fn main() {
|
|||||||
Box::new(load_balancer_score),
|
Box::new(load_balancer_score),
|
||||||
Box::new(tftp_score),
|
Box::new(tftp_score),
|
||||||
Box::new(http_score),
|
Box::new(http_score),
|
||||||
|
Box::new(SuccessScore {}),
|
||||||
|
Box::new(ErrorScore {}),
|
||||||
|
Box::new(PanicScore {}),
|
||||||
]);
|
]);
|
||||||
harmony_tui::init(maestro).await.unwrap();
|
harmony_tui::init(maestro).await.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,6 @@ pub struct Location {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Location {
|
impl Location {
|
||||||
#[cfg(test)]
|
|
||||||
pub fn test_building() -> Location {
|
pub fn test_building() -> Location {
|
||||||
Self {
|
Self {
|
||||||
address: String::new(),
|
address: String::new(),
|
||||||
|
@ -16,6 +16,8 @@ pub enum InterpretName {
|
|||||||
LoadBalancer,
|
LoadBalancer,
|
||||||
Tftp,
|
Tftp,
|
||||||
Http,
|
Http,
|
||||||
|
Dummy,
|
||||||
|
Panic,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for InterpretName {
|
impl std::fmt::Display for InterpretName {
|
||||||
@ -26,6 +28,8 @@ impl std::fmt::Display for InterpretName {
|
|||||||
InterpretName::LoadBalancer => f.write_str("LoadBalancer"),
|
InterpretName::LoadBalancer => f.write_str("LoadBalancer"),
|
||||||
InterpretName::Tftp => f.write_str("Tftp"),
|
InterpretName::Tftp => f.write_str("Tftp"),
|
||||||
InterpretName::Http => f.write_str("Http"),
|
InterpretName::Http => f.write_str("Http"),
|
||||||
|
InterpretName::Dummy => f.write_str("Dummy"),
|
||||||
|
InterpretName::Panic => f.write_str("Panic"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -43,7 +47,7 @@ pub trait Interpret: std::fmt::Debug + Send {
|
|||||||
fn get_children(&self) -> Vec<Id>;
|
fn get_children(&self) -> Vec<Id>;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, new)]
|
#[derive(Debug, new, Clone)]
|
||||||
pub struct Outcome {
|
pub struct Outcome {
|
||||||
pub status: InterpretStatus,
|
pub status: InterpretStatus,
|
||||||
pub message: String,
|
pub message: String,
|
||||||
@ -89,7 +93,7 @@ impl std::fmt::Display for InterpretStatus {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, new)]
|
||||||
pub struct InterpretError {
|
pub struct InterpretError {
|
||||||
msg: String,
|
msg: String,
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,6 @@ pub struct Inventory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Inventory {
|
impl Inventory {
|
||||||
#[cfg(test)]
|
|
||||||
pub fn empty_inventory() -> Self {
|
pub fn empty_inventory() -> Self {
|
||||||
Self {
|
Self {
|
||||||
location: Location::test_building(),
|
location: Location::test_building(),
|
||||||
|
232
harmony-rs/harmony/src/domain/topology/ha_cluster.rs
Normal file
232
harmony-rs/harmony/src/domain/topology/ha_cluster.rs
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
|
use harmony_macros::ip;
|
||||||
|
use harmony_types::net::MacAddress;
|
||||||
|
|
||||||
|
use crate::executors::ExecutorError;
|
||||||
|
|
||||||
|
use super::DHCPStaticEntry;
|
||||||
|
use super::DhcpServer;
|
||||||
|
use super::DnsRecord;
|
||||||
|
use super::DnsRecordType;
|
||||||
|
use super::DnsServer;
|
||||||
|
use super::Firewall;
|
||||||
|
use super::HttpServer;
|
||||||
|
use super::IpAddress;
|
||||||
|
use super::LoadBalancer;
|
||||||
|
use super::LoadBalancerService;
|
||||||
|
use super::LogicalHost;
|
||||||
|
use super::Router;
|
||||||
|
use super::TftpServer;
|
||||||
|
|
||||||
|
use super::Url;
|
||||||
|
use super::openshift::OpenshiftClient;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct HAClusterTopology {
|
||||||
|
pub domain_name: String,
|
||||||
|
pub router: Arc<dyn Router>,
|
||||||
|
pub load_balancer: Arc<dyn LoadBalancer>,
|
||||||
|
pub firewall: Arc<dyn Firewall>,
|
||||||
|
pub dhcp_server: Arc<dyn DhcpServer>,
|
||||||
|
pub tftp_server: Arc<dyn TftpServer>,
|
||||||
|
pub http_server: Arc<dyn HttpServer>,
|
||||||
|
pub dns_server: Arc<dyn DnsServer>,
|
||||||
|
pub bootstrap_host: LogicalHost,
|
||||||
|
pub control_plane: Vec<LogicalHost>,
|
||||||
|
pub workers: Vec<LogicalHost>,
|
||||||
|
pub switch: Vec<LogicalHost>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HAClusterTopology {
|
||||||
|
pub async fn oc_client(&self) -> Result<Arc<OpenshiftClient>, kube::Error> {
|
||||||
|
Ok(Arc::new(OpenshiftClient::try_default().await?))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dummy() -> Self {
|
||||||
|
let dummy_infra = Arc::new(DummyInfra {});
|
||||||
|
let dummy_host = LogicalHost {
|
||||||
|
ip: ip!("0.0.0.0"),
|
||||||
|
name: "dummyhost".to_string(),
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
domain_name: "DummyTopology".to_string(),
|
||||||
|
router: dummy_infra.clone(),
|
||||||
|
load_balancer: dummy_infra.clone(),
|
||||||
|
firewall: dummy_infra.clone(),
|
||||||
|
dhcp_server: dummy_infra.clone(),
|
||||||
|
tftp_server: dummy_infra.clone(),
|
||||||
|
http_server: dummy_infra.clone(),
|
||||||
|
dns_server: dummy_infra.clone(),
|
||||||
|
bootstrap_host: dummy_host,
|
||||||
|
control_plane: vec![],
|
||||||
|
workers: vec![],
|
||||||
|
switch: vec![],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DummyInfra;
|
||||||
|
|
||||||
|
const UNIMPLEMENTED_DUMMY_INFRA: &str = "This is a dummy infrastructure, no operation is supported";
|
||||||
|
|
||||||
|
impl Router for DummyInfra {
|
||||||
|
fn get_gateway(&self) -> super::IpAddress {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
fn get_cidr(&self) -> cidr::Ipv4Cidr {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
fn get_host(&self) -> LogicalHost {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Firewall for DummyInfra {
|
||||||
|
fn add_rule(
|
||||||
|
&mut self,
|
||||||
|
_rule: super::FirewallRule,
|
||||||
|
) -> Result<(), crate::executors::ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
fn remove_rule(&mut self, _rule_id: &str) -> Result<(), crate::executors::ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
fn list_rules(&self) -> Vec<super::FirewallRule> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
fn get_ip(&self) -> super::IpAddress {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
fn get_host(&self) -> LogicalHost {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl DhcpServer for DummyInfra {
|
||||||
|
async fn add_static_mapping(&self, _entry: &DHCPStaticEntry) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn remove_static_mapping(&self, _mac: &MacAddress) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn set_next_server(&self, _ip: IpAddress) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn set_boot_filename(&self, _boot_filename: &str) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
fn get_ip(&self) -> IpAddress {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
fn get_host(&self) -> LogicalHost {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn commit_config(&self) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl LoadBalancer for DummyInfra {
|
||||||
|
fn get_ip(&self) -> IpAddress {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
fn get_host(&self) -> LogicalHost {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn add_service(&self, _service: &LoadBalancerService) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn remove_service(&self, _service: &LoadBalancerService) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn list_services(&self) -> Vec<LoadBalancerService> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn ensure_initialized(&self) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn commit_config(&self) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn reload_restart(&self) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl TftpServer for DummyInfra {
|
||||||
|
async fn serve_files(&self, _url: &Url) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
fn get_ip(&self) -> IpAddress {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn set_ip(&self, _ip: IpAddress) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn ensure_initialized(&self) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn commit_config(&self) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn reload_restart(&self) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl HttpServer for DummyInfra {
|
||||||
|
async fn serve_files(&self, _url: &Url) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
fn get_ip(&self) -> IpAddress {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn ensure_initialized(&self) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn commit_config(&self) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn reload_restart(&self) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl DnsServer for DummyInfra {
|
||||||
|
async fn register_dhcp_leases(&self, _register: bool) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn register_hosts(&self, _hosts: Vec<DnsRecord>) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
fn remove_record(
|
||||||
|
&mut self,
|
||||||
|
_name: &str,
|
||||||
|
_record_type: DnsRecordType,
|
||||||
|
) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn list_records(&self) -> Vec<DnsRecord> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
fn get_ip(&self) -> IpAddress {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
fn get_host(&self) -> LogicalHost {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
async fn commit_config(&self) -> Result<(), ExecutorError> {
|
||||||
|
unimplemented!("{}", UNIMPLEMENTED_DUMMY_INFRA)
|
||||||
|
}
|
||||||
|
}
|
@ -4,8 +4,9 @@ mod load_balancer;
|
|||||||
pub mod openshift;
|
pub mod openshift;
|
||||||
mod router;
|
mod router;
|
||||||
mod tftp;
|
mod tftp;
|
||||||
|
mod ha_cluster;
|
||||||
|
pub use ha_cluster::*;
|
||||||
pub use load_balancer::*;
|
pub use load_balancer::*;
|
||||||
use openshift::OpenshiftClient;
|
|
||||||
pub use router::*;
|
pub use router::*;
|
||||||
mod network;
|
mod network;
|
||||||
pub use host_binding::*;
|
pub use host_binding::*;
|
||||||
@ -15,27 +16,6 @@ pub use tftp::*;
|
|||||||
|
|
||||||
use std::{net::IpAddr, sync::Arc};
|
use std::{net::IpAddr, sync::Arc};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct HAClusterTopology {
|
|
||||||
pub domain_name: String,
|
|
||||||
pub router: Arc<dyn Router>,
|
|
||||||
pub load_balancer: Arc<dyn LoadBalancer>,
|
|
||||||
pub firewall: Arc<dyn Firewall>,
|
|
||||||
pub dhcp_server: Arc<dyn DhcpServer>,
|
|
||||||
pub tftp_server: Arc<dyn TftpServer>,
|
|
||||||
pub http_server: Arc<dyn HttpServer>,
|
|
||||||
pub dns_server: Arc<dyn DnsServer>,
|
|
||||||
pub bootstrap_host: LogicalHost,
|
|
||||||
pub control_plane: Vec<LogicalHost>,
|
|
||||||
pub workers: Vec<LogicalHost>,
|
|
||||||
pub switch: Vec<LogicalHost>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl HAClusterTopology {
|
|
||||||
pub async fn oc_client(&self) -> Result<Arc<OpenshiftClient>, kube::Error> {
|
|
||||||
Ok(Arc::new(OpenshiftClient::try_default().await?))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type IpAddress = IpAddr;
|
pub type IpAddress = IpAddr;
|
||||||
|
|
||||||
|
138
harmony-rs/harmony/src/modules/dummy.rs
Normal file
138
harmony-rs/harmony/src/modules/dummy.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
data::Version,
|
||||||
|
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
|
||||||
|
inventory::Inventory,
|
||||||
|
score::Score,
|
||||||
|
topology::HAClusterTopology,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Score that always errors. This is only useful for development/testing purposes. It does nothing
|
||||||
|
/// except returning Err(InterpretError) when interpreted.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ErrorScore;
|
||||||
|
|
||||||
|
impl Score for ErrorScore {
|
||||||
|
fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret> {
|
||||||
|
Box::new(DummyInterpret {
|
||||||
|
result: Err(InterpretError::new("Error Score default error".to_string())),
|
||||||
|
status: InterpretStatus::QUEUED,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"ErrorScore".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn Score> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Score that always succeeds. This is only useful for development/testing purposes. It does nothing
|
||||||
|
/// except returning Ok(Outcome::success) when interpreted.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct SuccessScore;
|
||||||
|
|
||||||
|
impl Score for SuccessScore {
|
||||||
|
fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret> {
|
||||||
|
Box::new(DummyInterpret {
|
||||||
|
result: Ok(Outcome::success("SuccessScore default success".to_string())),
|
||||||
|
status: InterpretStatus::QUEUED,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"SuccessScore".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn Score> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An interpret that only returns the result it is given when built. It does nothing else. Only
|
||||||
|
/// useful for development/testing purposes.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct DummyInterpret {
|
||||||
|
status: InterpretStatus,
|
||||||
|
result: Result<Outcome, InterpretError>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Interpret for DummyInterpret {
|
||||||
|
fn get_name(&self) -> InterpretName {
|
||||||
|
InterpretName::Dummy
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_version(&self) -> Version {
|
||||||
|
Version::from("1.0.0").unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_status(&self) -> InterpretStatus {
|
||||||
|
self.status.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_children(&self) -> Vec<crate::domain::data::Id> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn execute(
|
||||||
|
&self,
|
||||||
|
_inventory: &Inventory,
|
||||||
|
_topology: &HAClusterTopology,
|
||||||
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
self.result.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Score that always panics. This is only useful for development/testing purposes. It does nothing
|
||||||
|
/// except panic! with an error message when interpreted
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PanicScore;
|
||||||
|
|
||||||
|
impl Score for PanicScore {
|
||||||
|
fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret> {
|
||||||
|
Box::new(PanicInterpret {})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> String {
|
||||||
|
"PanicScore".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn Score> {
|
||||||
|
Box::new(self.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An interpret that always panics when executed. Useful for development/testing purposes.
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct PanicInterpret;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl Interpret for PanicInterpret {
|
||||||
|
fn get_name(&self) -> InterpretName {
|
||||||
|
InterpretName::Panic
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_version(&self) -> Version {
|
||||||
|
Version::from("1.0.0").unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_status(&self) -> InterpretStatus {
|
||||||
|
InterpretStatus::QUEUED
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_children(&self) -> Vec<crate::domain::data::Id> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn execute(
|
||||||
|
&self,
|
||||||
|
_inventory: &Inventory,
|
||||||
|
_topology: &HAClusterTopology,
|
||||||
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
panic!("Panic interpret always panics when executed")
|
||||||
|
}
|
||||||
|
}
|
@ -5,3 +5,5 @@ pub mod k8s;
|
|||||||
pub mod load_balancer;
|
pub mod load_balancer;
|
||||||
pub mod okd;
|
pub mod okd;
|
||||||
pub mod tftp;
|
pub mod tftp;
|
||||||
|
pub mod dummy;
|
||||||
|
|
||||||
|
@ -15,3 +15,4 @@ crossterm = { version = "0.28.1", features = [ "event-stream" ] }
|
|||||||
color-eyre = "0.6.3"
|
color-eyre = "0.6.3"
|
||||||
tokio-stream = "0.1.17"
|
tokio-stream = "0.1.17"
|
||||||
tui-logger = "0.14.1"
|
tui-logger = "0.14.1"
|
||||||
|
log-panics = "2.1.0"
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
mod ratatui_utils;
|
mod ratatui_utils;
|
||||||
mod widget;
|
mod widget;
|
||||||
|
|
||||||
use log::{debug, info};
|
use log::{debug, error, info};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tokio_stream::StreamExt;
|
use tokio_stream::StreamExt;
|
||||||
use tui_logger::{TuiWidgetEvent, TuiWidgetState};
|
use tui_logger::{TuiWidgetEvent, TuiWidgetState};
|
||||||
use widget::{help::HelpWidget, score::ScoreListWidget};
|
use widget::{help::HelpWidget, score::ScoreListWidget};
|
||||||
|
|
||||||
use std::{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};
|
use harmony::{maestro::Maestro, score::Score};
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
self,
|
self, Frame,
|
||||||
layout::{Constraint, Layout, Position},
|
layout::{Constraint, Layout, Position},
|
||||||
style::{Color, Style},
|
style::{Color, Style},
|
||||||
widgets::{Block, Borders, ListItem},
|
widgets::{Block, Borders, ListItem},
|
||||||
Frame,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub mod tui {
|
pub mod tui {
|
||||||
@ -51,7 +50,6 @@ pub async fn init(maestro: Maestro) -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
pub struct HarmonyTUI {
|
pub struct HarmonyTUI {
|
||||||
score: ScoreListWidget,
|
score: ScoreListWidget,
|
||||||
should_quit: bool,
|
should_quit: bool,
|
||||||
channel_handle: tokio::task::JoinHandle<()>,
|
|
||||||
tui_state: TuiWidgetState,
|
tui_state: TuiWidgetState,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +67,6 @@ impl HarmonyTUI {
|
|||||||
HarmonyTUI {
|
HarmonyTUI {
|
||||||
should_quit: false,
|
should_quit: false,
|
||||||
score,
|
score,
|
||||||
channel_handle: handle,
|
|
||||||
tui_state: TuiWidgetState::new(),
|
tui_state: TuiWidgetState::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -84,15 +81,24 @@ impl HarmonyTUI {
|
|||||||
info!("Received event {event:#?}");
|
info!("Received event {event:#?}");
|
||||||
match event {
|
match event {
|
||||||
HarmonyTuiEvent::LaunchScore(score_item) => {
|
HarmonyTuiEvent::LaunchScore(score_item) => {
|
||||||
info!(
|
let maestro = maestro.clone();
|
||||||
"Interpretation result {:#?}",
|
|
||||||
maestro.interpret(score_item.0).await
|
let interpretation_result =
|
||||||
)
|
tokio::spawn(async move { maestro.interpret(score_item.0).await })
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match interpretation_result {
|
||||||
|
Ok(success) => info!("Score execution successful {success:?}"),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Score execution failed {:#?}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
info!("STOPPING message channel receiver loop");
|
info!("STOPPING message channel receiver loop");
|
||||||
});
|
});
|
||||||
|
|
||||||
(handle, sender)
|
(handle, sender)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +111,7 @@ impl HarmonyTUI {
|
|||||||
|
|
||||||
color_eyre::install()?;
|
color_eyre::install()?;
|
||||||
let mut terminal = ratatui::init();
|
let mut terminal = ratatui::init();
|
||||||
|
log_panics::init();
|
||||||
terminal.hide_cursor()?;
|
terminal.hide_cursor()?;
|
||||||
|
|
||||||
// TODO improve performance here
|
// TODO improve performance here
|
||||||
@ -130,17 +137,14 @@ impl HarmonyTUI {
|
|||||||
frame.set_cursor_position(Position::new(size.x / 2, size.y / 2));
|
frame.set_cursor_position(Position::new(size.x / 2, size.y / 2));
|
||||||
|
|
||||||
let [app_area, help_area] =
|
let [app_area, help_area] =
|
||||||
Layout::vertical([Constraint::Percentage(100), Constraint::Min(4)])
|
Layout::vertical([Constraint::Percentage(100), Constraint::Min(4)]).areas(frame.area());
|
||||||
.areas(frame.area());
|
|
||||||
|
|
||||||
let help_block = Block::default().borders(Borders::TOP);
|
let help_block = Block::default().borders(Borders::TOP);
|
||||||
frame.render_widget(&help_block, help_area);
|
frame.render_widget(&help_block, help_area);
|
||||||
frame.render_widget(HelpWidget::new(), help_block.inner(help_area));
|
frame.render_widget(HelpWidget::new(), help_block.inner(help_area));
|
||||||
|
|
||||||
let [list_area, output_area] =
|
let [list_area, output_area] =
|
||||||
Layout::horizontal([Constraint::Min(30), Constraint::Percentage(100)])
|
Layout::horizontal([Constraint::Min(30), Constraint::Percentage(100)]).areas(app_area);
|
||||||
.areas(app_area);
|
|
||||||
|
|
||||||
|
|
||||||
let block = Block::default().borders(Borders::RIGHT);
|
let block = Block::default().borders(Borders::RIGHT);
|
||||||
frame.render_widget(&block, list_area);
|
frame.render_widget(&block, list_area);
|
||||||
@ -172,7 +176,9 @@ impl HarmonyTUI {
|
|||||||
KeyCode::Char('q') | KeyCode::Esc => self.should_quit = true,
|
KeyCode::Char('q') | KeyCode::Esc => self.should_quit = true,
|
||||||
KeyCode::PageUp => self.tui_state.transition(TuiWidgetEvent::PrevPageKey),
|
KeyCode::PageUp => self.tui_state.transition(TuiWidgetEvent::PrevPageKey),
|
||||||
KeyCode::PageDown => self.tui_state.transition(TuiWidgetEvent::NextPageKey),
|
KeyCode::PageDown => self.tui_state.transition(TuiWidgetEvent::NextPageKey),
|
||||||
KeyCode::Char('G') | KeyCode::End => self.tui_state.transition(TuiWidgetEvent::EscapeKey),
|
KeyCode::Char('G') | KeyCode::End => {
|
||||||
|
self.tui_state.transition(TuiWidgetEvent::EscapeKey)
|
||||||
|
}
|
||||||
_ => self.score.handle_event(event).await,
|
_ => self.score.handle_event(event).await,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user