feat(harmony): enhance PhysicalHost with builder methods and update dependencies

- Added builder methods to `PhysicalHost` for configuring CPU, memory size, storage, MAC address, labels, and management interface.
- Updated the edition of various Cargo.toml files to 2024.
- Implemented workspace inheritance for version, readme, and license fields in Cargo.toml files.
This commit is contained in:
2025-02-01 11:45:04 -05:00
parent 0cfd5dc89e
commit 3eac78c6d3
12 changed files with 132 additions and 74 deletions

View File

@@ -0,0 +1,20 @@
[package]
name = "example-kube-rs"
edition = "2024"
version.workspace = true
readme.workspace = true
license.workspace = true
publish = false
[dependencies]
harmony = { version = "0.1.0", path = "../../harmony" }
cidr = { workspace = true }
tokio = { workspace = true }
harmony_macros = { path = "../../harmony_macros" }
log = { workspace = true }
env_logger = { workspace = true }
url = { workspace = true }
kube = "0.98.0"
k8s-openapi = { version = "0.24.0", features = [ "v1_30" ] }
http = "1.2.0"
serde_yaml = "0.9.34"

View File

@@ -0,0 +1,194 @@
use std::collections::BTreeMap;
use harmony_macros::yaml;
use k8s_openapi::{
api::{
apps::v1::{Deployment, DeploymentSpec},
core::v1::{Container, Node, Pod, PodSpec, PodTemplateSpec},
},
apimachinery::pkg::apis::meta::v1::LabelSelector,
};
use kube::{
Api, Client, Config, ResourceExt,
api::{ListParams, ObjectMeta, PostParams},
};
#[tokio::main]
async fn main() {
let client = Client::try_default()
.await
.expect("Should instanciate client from defaults");
println!("apiserver_version {:?}", client.apiserver_version().await);
println!("default namespace {:?}", client.default_namespace());
// println!(
// "apiserver_groups {:?}",
// client
// .list_api_groups()
// .await
// .unwrap()
// .groups
// .iter()
// .map(|g| g.name.clone())
// .collect::<Vec<_>>()
// );
// let pods: Api<Pod> = Api::default_namespaced(client.clone());
// for p in pods.list(&ListParams::default()).await.unwrap() {
// println!("found pod {}", p.name_any())
// }
// let nodes : Api<Node> = Api::all(client.clone());
// for n in nodes.list(&ListParams::default()).await.unwrap() {
// println!("found node {} status {:?}", n.name_any(), n.status.unwrap())
// }
let nginxdeployment = nginx_deployment_2();
let nginxdeployment = nginx_deployment_serde();
assert_eq!(nginx_deployment_2(), nginx_macro());
assert_eq!(nginx_deployment_serde(), nginx_macro());
let nginxdeployment = nginx_macro();
let deployment: Api<Deployment> = Api::default_namespaced(client.clone());
match deployment
.create(&PostParams::default(), &nginxdeployment)
.await
{
Ok(_d) => println!("Deployment success"),
Err(e) => {
println!("Error creating deployment {}", e);
if let kube::Error::Api(error_response) = &e {
if error_response.code == http::StatusCode::CONFLICT.as_u16() {
println!("Already exists");
return;
}
}
panic!("{}", e)
}
};
println!(
"{:?}",
deployment
.get_status(&nginxdeployment.name_unchecked())
.await
.unwrap()
);
println!("Hello world");
}
fn nginx_macro() -> Deployment {
yaml!(
r#"
metadata:
name: "nginx-test"
spec:
selector:
matchLabels:
app: nginx-test
template:
metadata:
labels:
app: nginx-test
spec:
containers:
- image: nginx
name: nginx"#
)
.unwrap()
}
fn nginx_deployment_serde() -> Deployment {
let deployment: Deployment = serde_yaml::from_str(
r#"
metadata:
name: "nginx-test"
spec:
selector:
matchLabels:
app: nginx-test
template:
metadata:
labels:
app: nginx-test
spec:
containers:
- image: nginx
name: nginx"#,
)
.unwrap();
return deployment;
}
fn nginx_deployment_2() -> Deployment {
let mut pod_template = PodTemplateSpec::default();
pod_template.metadata = Some(ObjectMeta {
labels: Some(BTreeMap::from([(
"app".to_string(),
"nginx-test".to_string(),
)])),
..Default::default()
});
pod_template.spec = Some(PodSpec {
containers: vec![Container {
name: "nginx".to_string(),
image: Some("nginx".to_string()),
..Default::default()
}],
..Default::default()
});
let mut spec = DeploymentSpec::default();
spec.template = pod_template;
spec.selector = LabelSelector {
match_expressions: None,
match_labels: Some(BTreeMap::from([(
"app".to_string(),
"nginx-test".to_string(),
)])),
};
let mut deployment = Deployment::default();
deployment.spec = Some(spec);
deployment.metadata.name = Some("nginx-test".to_string());
deployment
}
fn nginx_deployment() -> Deployment {
let deployment = Deployment {
metadata: ObjectMeta {
name: Some("nginx-test".to_string()),
..Default::default()
},
spec: Some(DeploymentSpec {
min_ready_seconds: None,
paused: None,
progress_deadline_seconds: None,
replicas: Some(1),
revision_history_limit: Some(10),
selector: LabelSelector {
match_expressions: None,
match_labels: Some(BTreeMap::from([(
"app".to_string(),
"nginx-test".to_string(),
)])),
},
strategy: None,
template: PodTemplateSpec {
metadata: Some(ObjectMeta {
labels: Some(BTreeMap::from([(
"app".to_string(),
"nginx-test".to_string(),
)])),
..Default::default()
}),
spec: Some(PodSpec {
containers: vec![Container {
name: "nginx".to_string(),
image: Some("nginx".to_string()),
..Default::default()
}],
..Default::default()
}),
},
}),
status: None,
};
println!("{:?}", deployment.managed_fields());
deployment
}

