feat(topology): generalize Score and Interpret implementations with topology traits

Refactor various `Score` and `Interpret` implementations to utilize generic `Topology` traits, removing hardcoded dependencies on `HAClusterTopology`. This enhancement allows for more flexible and extensible code, accommodating different types of network topologies.
This commit is contained in:
Jean-Gabriel Gill-Couture 2025-03-26 23:10:51 -04:00
parent d7897f29c4
commit fda007f014
22 changed files with 148 additions and 217 deletions

View File

@ -7,7 +7,7 @@ use super::{
data::{Id, Version}, data::{Id, Version},
executors::ExecutorError, executors::ExecutorError,
inventory::Inventory, inventory::Inventory,
topology::HAClusterTopology, topology::Topology,
}; };
pub enum InterpretName { pub enum InterpretName {
@ -37,12 +37,9 @@ impl std::fmt::Display for InterpretName {
} }
#[async_trait] #[async_trait]
pub trait Interpret: std::fmt::Debug + Send { pub trait Interpret<T: Topology>: std::fmt::Debug + Send {
async fn execute( async fn execute(&self, inventory: &Inventory, topology: &T)
&self, -> Result<Outcome, InterpretError>;
inventory: &Inventory,
topology: &HAClusterTopology,
) -> Result<Outcome, InterpretError>;
fn get_name(&self) -> InterpretName; fn get_name(&self) -> InterpretName;
fn get_version(&self) -> Version; fn get_version(&self) -> Version;
fn get_status(&self) -> InterpretStatus; fn get_status(&self) -> InterpretStatus;

View File

@ -6,19 +6,19 @@ use super::{
interpret::{InterpretError, Outcome}, interpret::{InterpretError, Outcome},
inventory::Inventory, inventory::Inventory,
score::Score, score::Score,
topology::HAClusterTopology, topology::Topology,
}; };
type ScoreVec = Vec<Box<dyn Score>>; type ScoreVec<T: Topology> = Vec<Box<dyn Score<T>>>;
pub struct Maestro { pub struct Maestro<T: Topology> {
inventory: Inventory, inventory: Inventory,
topology: HAClusterTopology, topology: T,
scores: Arc<RwLock<ScoreVec>>, scores: Arc<RwLock<ScoreVec<T>>>,
} }
impl Maestro { impl<T: Topology> Maestro<T> {
pub fn new(inventory: Inventory, topology: HAClusterTopology) -> Self { pub fn new(inventory: Inventory, topology: T) -> Self {
Self { Self {
inventory, inventory,
topology, topology,
@ -51,12 +51,15 @@ impl Maestro {
info!("Starting Maestro"); info!("Starting Maestro");
} }
pub fn register_all(&mut self, mut scores: ScoreVec) { pub fn register_all(&mut self, mut scores: ScoreVec<T>) {
let mut score_mut = self.scores.write().expect("Should acquire lock"); let mut score_mut = self.scores.write().expect("Should acquire lock");
score_mut.append(&mut scores); score_mut.append(&mut scores);
} }
pub async fn interpret(&self, score: Box<dyn Score>) -> Result<Outcome, InterpretError> { pub async fn interpret<S>(&self, score: S) -> Result<Outcome, InterpretError>
where
S: Score<T>,
{
info!("Running score {score:?}"); info!("Running score {score:?}");
let interpret = score.create_interpret(); let interpret = score.create_interpret();
info!("Launching interpret {interpret:?}"); info!("Launching interpret {interpret:?}");
@ -65,7 +68,7 @@ impl Maestro {
result result
} }
pub fn scores(&self) -> Arc<RwLock<ScoreVec>> { pub fn scores(&self) -> Arc<RwLock<ScoreVec<T>>> {
self.scores.clone() self.scores.clone()
} }
} }

View File

@ -1,7 +1,6 @@
use super::interpret::Interpret; use super::{interpret::Interpret, topology::Topology};
pub trait Score: std::fmt::Debug + Send + Sync { pub trait Score<T: Topology>: std::fmt::Debug + Send + Sync {
fn create_interpret(&self) -> Box<dyn Interpret>; fn create_interpret(&self) -> Box<dyn Interpret<T>>;
fn name(&self) -> String; fn name(&self) -> String;
fn clone_box(&self) -> Box<dyn Score>;
} }

View File

@ -15,9 +15,11 @@ use super::IpAddress;
use super::LoadBalancer; use super::LoadBalancer;
use super::LoadBalancerService; use super::LoadBalancerService;
use super::LogicalHost; use super::LogicalHost;
use super::OcK8sclient;
use super::Router; use super::Router;
use super::TftpServer; use super::TftpServer;
use super::Topology;
use super::Url; use super::Url;
use super::openshift::OpenshiftClient; use super::openshift::OpenshiftClient;
use std::sync::Arc; use std::sync::Arc;
@ -38,11 +40,20 @@ pub struct HAClusterTopology {
pub switch: Vec<LogicalHost>, pub switch: Vec<LogicalHost>,
} }
impl HAClusterTopology { impl Topology for HAClusterTopology {
pub async fn oc_client(&self) -> Result<Arc<OpenshiftClient>, kube::Error> { fn name(&self) -> &str {
todo!()
}
}
#[async_trait]
impl OcK8sclient for HAClusterTopology {
async fn oc_client(&self) -> Result<Arc<OpenshiftClient>, kube::Error> {
Ok(Arc::new(OpenshiftClient::try_default().await?)) Ok(Arc::new(OpenshiftClient::try_default().await?))
} }
}
impl HAClusterTopology {
pub fn autoload() -> Self { pub fn autoload() -> Self {
let dummy_infra = Arc::new(DummyInfra {}); let dummy_infra = Arc::new(DummyInfra {});
let dummy_host = LogicalHost { let dummy_host = LogicalHost {
@ -67,6 +78,7 @@ impl HAClusterTopology {
} }
} }
#[derive(Debug)]
struct DummyInfra; struct DummyInfra;
const UNIMPLEMENTED_DUMMY_INFRA: &str = "This is a dummy infrastructure, no operation is supported"; const UNIMPLEMENTED_DUMMY_INFRA: &str = "This is a dummy infrastructure, no operation is supported";

View File

@ -16,6 +16,12 @@ pub use tftp::*;
use std::net::IpAddr; use std::net::IpAddr;
pub trait Topology {
fn name(&self) -> &str;
}
pub trait Capability {}
pub type IpAddress = IpAddr; pub type IpAddress = IpAddr;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View File

@ -1,11 +1,11 @@
use std::{net::Ipv4Addr, str::FromStr}; use std::{net::Ipv4Addr, str::FromStr, sync::Arc};
use async_trait::async_trait; use async_trait::async_trait;
use harmony_types::net::MacAddress; use harmony_types::net::MacAddress;
use crate::executors::ExecutorError; use crate::executors::ExecutorError;
use super::{IpAddress, LogicalHost}; use super::{openshift::OpenshiftClient, IpAddress, LogicalHost};
#[derive(Debug)] #[derive(Debug)]
pub struct DHCPStaticEntry { pub struct DHCPStaticEntry {
@ -40,9 +40,14 @@ impl std::fmt::Debug for dyn Firewall {
pub struct NetworkDomain { pub struct NetworkDomain {
pub name: String, pub name: String,
} }
#[async_trait]
pub trait OcK8sclient: Send + Sync + std::fmt::Debug {
async fn oc_client(&self) -> Result<Arc<OpenshiftClient>, kube::Error>;
}
#[async_trait] #[async_trait]
pub trait DhcpServer: Send + Sync { pub trait DhcpServer: Send + Sync + std::fmt::Debug {
async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError>; async fn add_static_mapping(&self, entry: &DHCPStaticEntry) -> Result<(), ExecutorError>;
async fn remove_static_mapping(&self, mac: &MacAddress) -> Result<(), ExecutorError>; async fn remove_static_mapping(&self, mac: &MacAddress) -> Result<(), ExecutorError>;
async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)>; async fn list_static_mappings(&self) -> Vec<(MacAddress, IpAddress)>;
@ -53,12 +58,6 @@ pub trait DhcpServer: Send + Sync {
async fn commit_config(&self) -> Result<(), ExecutorError>; async fn commit_config(&self) -> Result<(), ExecutorError>;
} }
impl std::fmt::Debug for dyn DhcpServer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_fmt(format_args!("DhcpServer {}", self.get_ip()))
}
}
#[async_trait] #[async_trait]
pub trait DnsServer: Send + Sync { pub trait DnsServer: Send + Sync {
async fn register_dhcp_leases(&self, register: bool) -> Result<(), ExecutorError>; async fn register_dhcp_leases(&self, register: bool) -> Result<(), ExecutorError>;

View File

@ -8,7 +8,7 @@ use crate::{
domain::{data::Version, interpret::InterpretStatus}, domain::{data::Version, interpret::InterpretStatus},
interpret::{Interpret, InterpretError, InterpretName, Outcome}, interpret::{Interpret, InterpretError, InterpretName, Outcome},
inventory::Inventory, inventory::Inventory,
topology::{DHCPStaticEntry, HAClusterTopology, HostBinding, IpAddress}, topology::{DHCPStaticEntry, DhcpServer, HAClusterTopology, HostBinding, IpAddress, Topology},
}; };
use crate::domain::score::Score; use crate::domain::score::Score;
@ -20,18 +20,14 @@ pub struct DhcpScore {
pub boot_filename: Option<String>, pub boot_filename: Option<String>,
} }
impl Score for DhcpScore { impl<T: Topology + DhcpServer> Score<T> for DhcpScore {
fn create_interpret(&self) -> Box<dyn Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
Box::new(DhcpInterpret::new(self.clone())) Box::new(DhcpInterpret::new(self.clone()))
} }
fn name(&self) -> String { fn name(&self) -> String {
"DhcpScore".to_string() "DhcpScore".to_string()
} }
fn clone_box(&self) -> Box<dyn Score> {
Box::new(self.clone())
}
} }
// https://docs.opnsense.org/manual/dhcp.html#advanced-settings // https://docs.opnsense.org/manual/dhcp.html#advanced-settings
@ -52,10 +48,10 @@ impl DhcpInterpret {
status: InterpretStatus::QUEUED, status: InterpretStatus::QUEUED,
} }
} }
async fn add_static_entries( async fn add_static_entries<D: DhcpServer>(
&self, &self,
_inventory: &Inventory, _inventory: &Inventory,
topology: &HAClusterTopology, dhcp_server: &D,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
let dhcp_entries: Vec<DHCPStaticEntry> = self let dhcp_entries: Vec<DHCPStaticEntry> = self
.score .score
@ -78,7 +74,6 @@ impl DhcpInterpret {
.collect(); .collect();
info!("DHCPStaticEntry : {:?}", dhcp_entries); info!("DHCPStaticEntry : {:?}", dhcp_entries);
let dhcp_server = Arc::new(topology.dhcp_server.clone());
info!("DHCP server : {:?}", dhcp_server); info!("DHCP server : {:?}", dhcp_server);
let number_new_entries = dhcp_entries.len(); let number_new_entries = dhcp_entries.len();
@ -96,14 +91,13 @@ impl DhcpInterpret {
)) ))
} }
async fn set_pxe_options( async fn set_pxe_options<D: DhcpServer>(
&self, &self,
_inventory: &Inventory, _inventory: &Inventory,
topology: &HAClusterTopology, dhcp_server: &D,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
let next_server_outcome = match self.score.next_server { let next_server_outcome = match self.score.next_server {
Some(next_server) => { Some(next_server) => {
let dhcp_server = Arc::new(topology.dhcp_server.clone());
dhcp_server.set_next_server(next_server).await?; dhcp_server.set_next_server(next_server).await?;
Outcome::new( Outcome::new(
InterpretStatus::SUCCESS, InterpretStatus::SUCCESS,
@ -115,7 +109,6 @@ impl DhcpInterpret {
let boot_filename_outcome = match &self.score.boot_filename { let boot_filename_outcome = match &self.score.boot_filename {
Some(boot_filename) => { Some(boot_filename) => {
let dhcp_server = Arc::new(topology.dhcp_server.clone());
dhcp_server.set_boot_filename(&boot_filename).await?; dhcp_server.set_boot_filename(&boot_filename).await?;
Outcome::new( Outcome::new(
InterpretStatus::SUCCESS, InterpretStatus::SUCCESS,
@ -142,7 +135,7 @@ impl DhcpInterpret {
} }
#[async_trait] #[async_trait]
impl Interpret for DhcpInterpret { impl<T: Topology + DhcpServer> Interpret<T> for DhcpInterpret {
fn get_name(&self) -> InterpretName { fn get_name(&self) -> InterpretName {
InterpretName::OPNSenseDHCP InterpretName::OPNSenseDHCP
} }
@ -162,15 +155,15 @@ impl Interpret for DhcpInterpret {
async fn execute( async fn execute(
&self, &self,
inventory: &Inventory, inventory: &Inventory,
topology: &HAClusterTopology, topology: &T,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
info!("Executing {} on inventory {inventory:?}", self.get_name()); info!("Executing DhcpInterpret on inventory {inventory:?}");
self.set_pxe_options(inventory, topology).await?; self.set_pxe_options(inventory, topology).await?;
self.add_static_entries(inventory, topology).await?; self.add_static_entries(inventory, topology).await?;
topology.dhcp_server.commit_config().await?; topology.commit_config().await?;
Ok(Outcome::new( Ok(Outcome::new(
InterpretStatus::SUCCESS, InterpretStatus::SUCCESS,

View File

@ -7,7 +7,7 @@ use crate::{
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory, inventory::Inventory,
score::Score, score::Score,
topology::{DnsRecord, HAClusterTopology}, topology::{DnsRecord, DnsServer, HAClusterTopology, Topology},
}; };
#[derive(Debug, new, Clone)] #[derive(Debug, new, Clone)]
@ -16,18 +16,14 @@ pub struct DnsScore {
register_dhcp_leases: Option<bool>, register_dhcp_leases: Option<bool>,
} }
impl Score for DnsScore { impl<T: Topology + DnsServer> Score<T> for DnsScore {
fn create_interpret(&self) -> Box<dyn Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
Box::new(DnsInterpret::new(self.clone())) Box::new(DnsInterpret::new(self.clone()))
} }
fn name(&self) -> String { fn name(&self) -> String {
"DnsScore".to_string() "DnsScore".to_string()
} }
fn clone_box(&self) -> Box<dyn Score> {
Box::new(self.clone())
}
} }
// https://docs.opnsense.org/manual/dhcp.html#advanced-settings // https://docs.opnsense.org/manual/dhcp.html#advanced-settings
@ -48,12 +44,11 @@ impl DnsInterpret {
status: InterpretStatus::QUEUED, status: InterpretStatus::QUEUED,
} }
} }
async fn serve_dhcp_entries( async fn serve_dhcp_entries<T: DnsServer>(
&self, &self,
_inventory: &Inventory, _inventory: &Inventory,
topology: &HAClusterTopology, dns: &T,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
let dns = topology.dns_server.clone();
if let Some(register) = self.score.register_dhcp_leases { if let Some(register) = self.score.register_dhcp_leases {
dns.register_dhcp_leases(register).await?; dns.register_dhcp_leases(register).await?;
} }
@ -64,15 +59,12 @@ impl DnsInterpret {
)) ))
} }
async fn ensure_hosts_registered( async fn ensure_hosts_registered<D: DnsServer>(
&self, &self,
topology: &HAClusterTopology, dns_server: &D,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
let entries = &self.score.dns_entries; let entries = &self.score.dns_entries;
topology dns_server.ensure_hosts_registered(entries.clone()).await?;
.dns_server
.ensure_hosts_registered(entries.clone())
.await?;
Ok(Outcome::new( Ok(Outcome::new(
InterpretStatus::SUCCESS, InterpretStatus::SUCCESS,
@ -85,7 +77,7 @@ impl DnsInterpret {
} }
#[async_trait] #[async_trait]
impl Interpret for DnsInterpret { impl<T: Topology + DnsServer> Interpret<T> for DnsInterpret {
fn get_name(&self) -> InterpretName { fn get_name(&self) -> InterpretName {
InterpretName::OPNSenseDns InterpretName::OPNSenseDns
} }
@ -105,14 +97,14 @@ impl Interpret for DnsInterpret {
async fn execute( async fn execute(
&self, &self,
inventory: &Inventory, inventory: &Inventory,
topology: &HAClusterTopology, topology: &T,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
info!("Executing {} on inventory {inventory:?}", self.get_name()); info!("Executing {} on inventory {inventory:?}", <DnsInterpret as Interpret<T>>::get_name(self));
self.serve_dhcp_entries(inventory, topology).await?; self.serve_dhcp_entries(inventory, topology).await?;
self.ensure_hosts_registered(&topology).await?; self.ensure_hosts_registered(topology).await?;
topology.dns_server.commit_config().await?; topology.commit_config().await?;
Ok(Outcome::new( Ok(Outcome::new(
InterpretStatus::SUCCESS, InterpretStatus::SUCCESS,

View File

@ -5,7 +5,7 @@ use crate::{
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory, inventory::Inventory,
score::Score, score::Score,
topology::HAClusterTopology, topology::{HAClusterTopology, Topology},
}; };
/// Score that always errors. This is only useful for development/testing purposes. It does nothing /// Score that always errors. This is only useful for development/testing purposes. It does nothing
@ -13,8 +13,8 @@ use crate::{
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ErrorScore; pub struct ErrorScore;
impl Score for ErrorScore { impl<T: Topology> Score<T> for ErrorScore {
fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret> { fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret<T>> {
Box::new(DummyInterpret { Box::new(DummyInterpret {
result: Err(InterpretError::new("Error Score default error".to_string())), result: Err(InterpretError::new("Error Score default error".to_string())),
status: InterpretStatus::QUEUED, status: InterpretStatus::QUEUED,
@ -24,10 +24,6 @@ impl Score for ErrorScore {
fn name(&self) -> String { fn name(&self) -> String {
"ErrorScore".to_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 /// Score that always succeeds. This is only useful for development/testing purposes. It does nothing
@ -35,8 +31,8 @@ impl Score for ErrorScore {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct SuccessScore; pub struct SuccessScore;
impl Score for SuccessScore { impl<T: Topology> Score<T> for SuccessScore {
fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
Box::new(DummyInterpret { Box::new(DummyInterpret {
result: Ok(Outcome::success("SuccessScore default success".to_string())), result: Ok(Outcome::success("SuccessScore default success".to_string())),
status: InterpretStatus::QUEUED, status: InterpretStatus::QUEUED,
@ -46,10 +42,6 @@ impl Score for SuccessScore {
fn name(&self) -> String { fn name(&self) -> String {
"SuccessScore".to_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 /// An interpret that only returns the result it is given when built. It does nothing else. Only
@ -61,7 +53,7 @@ struct DummyInterpret {
} }
#[async_trait] #[async_trait]
impl Interpret for DummyInterpret { impl<T: Topology> Interpret<T> for DummyInterpret {
fn get_name(&self) -> InterpretName { fn get_name(&self) -> InterpretName {
InterpretName::Dummy InterpretName::Dummy
} }
@ -81,7 +73,7 @@ impl Interpret for DummyInterpret {
async fn execute( async fn execute(
&self, &self,
_inventory: &Inventory, _inventory: &Inventory,
_topology: &HAClusterTopology, _topology: &T,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
self.result.clone() self.result.clone()
} }
@ -92,18 +84,14 @@ impl Interpret for DummyInterpret {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct PanicScore; pub struct PanicScore;
impl Score for PanicScore { impl<T: Topology> Score<T> for PanicScore {
fn create_interpret(&self) -> Box<dyn crate::interpret::Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
Box::new(PanicInterpret {}) Box::new(PanicInterpret {})
} }
fn name(&self) -> String { fn name(&self) -> String {
"PanicScore".to_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. /// An interpret that always panics when executed. Useful for development/testing purposes.
@ -111,7 +99,7 @@ impl Score for PanicScore {
struct PanicInterpret; struct PanicInterpret;
#[async_trait] #[async_trait]
impl Interpret for PanicInterpret { impl<T: Topology> Interpret<T> for PanicInterpret {
fn get_name(&self) -> InterpretName { fn get_name(&self) -> InterpretName {
InterpretName::Panic InterpretName::Panic
} }
@ -131,7 +119,7 @@ impl Interpret for PanicInterpret {
async fn execute( async fn execute(
&self, &self,
_inventory: &Inventory, _inventory: &Inventory,
_topology: &HAClusterTopology, _topology: &T,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
panic!("Panic interpret always panics when executed") panic!("Panic interpret always panics when executed")
} }

View File

@ -6,7 +6,7 @@ use crate::{
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory, inventory::Inventory,
score::Score, score::Score,
topology::{HAClusterTopology, Url}, topology::{HAClusterTopology, HttpServer, Topology, Url},
}; };
#[derive(Debug, new, Clone)] #[derive(Debug, new, Clone)]
@ -14,18 +14,14 @@ pub struct HttpScore {
files_to_serve: Url, files_to_serve: Url,
} }
impl Score for HttpScore { impl<T: Topology + HttpServer> Score<T> for HttpScore {
fn create_interpret(&self) -> Box<dyn Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
Box::new(HttpInterpret::new(self.clone())) Box::new(HttpInterpret::new(self.clone()))
} }
fn name(&self) -> String { fn name(&self) -> String {
"HttpScore".to_string() "HttpScore".to_string()
} }
fn clone_box(&self) -> Box<dyn Score> {
Box::new(self.clone())
}
} }
#[derive(Debug, new, Clone)] #[derive(Debug, new, Clone)]
@ -34,13 +30,12 @@ pub struct HttpInterpret {
} }
#[async_trait] #[async_trait]
impl Interpret for HttpInterpret { impl<T: Topology + HttpServer> Interpret<T> for HttpInterpret {
async fn execute( async fn execute(
&self, &self,
_inventory: &Inventory, _inventory: &Inventory,
topology: &HAClusterTopology, http_server: &T,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
let http_server = &topology.http_server;
http_server.ensure_initialized().await?; http_server.ensure_initialized().await?;
// http_server.set_ip(topology.router.get_gateway()).await?; // http_server.set_ip(topology.router.get_gateway()).await?;
http_server.serve_files(&self.score.files_to_serve).await?; http_server.serve_files(&self.score.files_to_serve).await?;

View File

@ -1,7 +1,7 @@
use k8s_openapi::api::apps::v1::Deployment; use k8s_openapi::api::apps::v1::Deployment;
use serde_json::json; use serde_json::json;
use crate::{interpret::Interpret, score::Score}; use crate::{interpret::Interpret, score::Score, topology::{OcK8sclient, Topology}};
use super::resource::{K8sResourceInterpret, K8sResourceScore}; use super::resource::{K8sResourceInterpret, K8sResourceScore};
@ -11,8 +11,8 @@ pub struct K8sDeploymentScore {
pub image: String, pub image: String,
} }
impl Score for K8sDeploymentScore { impl <T:Topology + OcK8sclient> Score<T> for K8sDeploymentScore {
fn create_interpret(&self) -> Box<dyn Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
let deployment: Deployment = serde_json::from_value(json!( let deployment: Deployment = serde_json::from_value(json!(
{ {
"metadata": { "metadata": {
@ -51,8 +51,4 @@ impl Score for K8sDeploymentScore {
fn name(&self) -> String { fn name(&self) -> String {
"K8sDeploymentScore".to_string() "K8sDeploymentScore".to_string()
} }
fn clone_box(&self) -> Box<dyn Score> {
Box::new(self.clone())
}
} }

View File

@ -8,7 +8,7 @@ use crate::{
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory, inventory::Inventory,
score::Score, score::Score,
topology::HAClusterTopology, topology::{HAClusterTopology, OcK8sclient, Topology},
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -34,21 +34,18 @@ impl<
+ 'static + 'static
+ Send + Send
+ Clone, + Clone,
> Score for K8sResourceScore<K> T: Topology,
> Score<T> for K8sResourceScore<K>
where where
<K as kube::Resource>::DynamicType: Default, <K as kube::Resource>::DynamicType: Default,
{ {
fn create_interpret(&self) -> Box<dyn Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
todo!() todo!()
} }
fn name(&self) -> String { fn name(&self) -> String {
"K8sResourceScore".to_string() "K8sResourceScore".to_string()
} }
fn clone_box(&self) -> Box<dyn Score> {
Box::new(self.clone())
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -66,14 +63,15 @@ impl<
+ Default + Default
+ Send + Send
+ Sync, + Sync,
> Interpret for K8sResourceInterpret<K> T: Topology + OcK8sclient,
> Interpret<T> for K8sResourceInterpret<K>
where where
<K as kube::Resource>::DynamicType: Default, <K as kube::Resource>::DynamicType: Default,
{ {
async fn execute( async fn execute(
&self, &self,
_inventory: &Inventory, _inventory: &Inventory,
topology: &HAClusterTopology, topology: &T,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
topology topology
.oc_client() .oc_client()

View File

@ -8,7 +8,7 @@ use crate::{
inventory::Inventory, inventory::Inventory,
modules::k8s::deployment::K8sDeploymentScore, modules::k8s::deployment::K8sDeploymentScore,
score::Score, score::Score,
topology::{HAClusterTopology, Url}, topology::{HAClusterTopology, OcK8sclient, Topology, Url},
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -34,18 +34,14 @@ impl Default for LAMPConfig {
} }
} }
impl Score for LAMPScore { impl <T:Topology> Score<T> for LAMPScore {
fn create_interpret(&self) -> Box<dyn Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
todo!() todo!()
} }
fn name(&self) -> String { fn name(&self) -> String {
"LampScore".to_string() "LampScore".to_string()
} }
fn clone_box(&self) -> Box<dyn Score> {
Box::new(self.clone())
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -54,14 +50,14 @@ pub struct LAMPInterpret {
} }
#[async_trait] #[async_trait]
impl Interpret for LAMPInterpret { impl <T:Topology + OcK8sclient> Interpret<T> for LAMPInterpret {
async fn execute( async fn execute(
&self, &self,
inventory: &Inventory, inventory: &Inventory,
topology: &HAClusterTopology, topology: &T,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
let deployment_score = K8sDeploymentScore { let deployment_score = K8sDeploymentScore {
name: self.score.name(), name: <LAMPScore as Score<T>>::name(&self.score),
image: "local_image".to_string(), image: "local_image".to_string(),
}; };

View File

@ -6,7 +6,7 @@ use crate::{
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory, inventory::Inventory,
score::Score, score::Score,
topology::{HAClusterTopology, LoadBalancerService}, topology::{HAClusterTopology, LoadBalancer, LoadBalancerService, Topology},
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -19,18 +19,14 @@ pub struct LoadBalancerScore {
// uuid? // uuid?
} }
impl Score for LoadBalancerScore { impl<T: Topology + LoadBalancer> Score<T> for LoadBalancerScore {
fn create_interpret(&self) -> Box<dyn Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
Box::new(LoadBalancerInterpret::new(self.clone())) Box::new(LoadBalancerInterpret::new(self.clone()))
} }
fn name(&self) -> String { fn name(&self) -> String {
"LoadBalancerScore".to_string() "LoadBalancerScore".to_string()
} }
fn clone_box(&self) -> Box<dyn Score> {
Box::new(self.clone())
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -51,37 +47,32 @@ impl LoadBalancerInterpret {
} }
#[async_trait] #[async_trait]
impl Interpret for LoadBalancerInterpret { impl<T: Topology + LoadBalancer> Interpret<T> for LoadBalancerInterpret {
async fn execute( async fn execute(
&self, &self,
_inventory: &Inventory, _inventory: &Inventory,
topology: &HAClusterTopology, load_balancer: &T,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
info!( info!(
"Making sure Load Balancer is initialized: {:?}", "Making sure Load Balancer is initialized: {:?}",
topology.load_balancer.ensure_initialized().await? load_balancer.ensure_initialized().await?
); );
for service in self.score.public_services.iter() { for service in self.score.public_services.iter() {
info!("Ensuring service exists {service:?}"); info!("Ensuring service exists {service:?}");
topology
.load_balancer load_balancer.ensure_service_exists(service).await?;
.ensure_service_exists(service)
.await?;
} }
for service in self.score.private_services.iter() { for service in self.score.private_services.iter() {
info!("Ensuring private service exists {service:?}"); info!("Ensuring private service exists {service:?}");
topology load_balancer.ensure_service_exists(service).await?;
.load_balancer
.ensure_service_exists(service)
.await?;
} }
info!("Applying load balancer configuration"); info!("Applying load balancer configuration");
topology.load_balancer.commit_config().await?; load_balancer.commit_config().await?;
info!("Making a full reload and restart of haproxy"); info!("Making a full reload and restart of haproxy");
topology.load_balancer.reload_restart().await?; load_balancer.reload_restart().await?;
Ok(Outcome::success(format!( Ok(Outcome::success(format!(
"Load balancer successfully configured {} services", "Load balancer successfully configured {} services",
self.score.public_services.len() + self.score.private_services.len() self.score.public_services.len() + self.score.private_services.len()

View File

@ -3,7 +3,7 @@ use crate::{
inventory::Inventory, inventory::Inventory,
modules::dhcp::DhcpScore, modules::dhcp::DhcpScore,
score::Score, score::Score,
topology::{HAClusterTopology, HostBinding}, topology::{DhcpServer, HAClusterTopology, HostBinding, Topology},
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -46,16 +46,12 @@ impl OKDBootstrapDhcpScore {
} }
} }
impl Score for OKDBootstrapDhcpScore { impl<T: Topology + DhcpServer> Score<T> for OKDBootstrapDhcpScore {
fn create_interpret(&self) -> Box<dyn Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
self.dhcp_score.create_interpret() self.dhcp_score.create_interpret()
} }
fn name(&self) -> String { fn name(&self) -> String {
"OKDBootstrapDhcpScore".to_string() "OKDBootstrapDhcpScore".to_string()
} }
fn clone_box(&self) -> Box<dyn Score> {
Box::new(self.clone())
}
} }

View File

@ -5,8 +5,7 @@ use crate::{
modules::load_balancer::LoadBalancerScore, modules::load_balancer::LoadBalancerScore,
score::Score, score::Score,
topology::{ topology::{
BackendServer, HAClusterTopology, HealthCheck, HttpMethod, HttpStatusCode, BackendServer, HAClusterTopology, HealthCheck, HttpMethod, HttpStatusCode, LoadBalancer, LoadBalancerService, Topology
LoadBalancerService,
}, },
}; };
@ -69,16 +68,12 @@ impl OKDBootstrapLoadBalancerScore {
} }
} }
impl Score for OKDBootstrapLoadBalancerScore { impl<T: Topology + LoadBalancer> Score<T> for OKDBootstrapLoadBalancerScore {
fn create_interpret(&self) -> Box<dyn Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
self.load_balancer_score.create_interpret() self.load_balancer_score.create_interpret()
} }
fn name(&self) -> String { fn name(&self) -> String {
"OKDBootstrapLoadBalancerScore".to_string() "OKDBootstrapLoadBalancerScore".to_string()
} }
fn clone_box(&self) -> Box<dyn Score> {
Box::new(self.clone())
}
} }

View File

@ -3,7 +3,7 @@ use crate::{
inventory::Inventory, inventory::Inventory,
modules::dhcp::DhcpScore, modules::dhcp::DhcpScore,
score::Score, score::Score,
topology::{HAClusterTopology, HostBinding}, topology::{DhcpServer, HAClusterTopology, HostBinding, Topology},
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -38,16 +38,12 @@ impl OKDDhcpScore {
} }
} }
impl Score for OKDDhcpScore { impl<T: Topology + DhcpServer> Score<T> for OKDDhcpScore {
fn create_interpret(&self) -> Box<dyn Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
self.dhcp_score.create_interpret() self.dhcp_score.create_interpret()
} }
fn name(&self) -> String { fn name(&self) -> String {
"OKDDhcpScore".to_string() "OKDDhcpScore".to_string()
} }
fn clone_box(&self) -> Box<dyn Score> {
Box::new(self.clone())
}
} }

View File

@ -2,7 +2,7 @@ use crate::{
interpret::Interpret, interpret::Interpret,
modules::dns::DnsScore, modules::dns::DnsScore,
score::Score, score::Score,
topology::{DnsRecord, DnsRecordType, HAClusterTopology}, topology::{DnsRecord, DnsRecordType, DnsServer, HAClusterTopology, Topology},
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -40,16 +40,12 @@ impl OKDDnsScore {
} }
} }
impl Score for OKDDnsScore { impl<T: Topology + DnsServer> Score<T> for OKDDnsScore {
fn create_interpret(&self) -> Box<dyn Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
self.dns_score.create_interpret() self.dns_score.create_interpret()
} }
fn name(&self) -> String { fn name(&self) -> String {
"OKDDnsScore".to_string() "OKDDnsScore".to_string()
} }
fn clone_box(&self) -> Box<dyn Score> {
Box::new(self.clone())
}
} }

View File

@ -5,8 +5,7 @@ use crate::{
modules::load_balancer::LoadBalancerScore, modules::load_balancer::LoadBalancerScore,
score::Score, score::Score,
topology::{ topology::{
BackendServer, HAClusterTopology, HealthCheck, HttpMethod, HttpStatusCode, BackendServer, HAClusterTopology, HealthCheck, HttpMethod, HttpStatusCode, LoadBalancer, LoadBalancerService, Topology
LoadBalancerService,
}, },
}; };
@ -80,16 +79,12 @@ impl OKDLoadBalancerScore {
} }
} }
impl Score for OKDLoadBalancerScore { impl<T: Topology + LoadBalancer> Score<T> for OKDLoadBalancerScore {
fn create_interpret(&self) -> Box<dyn Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
self.load_balancer_score.create_interpret() self.load_balancer_score.create_interpret()
} }
fn name(&self) -> String { fn name(&self) -> String {
"OKDLoadBalancerScore".to_string() "OKDLoadBalancerScore".to_string()
} }
fn clone_box(&self) -> Box<dyn Score> {
Box::new(self.clone())
}
} }

View File

@ -8,7 +8,7 @@ use crate::{
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory, inventory::Inventory,
score::Score, score::Score,
topology::HAClusterTopology, topology::{HAClusterTopology, Topology},
}; };
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -17,8 +17,8 @@ pub struct OPNsenseShellCommandScore {
pub command: String, pub command: String,
} }
impl Score for OPNsenseShellCommandScore { impl<T: Topology> Score<T> for OPNsenseShellCommandScore {
fn create_interpret(&self) -> Box<dyn Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
Box::new(OPNsenseShellInterpret { Box::new(OPNsenseShellInterpret {
status: InterpretStatus::QUEUED, status: InterpretStatus::QUEUED,
score: self.clone(), score: self.clone(),
@ -28,10 +28,6 @@ impl Score for OPNsenseShellCommandScore {
fn name(&self) -> String { fn name(&self) -> String {
"OPNSenseShellCommandScore".to_string() "OPNSenseShellCommandScore".to_string()
} }
fn clone_box(&self) -> Box<dyn Score> {
Box::new(self.clone())
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -41,11 +37,11 @@ pub struct OPNsenseShellInterpret {
} }
#[async_trait] #[async_trait]
impl Interpret for OPNsenseShellInterpret { impl <T:Topology> Interpret<T> for OPNsenseShellInterpret {
async fn execute( async fn execute(
&self, &self,
_inventory: &Inventory, _inventory: &Inventory,
_topology: &HAClusterTopology, _topology: &T,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
let output = self let output = self
.score .score

View File

@ -5,6 +5,7 @@ use tokio::sync::RwLock;
use crate::{ use crate::{
interpret::{Interpret, InterpretStatus}, interpret::{Interpret, InterpretStatus},
score::Score, score::Score,
topology::Topology,
}; };
use super::{OPNsenseShellCommandScore, OPNsenseShellInterpret}; use super::{OPNsenseShellCommandScore, OPNsenseShellInterpret};
@ -14,8 +15,8 @@ pub struct OPNSenseLaunchUpgrade {
pub opnsense: Arc<RwLock<opnsense_config::Config>>, pub opnsense: Arc<RwLock<opnsense_config::Config>>,
} }
impl Score for OPNSenseLaunchUpgrade { impl<T: Topology> Score<T> for OPNSenseLaunchUpgrade {
fn create_interpret(&self) -> Box<dyn Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
let score = OPNsenseShellCommandScore { let score = OPNsenseShellCommandScore {
opnsense: self.opnsense.clone(), opnsense: self.opnsense.clone(),
command: "/usr/local/opnsense/scripts/firmware/update.sh".to_string(), command: "/usr/local/opnsense/scripts/firmware/update.sh".to_string(),
@ -30,8 +31,4 @@ impl Score for OPNSenseLaunchUpgrade {
fn name(&self) -> String { fn name(&self) -> String {
"OPNSenseLaunchUpgrade".to_string() "OPNSenseLaunchUpgrade".to_string()
} }
fn clone_box(&self) -> Box<dyn Score> {
Box::new(self.clone())
}
} }

View File

@ -6,7 +6,7 @@ use crate::{
interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome},
inventory::Inventory, inventory::Inventory,
score::Score, score::Score,
topology::{HAClusterTopology, Url}, topology::{HAClusterTopology, Router, TftpServer, Topology, Url},
}; };
#[derive(Debug, new, Clone)] #[derive(Debug, new, Clone)]
@ -14,18 +14,14 @@ pub struct TftpScore {
files_to_serve: Url, files_to_serve: Url,
} }
impl Score for TftpScore { impl <T:Topology + TftpServer + Router> Score<T> for TftpScore {
fn create_interpret(&self) -> Box<dyn Interpret> { fn create_interpret(&self) -> Box<dyn Interpret<T>> {
Box::new(TftpInterpret::new(self.clone())) Box::new(TftpInterpret::new(self.clone()))
} }
fn name(&self) -> String { fn name(&self) -> String {
"TftpScore".to_string() "TftpScore".to_string()
} }
fn clone_box(&self) -> Box<dyn Score> {
Box::new(self.clone())
}
} }
#[derive(Debug, new, Clone)] #[derive(Debug, new, Clone)]
@ -34,18 +30,17 @@ pub struct TftpInterpret {
} }
#[async_trait] #[async_trait]
impl Interpret for TftpInterpret { impl <T:Topology + TftpServer + Router> Interpret<T> for TftpInterpret {
async fn execute( async fn execute(
&self, &self,
_inventory: &Inventory, _inventory: &Inventory,
topology: &HAClusterTopology, topology: &T,
) -> Result<Outcome, InterpretError> { ) -> Result<Outcome, InterpretError> {
let tftp_server = &topology.tftp_server; topology.ensure_initialized().await?;
tftp_server.ensure_initialized().await?; topology.set_ip(topology.get_gateway()).await?;
tftp_server.set_ip(topology.router.get_gateway()).await?; topology.serve_files(&self.score.files_to_serve).await?;
tftp_server.serve_files(&self.score.files_to_serve).await?; topology.commit_config().await?;
tftp_server.commit_config().await?; topology.reload_restart().await?;
tftp_server.reload_restart().await?;
Ok(Outcome::success(format!( Ok(Outcome::success(format!(
"TFTP Server running and serving files from {}", "TFTP Server running and serving files from {}",
self.score.files_to_serve self.score.files_to_serve