diff --git a/harmony-rs/Cargo.lock b/harmony-rs/Cargo.lock index bbb6037..76ef8dc 100644 --- a/harmony-rs/Cargo.lock +++ b/harmony-rs/Cargo.lock @@ -931,6 +931,9 @@ dependencies = [ "env_logger", "harmony_macros", "harmony_types", + "http 1.2.0", + "k8s-openapi", + "kube", "libredfish", "log", "opnsense-config", @@ -941,6 +944,7 @@ dependencies = [ "semver", "serde", "serde_json", + "serde_yaml", "tokio", "url", "uuid", diff --git a/harmony-rs/Cargo.toml b/harmony-rs/Cargo.toml index a7c37e2..96ee74c 100644 --- a/harmony-rs/Cargo.toml +++ b/harmony-rs/Cargo.toml @@ -26,6 +26,10 @@ russh = "0.45.0" russh-keys = "0.45.0" rand = "0.8.5" url = "2.5.4" +kube = "0.98.0" +k8s-openapi = { version = "0.24.0", features = [ "v1_30" ] } +serde_yaml = "0.9.34" +http = "1.2.0" [workspace.dependencies.uuid] version = "1.11.0" diff --git a/harmony-rs/harmony/Cargo.toml b/harmony-rs/harmony/Cargo.toml index f4c79f5..8418d9e 100644 --- a/harmony-rs/harmony/Cargo.toml +++ b/harmony-rs/harmony/Cargo.toml @@ -23,3 +23,7 @@ harmony_macros = { path = "../harmony_macros" } harmony_types = { path = "../harmony_types" } uuid = { workspace = true } url = { workspace = true } +kube = { workspace = true } +k8s-openapi = { workspace = true } +serde_yaml = { workspace = true } +http = { workspace = true } diff --git a/harmony-rs/harmony/src/domain/hardware/mod.rs b/harmony-rs/harmony/src/domain/hardware/mod.rs index 73233db..c05b288 100644 --- a/harmony-rs/harmony/src/domain/hardware/mod.rs +++ b/harmony-rs/harmony/src/domain/hardware/mod.rs @@ -3,7 +3,6 @@ use std::sync::Arc; use derive_new::new; use harmony_types::net::MacAddress; - pub type HostGroup = Vec; pub type SwitchGroup = Vec; pub type FirewallGroup = Vec; @@ -29,7 +28,11 @@ impl PhysicalHost { } pub fn cluster_mac(&self) -> MacAddress { - self.network.get(0).expect("Cluster physical host should have a network interface").mac_address.clone() + self.network + .get(0) + .expect("Cluster physical host should have a network interface") + .mac_address + .clone() } } diff --git a/harmony-rs/harmony/src/domain/interpret/mod.rs b/harmony-rs/harmony/src/domain/interpret/mod.rs index 8260382..4a8faee 100644 --- a/harmony-rs/harmony/src/domain/interpret/mod.rs +++ b/harmony-rs/harmony/src/domain/interpret/mod.rs @@ -108,3 +108,12 @@ impl From for InterpretError { } } } + +impl From for InterpretError{ + fn from(value: kube::Error) -> Self { + Self { + msg: format!("InterpretError : {value}"), + } + } + +} diff --git a/harmony-rs/harmony/src/domain/topology/mod.rs b/harmony-rs/harmony/src/domain/topology/mod.rs index 9f7dd71..a664a8c 100644 --- a/harmony-rs/harmony/src/domain/topology/mod.rs +++ b/harmony-rs/harmony/src/domain/topology/mod.rs @@ -1,9 +1,11 @@ mod host_binding; mod http; mod load_balancer; +pub mod openshift; mod router; mod tftp; pub use load_balancer::*; +use openshift::OpenshiftClient; pub use router::*; mod network; pub use host_binding::*; @@ -29,6 +31,12 @@ pub struct HAClusterTopology { pub switch: Vec, } +impl HAClusterTopology { + pub async fn oc_client(&self) -> Result, kube::Error> { + Ok(Arc::new(OpenshiftClient::try_default().await?)) + } +} + pub type IpAddress = IpAddr; #[derive(Debug, Clone)] diff --git a/harmony-rs/harmony/src/domain/topology/openshift.rs b/harmony-rs/harmony/src/domain/topology/openshift.rs new file mode 100644 index 0000000..e1713f9 --- /dev/null +++ b/harmony-rs/harmony/src/domain/topology/openshift.rs @@ -0,0 +1,55 @@ +use k8s_openapi::NamespaceResourceScope; +use kube::{api::PostParams, Api, Client, Error, Resource}; +use serde::de::DeserializeOwned; + +pub struct OpenshiftClient { + client: Client, +} + +impl OpenshiftClient { + pub async fn try_default() -> Result { + Ok(Self { + client: Client::try_default().await?, + }) + } + + pub async fn apply_all< + K: Resource + + std::fmt::Debug + + Sync + + DeserializeOwned + + Default + + serde::Serialize + + Clone, + >( + &self, + resource: &Vec, + ) -> Result, kube::Error> + where + ::DynamicType: Default, + { + let mut result = vec![]; + for r in resource.iter() { + let api: Api = Api::all(self.client.clone()); + result.push(api.create(&PostParams::default(), &r).await?); + } + Ok(result) + } + + pub async fn apply_namespaced(&self, resource: &Vec) -> Result + where + K: Resource + + Clone + + std::fmt::Debug + + DeserializeOwned + + serde::Serialize + + Default, + ::DynamicType: Default, + { + for r in resource.iter() { + let api: Api = Api::default_namespaced(self.client.clone()); + api.create(&PostParams::default(), &r).await?; + } + todo!("") + } +} diff --git a/harmony-rs/harmony/src/modules/k8s/Resource.rs b/harmony-rs/harmony/src/modules/k8s/Resource.rs deleted file mode 100644 index 787a9b1..0000000 --- a/harmony-rs/harmony/src/modules/k8s/Resource.rs +++ /dev/null @@ -1,42 +0,0 @@ -use async_trait::async_trait; - -use crate::{data::{Id, Version}, interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, inventory::Inventory, score::Score, topology::HAClusterTopology}; - -#[derive(Debug)] -pub struct K8sResourceScore {} - -impl Score for K8sResourceScore { - type InterpretType = K8sResourceInterpret; - - fn create_interpret(self) -> Self::InterpretType { - todo!() - } -} - -#[derive(Debug)] -pub struct K8sResourceInterpret { - score: K8sResourceScore, -} - -#[async_trait] -impl Interpret for K8sResourceInterpret { - async fn execute( - &self, - inventory: &Inventory, - topology: &HAClusterTopology, - ) -> Result { - todo!() - } - fn get_name(&self) -> InterpretName { - todo!() - } - fn get_version(&self) -> Version { - todo!() - } - fn get_status(&self) -> InterpretStatus { - todo!() - } - fn get_children(&self) -> Vec { - todo!() - } -} diff --git a/harmony-rs/harmony/src/modules/k8s/deployment.rs b/harmony-rs/harmony/src/modules/k8s/deployment.rs new file mode 100644 index 0000000..49dc170 --- /dev/null +++ b/harmony-rs/harmony/src/modules/k8s/deployment.rs @@ -0,0 +1,53 @@ +use harmony_macros::yaml; +use k8s_openapi::api::apps::v1::Deployment; +use serde_json::json; + +use crate::score::Score; + +use super::resource::{K8sResourceInterpret, K8sResourceScore}; + +#[derive(Debug)] +pub struct K8sDeploymentScore { + pub name: String, + pub image: String, +} + +impl Score for K8sDeploymentScore { + type InterpretType = K8sResourceInterpret; + + fn create_interpret(self) -> Self::InterpretType { + let deployment: Deployment = serde_json::from_value(json!( + { + "metadata": { + "name": self.name + }, + "spec": { + "selector": { + "matchLabels": { + "app": self.name + }, + }, + "template": { + "metadata": { + "labels": { + "app": self.name + }, + }, + "spec": { + "containers": [ + { + "image": self.image, + "name": self.image + } + ] + } + } + } + } + )) + .unwrap(); + K8sResourceInterpret { + score: K8sResourceScore::single(deployment), + } + } +} diff --git a/harmony-rs/harmony/src/modules/k8s/mod.rs b/harmony-rs/harmony/src/modules/k8s/mod.rs index eea7ad8..1f1bf27 100644 --- a/harmony-rs/harmony/src/modules/k8s/mod.rs +++ b/harmony-rs/harmony/src/modules/k8s/mod.rs @@ -1,3 +1,4 @@ -pub mod Resource; +pub mod resource; +pub mod deployment; diff --git a/harmony-rs/harmony/src/modules/k8s/resource.rs b/harmony-rs/harmony/src/modules/k8s/resource.rs new file mode 100644 index 0000000..aa1ce87 --- /dev/null +++ b/harmony-rs/harmony/src/modules/k8s/resource.rs @@ -0,0 +1,92 @@ +use async_trait::async_trait; +use k8s_openapi::NamespaceResourceScope; +use kube::Resource; +use serde::de::DeserializeOwned; + +use crate::{ + data::{Id, Version}, + interpret::{Interpret, InterpretError, InterpretName, InterpretStatus, Outcome}, + inventory::Inventory, + score::Score, + topology::HAClusterTopology, +}; + +#[derive(Debug)] +pub struct K8sResourceScore { + pub resource: Vec, +} + +impl K8sResourceScore { + pub fn single(resource: K) -> Self { + Self { + resource: vec![resource], + } + } +} + +impl< + K: Resource + + std::fmt::Debug + + Sync + + DeserializeOwned + + Default + + serde::Serialize + + Clone, + > Score for K8sResourceScore +where + ::DynamicType: Default, +{ + type InterpretType = K8sResourceInterpret; + + fn create_interpret(self) -> Self::InterpretType { + todo!() + } +} + +#[derive(Debug)] +pub struct K8sResourceInterpret { + pub score: K8sResourceScore, +} + +#[async_trait] +impl< + K: Resource + + Clone + + std::fmt::Debug + + DeserializeOwned + + serde::Serialize + + Default + + Sync, + > Interpret for K8sResourceInterpret +where + ::DynamicType: Default, +{ + async fn execute( + &self, + inventory: &Inventory, + topology: &HAClusterTopology, + ) -> Result { + topology + .oc_client() + .await + .expect("Environment should provide enough information to instanciate a client") + .apply_namespaced(&self.score.resource) + .await?; + + Ok(Outcome::success( + "Successfully applied resource".to_string(), + )) + } + fn get_name(&self) -> InterpretName { + todo!() + } + fn get_version(&self) -> Version { + todo!() + } + fn get_status(&self) -> InterpretStatus { + todo!() + } + fn get_children(&self) -> Vec { + todo!() + } +} diff --git a/harmony-rs/opnsense-config-xml/src/data/mod.rs b/harmony-rs/opnsense-config-xml/src/data/mod.rs index ccc1ba1..68cee6b 100644 --- a/harmony-rs/opnsense-config-xml/src/data/mod.rs +++ b/harmony-rs/opnsense-config-xml/src/data/mod.rs @@ -1,10 +1,10 @@ -mod opnsense; -mod interfaces; +mod caddy; mod dhcpd; mod haproxy; -mod caddy; +mod interfaces; +mod opnsense; pub use caddy::*; -pub use haproxy::*; -pub use opnsense::*; -pub use interfaces::*; pub use dhcpd::*; +pub use haproxy::*; +pub use interfaces::*; +pub use opnsense::*; diff --git a/harmony-rs/opnsense-config-xml/src/data/opnsense.rs b/harmony-rs/opnsense-config-xml/src/data/opnsense.rs index d992704..86d4b06 100644 --- a/harmony-rs/opnsense-config-xml/src/data/opnsense.rs +++ b/harmony-rs/opnsense-config-xml/src/data/opnsense.rs @@ -141,6 +141,7 @@ pub struct Rule { pub struct Source { pub any: Option, pub network: Option, + pub address: Option, } #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] @@ -164,7 +165,7 @@ pub struct Sysctl { #[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)] pub struct SysctlItem { - pub descr: String, + pub descr: MaybeString, pub tunable: String, pub value: String, } diff --git a/harmony-rs/opnsense-config-xml/src/lib.rs b/harmony-rs/opnsense-config-xml/src/lib.rs index f210ae5..4f28370 100644 --- a/harmony-rs/opnsense-config-xml/src/lib.rs +++ b/harmony-rs/opnsense-config-xml/src/lib.rs @@ -1,4 +1,4 @@ -mod xml_utils; mod data; +mod xml_utils; pub use data::*; pub use yaserde::MaybeString;