View File

@@ -0,0 +1,18 @@
[package]
name = "example-opnsense"
edition = "2024"
version.workspace = true
readme.workspace = true
license.workspace = true
publish = false
[dependencies]
harmony = { path = "../../harmony" }
harmony_tui = { path = "../../harmony_tui" }
harmony_types = { path = "../../harmony_types" }
cidr = { workspace = true }
tokio = { workspace = true }
harmony_macros = { path = "../../harmony_macros" }
log = { workspace = true }
env_logger = { workspace = true }
url = { workspace = true }

View File

@@ -0,0 +1,15 @@
## OPNSense demo
Download the virtualbox snapshot from {{TODO URL}}
Start the virtualbox image
This virtualbox image is configured to use a bridge on the host's physical interface, make sure the bridge is up and the virtual machine can reach internet.
Credentials are opnsense default (root/opnsense)
Run the project with the correct ip address on the command line :
```bash
cargo run -p example-opnsense -- 192.168.5.229
```

View File

@@ -0,0 +1,93 @@
use std::{
net::{IpAddr, Ipv4Addr},
sync::Arc,
};
use cidr::Ipv4Cidr;
use harmony::{
hardware::{FirewallGroup, HostCategory, Location, PhysicalHost, SwitchGroup},
infra::opnsense::OPNSenseManagementInterface,
inventory::Inventory,
maestro::Maestro,
modules::{
http::HttpScore,
okd::{dhcp::OKDDhcpScore, dns::OKDDnsScore},
tftp::TftpScore,
},
topology::{LogicalHost, UnmanagedRouter, Url},
};
use harmony_macros::{ip, mac_address};
#[tokio::main]
async fn main() {
let firewall = harmony::topology::LogicalHost {
ip: ip!("192.168.5.229"),
name: String::from("opnsense-1"),
};
let opnsense = Arc::new(
harmony::infra::opnsense::OPNSenseFirewall::new(firewall, None, "root", "opnsense").await,
);
let lan_subnet = Ipv4Addr::new(10, 100, 8, 0);
let gateway_ipv4 = Ipv4Addr::new(10, 100, 8, 1);
let gateway_ip = IpAddr::V4(gateway_ipv4);
let topology = harmony::topology::HAClusterTopology {
domain_name: "demo.harmony.mcd".to_string(),
router: Arc::new(UnmanagedRouter::new(
gateway_ip,
Ipv4Cidr::new(lan_subnet, 24).unwrap(),
)),
load_balancer: opnsense.clone(),
firewall: opnsense.clone(),
tftp_server: opnsense.clone(),
http_server: opnsense.clone(),
dhcp_server: opnsense.clone(),
dns_server: opnsense.clone(),
control_plane: vec![LogicalHost {
ip: ip!("10.100.8.20"),
name: "cp0".to_string(),
}],
bootstrap_host: LogicalHost {
ip: ip!("10.100.8.20"),
name: "cp0".to_string(),
},
workers: vec![],
switch: vec![],
};
let inventory = Inventory {
location: Location::new(
"232 des Éperviers, Wendake, Qc, G0A 4V0".to_string(),
"wk".to_string(),
),
switch: SwitchGroup::from([]),
firewall: FirewallGroup::from([PhysicalHost::empty(HostCategory::Firewall)
.management(Arc::new(OPNSenseManagementInterface::new()))]),
storage_host: vec![],
worker_host: vec![],
control_plane_host: vec![PhysicalHost::empty(HostCategory::Server).mac_address(mac_address!("08:00:27:62:EC:C3"))],
};
// TODO regroup smaller scores in a larger one such as this
// let okd_boostrap_preparation();
let dhcp_score = OKDDhcpScore::new(&topology, &inventory);
let dns_score = OKDDnsScore::new(&topology);
let load_balancer_score =
harmony::modules::okd::load_balancer::OKDLoadBalancerScore::new(&topology);
let tftp_score = TftpScore::new(Url::LocalFolder("../../../watchguard/tftpboot".to_string()));
let http_score = HttpScore::new(Url::LocalFolder(
"../../../watchguard/pxe-http-files".to_string(),
));
let mut maestro = Maestro::new(inventory, topology);
maestro.register_all(vec![
Box::new(dns_score),
Box::new(dhcp_score),
Box::new(load_balancer_score),
Box::new(tftp_score),
Box::new(http_score),
]);
harmony_tui::init(maestro).await.unwrap();
}