Small example to show how to compose components (capabilities) in a Topology to simplify reusing existing components for different topologies
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/target
|
||||
72
Cargo.lock
generated
Normal file
72
Cargo.lock
generated
Normal file
@@ -0,0 +1,72 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "ambassador"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e68de4cdc6006162265d0957edb4a860fe4e711b1dc17a5746fd95f952f08285"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "harmony-composable"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"ambassador",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.41"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
7
Cargo.toml
Normal file
7
Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
name = "harmony-composable"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
ambassador = "0.4.2"
|
||||
2
src/components/mod.rs
Normal file
2
src/components/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod network_manager;
|
||||
pub mod switch;
|
||||
7
src/components/network_manager.rs
Normal file
7
src/components/network_manager.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use ambassador::delegatable_trait;
|
||||
|
||||
#[delegatable_trait]
|
||||
pub trait NetworkManager {
|
||||
fn ensure_network_manager_installed(&self) -> Result<(), String>;
|
||||
fn configure_bond(&self, bond: String) -> Result<(), String>;
|
||||
}
|
||||
7
src/components/switch.rs
Normal file
7
src/components/switch.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
use ambassador::delegatable_trait;
|
||||
|
||||
#[delegatable_trait]
|
||||
pub trait Switch {
|
||||
fn setup_switch(&self) -> Result<(), String>;
|
||||
fn create_port_channel(&self, port_channel: String) -> Result<(), String>;
|
||||
}
|
||||
1
src/core/mod.rs
Normal file
1
src/core/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod topology;
|
||||
3
src/core/topology.rs
Normal file
3
src/core/topology.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub trait Topology {
|
||||
fn name(&self) -> String;
|
||||
}
|
||||
15
src/infra/brocade.rs
Normal file
15
src/infra/brocade.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use crate::components::switch::Switch;
|
||||
|
||||
pub struct BrocadeSwitch {}
|
||||
|
||||
impl Switch for BrocadeSwitch {
|
||||
fn setup_switch(&self) -> Result<(), String> {
|
||||
println!("BrocadeSwitch: Installing...");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_port_channel(&self, port_channel: String) -> Result<(), String> {
|
||||
println!("BrocadeSwitch: Creating port channel {}", port_channel);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
1
src/infra/k8s_client.rs
Normal file
1
src/infra/k8s_client.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub struct Client {}
|
||||
4
src/infra/mod.rs
Normal file
4
src/infra/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod brocade;
|
||||
pub mod k8s_client;
|
||||
pub mod nmcli;
|
||||
pub mod openshift;
|
||||
15
src/infra/nmcli.rs
Normal file
15
src/infra/nmcli.rs
Normal file
@@ -0,0 +1,15 @@
|
||||
use crate::components::network_manager::NetworkManager;
|
||||
|
||||
pub struct NmcliNetworkManager {}
|
||||
|
||||
impl NetworkManager for NmcliNetworkManager {
|
||||
fn ensure_network_manager_installed(&self) -> Result<(), String> {
|
||||
println!("Nmcli: Installing...");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn configure_bond(&self, bond: String) -> Result<(), String> {
|
||||
println!("Nmcli: Configuring bond {}", bond);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
22
src/infra/openshift.rs
Normal file
22
src/infra/openshift.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{components::network_manager::NetworkManager, infra::k8s_client};
|
||||
|
||||
pub struct OpenShiftNmStateNetworkManager {
|
||||
pub k8s_client: Arc<k8s_client::Client>,
|
||||
}
|
||||
|
||||
impl NetworkManager for OpenShiftNmStateNetworkManager {
|
||||
fn ensure_network_manager_installed(&self) -> Result<(), String> {
|
||||
println!("OpenShift NM State: Installing...");
|
||||
|
||||
// self.k8s_client.apply(...)
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn configure_bond(&self, bond: String) -> Result<(), String> {
|
||||
println!("OpenShift NM State: Configuring bond {}", bond);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
38
src/main.rs
Normal file
38
src/main.rs
Normal file
@@ -0,0 +1,38 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
components::{network_manager::NetworkManager, switch::Switch},
|
||||
core::topology::Topology,
|
||||
infra::brocade::BrocadeSwitch,
|
||||
topologies::{k8s::NetworkManagerProvider, k8s_anywhere::K8sAnywhereTopology},
|
||||
};
|
||||
|
||||
mod components;
|
||||
mod core;
|
||||
mod infra;
|
||||
mod topologies;
|
||||
|
||||
fn configure_port_channel<T: Topology + Switch>(topology: &T) -> Result<(), String> {
|
||||
println!(
|
||||
"--- Running configure_port_channel for: {} ---",
|
||||
topology.name()
|
||||
);
|
||||
topology.setup_switch()?;
|
||||
topology.create_port_channel("pc-123".to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn configure_bond<T: Topology + NetworkManager>(topology: &T) -> Result<(), String> {
|
||||
println!("--- Running configure_bond for: {} ---", topology.name());
|
||||
topology.ensure_network_manager_installed()?;
|
||||
topology.configure_bond("bond0".to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let topology = K8sAnywhereTopology::new(NetworkManagerProvider {}, Arc::new(BrocadeSwitch {}));
|
||||
|
||||
// Simulate the execution of a Score's Interpret
|
||||
configure_bond(&topology).unwrap();
|
||||
configure_port_channel(&topology).unwrap();
|
||||
}
|
||||
35
src/topologies/k8s.rs
Normal file
35
src/topologies/k8s.rs
Normal file
@@ -0,0 +1,35 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{
|
||||
components::network_manager::NetworkManager,
|
||||
core::topology::Topology,
|
||||
infra::{k8s_client, nmcli::NmcliNetworkManager, openshift::OpenShiftNmStateNetworkManager},
|
||||
};
|
||||
|
||||
pub enum K8sSource {
|
||||
K3dFamily,
|
||||
OpenShiftFamily,
|
||||
}
|
||||
|
||||
pub struct K8sState {
|
||||
pub source: K8sSource,
|
||||
}
|
||||
|
||||
pub trait K8sClient {
|
||||
fn k8s_state(&self) -> K8sState;
|
||||
fn k8s_client(&self) -> Arc<k8s_client::Client>;
|
||||
}
|
||||
|
||||
pub struct NetworkManagerProvider {}
|
||||
|
||||
impl NetworkManagerProvider {
|
||||
pub fn provide<T: Topology + K8sClient>(&self, topology: &T) -> Arc<dyn NetworkManager> {
|
||||
match topology.k8s_state().source {
|
||||
K8sSource::K3dFamily => Arc::new(NmcliNetworkManager {}),
|
||||
K8sSource::OpenShiftFamily => {
|
||||
let k8s_client = topology.k8s_client().clone();
|
||||
Arc::new(OpenShiftNmStateNetworkManager { k8s_client })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
60
src/topologies/k8s_anywhere.rs
Normal file
60
src/topologies/k8s_anywhere.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use std::{cell::OnceCell, sync::Arc};
|
||||
|
||||
use ambassador::delegate_to_methods;
|
||||
|
||||
use crate::{
|
||||
components::{
|
||||
network_manager::{NetworkManager, ambassador_impl_NetworkManager},
|
||||
switch::{Switch, ambassador_impl_Switch},
|
||||
},
|
||||
core::topology::Topology,
|
||||
infra::k8s_client,
|
||||
topologies::k8s::{K8sClient, K8sSource, K8sState, NetworkManagerProvider},
|
||||
};
|
||||
|
||||
pub struct K8sAnywhereTopology {
|
||||
network_manager_provider: NetworkManagerProvider,
|
||||
network_manager: OnceCell<Arc<dyn NetworkManager>>,
|
||||
switch: Arc<dyn Switch>,
|
||||
}
|
||||
|
||||
#[delegate_to_methods]
|
||||
#[delegate(NetworkManager, target_ref = "network_manager")]
|
||||
#[delegate(Switch, target_ref = "switch")]
|
||||
impl K8sAnywhereTopology {
|
||||
pub fn new(network_manager_provider: NetworkManagerProvider, switch: Arc<dyn Switch>) -> Self {
|
||||
Self {
|
||||
network_manager_provider,
|
||||
network_manager: OnceCell::new(),
|
||||
switch,
|
||||
}
|
||||
}
|
||||
|
||||
fn network_manager(&self) -> &dyn NetworkManager {
|
||||
self.network_manager
|
||||
.get_or_init(|| self.network_manager_provider.provide(self))
|
||||
.as_ref()
|
||||
}
|
||||
|
||||
fn switch(&self) -> &dyn Switch {
|
||||
self.switch.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Topology for K8sAnywhereTopology {
|
||||
fn name(&self) -> String {
|
||||
"K8sAnywhere".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl K8sClient for K8sAnywhereTopology {
|
||||
fn k8s_state(&self) -> K8sState {
|
||||
K8sState {
|
||||
source: K8sSource::OpenShiftFamily,
|
||||
}
|
||||
}
|
||||
|
||||
fn k8s_client(&self) -> Arc<k8s_client::Client> {
|
||||
Arc::new(k8s_client::Client {})
|
||||
}
|
||||
}
|
||||
2
src/topologies/mod.rs
Normal file
2
src/topologies/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod k8s;
|
||||
pub mod k8s_anywhere;
|
||||
Reference in New Issue
Block a user