feat(k8s): add Kubernetes deployment resource handling
Introduce new modules to handle Kubernetes resources specifically focusing on Deployment resources. Added `K8sResource` and `K8sDeployment` structs along with necessary traits implementations for interpretation and execution in the inventory system. Also, fixed module reordering issues in opnsense-config-xml and corrected some fields types within its data structures.
This commit is contained in:
parent
caec71f06d
commit
d6c8650d52
4
harmony-rs/Cargo.lock
generated
4
harmony-rs/Cargo.lock
generated
@ -931,6 +931,9 @@ dependencies = [
|
|||||||
"env_logger",
|
"env_logger",
|
||||||
"harmony_macros",
|
"harmony_macros",
|
||||||
"harmony_types",
|
"harmony_types",
|
||||||
|
"http 1.2.0",
|
||||||
|
"k8s-openapi",
|
||||||
|
"kube",
|
||||||
"libredfish",
|
"libredfish",
|
||||||
"log",
|
"log",
|
||||||
"opnsense-config",
|
"opnsense-config",
|
||||||
@ -941,6 +944,7 @@ dependencies = [
|
|||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_yaml",
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
|
@ -26,6 +26,10 @@ russh = "0.45.0"
|
|||||||
russh-keys = "0.45.0"
|
russh-keys = "0.45.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
url = "2.5.4"
|
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]
|
[workspace.dependencies.uuid]
|
||||||
version = "1.11.0"
|
version = "1.11.0"
|
||||||
|
@ -23,3 +23,7 @@ harmony_macros = { path = "../harmony_macros" }
|
|||||||
harmony_types = { path = "../harmony_types" }
|
harmony_types = { path = "../harmony_types" }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
url = { workspace = true }
|
url = { workspace = true }
|
||||||
|
kube = { workspace = true }
|
||||||
|
k8s-openapi = { workspace = true }
|
||||||
|
serde_yaml = { workspace = true }
|
||||||
|
http = { workspace = true }
|
||||||
|
@ -3,7 +3,6 @@ use std::sync::Arc;
|
|||||||
use derive_new::new;
|
use derive_new::new;
|
||||||
use harmony_types::net::MacAddress;
|
use harmony_types::net::MacAddress;
|
||||||
|
|
||||||
|
|
||||||
pub type HostGroup = Vec<PhysicalHost>;
|
pub type HostGroup = Vec<PhysicalHost>;
|
||||||
pub type SwitchGroup = Vec<Switch>;
|
pub type SwitchGroup = Vec<Switch>;
|
||||||
pub type FirewallGroup = Vec<PhysicalHost>;
|
pub type FirewallGroup = Vec<PhysicalHost>;
|
||||||
@ -29,7 +28,11 @@ impl PhysicalHost {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn cluster_mac(&self) -> MacAddress {
|
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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,3 +108,12 @@ impl From<ExecutorError> for InterpretError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<kube::Error> for InterpretError{
|
||||||
|
fn from(value: kube::Error) -> Self {
|
||||||
|
Self {
|
||||||
|
msg: format!("InterpretError : {value}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
mod host_binding;
|
mod host_binding;
|
||||||
mod http;
|
mod http;
|
||||||
mod load_balancer;
|
mod load_balancer;
|
||||||
|
pub mod openshift;
|
||||||
mod router;
|
mod router;
|
||||||
mod tftp;
|
mod tftp;
|
||||||
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::*;
|
||||||
@ -29,6 +31,12 @@ pub struct HAClusterTopology {
|
|||||||
pub switch: 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;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
55
harmony-rs/harmony/src/domain/topology/openshift.rs
Normal file
55
harmony-rs/harmony/src/domain/topology/openshift.rs
Normal file
@ -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<Self, Error> {
|
||||||
|
Ok(Self {
|
||||||
|
client: Client::try_default().await?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn apply_all<
|
||||||
|
K: Resource<Scope = NamespaceResourceScope>
|
||||||
|
+ std::fmt::Debug
|
||||||
|
+ Sync
|
||||||
|
+ DeserializeOwned
|
||||||
|
+ Default
|
||||||
|
+ serde::Serialize
|
||||||
|
+ Clone,
|
||||||
|
>(
|
||||||
|
&self,
|
||||||
|
resource: &Vec<K>,
|
||||||
|
) -> Result<Vec<K>, kube::Error>
|
||||||
|
where
|
||||||
|
<K as kube::Resource>::DynamicType: Default,
|
||||||
|
{
|
||||||
|
let mut result = vec![];
|
||||||
|
for r in resource.iter() {
|
||||||
|
let api: Api<K> = Api::all(self.client.clone());
|
||||||
|
result.push(api.create(&PostParams::default(), &r).await?);
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn apply_namespaced<K>(&self, resource: &Vec<K>) -> Result<K, Error>
|
||||||
|
where
|
||||||
|
K: Resource<Scope = NamespaceResourceScope>
|
||||||
|
+ Clone
|
||||||
|
+ std::fmt::Debug
|
||||||
|
+ DeserializeOwned
|
||||||
|
+ serde::Serialize
|
||||||
|
+ Default,
|
||||||
|
<K as kube::Resource>::DynamicType: Default,
|
||||||
|
{
|
||||||
|
for r in resource.iter() {
|
||||||
|
let api: Api<K> = Api::default_namespaced(self.client.clone());
|
||||||
|
api.create(&PostParams::default(), &r).await?;
|
||||||
|
}
|
||||||
|
todo!("")
|
||||||
|
}
|
||||||
|
}
|
@ -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<Outcome, InterpretError> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn get_name(&self) -> InterpretName {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn get_version(&self) -> Version {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn get_status(&self) -> InterpretStatus {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
fn get_children(&self) -> Vec<Id> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
53
harmony-rs/harmony/src/modules/k8s/deployment.rs
Normal file
53
harmony-rs/harmony/src/modules/k8s/deployment.rs
Normal file
@ -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<Deployment>;
|
||||||
|
|
||||||
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
|
||||||
pub mod Resource;
|
pub mod resource;
|
||||||
|
pub mod deployment;
|
||||||
|
|
||||||
|
92
harmony-rs/harmony/src/modules/k8s/resource.rs
Normal file
92
harmony-rs/harmony/src/modules/k8s/resource.rs
Normal file
@ -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<K: Resource + std::fmt::Debug> {
|
||||||
|
pub resource: Vec<K>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Resource + std::fmt::Debug> K8sResourceScore<K> {
|
||||||
|
pub fn single(resource: K) -> Self {
|
||||||
|
Self {
|
||||||
|
resource: vec![resource],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<
|
||||||
|
K: Resource<Scope = NamespaceResourceScope>
|
||||||
|
+ std::fmt::Debug
|
||||||
|
+ Sync
|
||||||
|
+ DeserializeOwned
|
||||||
|
+ Default
|
||||||
|
+ serde::Serialize
|
||||||
|
+ Clone,
|
||||||
|
> Score for K8sResourceScore<K>
|
||||||
|
where
|
||||||
|
<K as kube::Resource>::DynamicType: Default,
|
||||||
|
{
|
||||||
|
type InterpretType = K8sResourceInterpret<K>;
|
||||||
|
|
||||||
|
fn create_interpret(self) -> Self::InterpretType {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct K8sResourceInterpret<K: Resource + std::fmt::Debug + Sync> {
|
||||||
|
pub score: K8sResourceScore<K>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<
|
||||||
|
K: Resource<Scope = NamespaceResourceScope>
|
||||||
|
+ Clone
|
||||||
|
+ std::fmt::Debug
|
||||||
|
+ DeserializeOwned
|
||||||
|
+ serde::Serialize
|
||||||
|
+ Default
|
||||||
|
+ Sync,
|
||||||
|
> Interpret for K8sResourceInterpret<K>
|
||||||
|
where
|
||||||
|
<K as kube::Resource>::DynamicType: Default,
|
||||||
|
{
|
||||||
|
async fn execute(
|
||||||
|
&self,
|
||||||
|
inventory: &Inventory,
|
||||||
|
topology: &HAClusterTopology,
|
||||||
|
) -> Result<Outcome, InterpretError> {
|
||||||
|
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<Id> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
@ -1,10 +1,10 @@
|
|||||||
mod opnsense;
|
mod caddy;
|
||||||
mod interfaces;
|
|
||||||
mod dhcpd;
|
mod dhcpd;
|
||||||
mod haproxy;
|
mod haproxy;
|
||||||
mod caddy;
|
mod interfaces;
|
||||||
|
mod opnsense;
|
||||||
pub use caddy::*;
|
pub use caddy::*;
|
||||||
pub use haproxy::*;
|
|
||||||
pub use opnsense::*;
|
|
||||||
pub use interfaces::*;
|
|
||||||
pub use dhcpd::*;
|
pub use dhcpd::*;
|
||||||
|
pub use haproxy::*;
|
||||||
|
pub use interfaces::*;
|
||||||
|
pub use opnsense::*;
|
||||||
|
@ -141,6 +141,7 @@ pub struct Rule {
|
|||||||
pub struct Source {
|
pub struct Source {
|
||||||
pub any: Option<u8>,
|
pub any: Option<u8>,
|
||||||
pub network: Option<MaybeString>,
|
pub network: Option<MaybeString>,
|
||||||
|
pub address: Option<MaybeString>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
|
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
|
||||||
@ -164,7 +165,7 @@ pub struct Sysctl {
|
|||||||
|
|
||||||
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
|
#[derive(Default, PartialEq, Debug, YaSerialize, YaDeserialize)]
|
||||||
pub struct SysctlItem {
|
pub struct SysctlItem {
|
||||||
pub descr: String,
|
pub descr: MaybeString,
|
||||||
pub tunable: String,
|
pub tunable: String,
|
||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
mod xml_utils;
|
|
||||||
mod data;
|
mod data;
|
||||||
|
mod xml_utils;
|
||||||
pub use data::*;
|
pub use data::*;
|
||||||
pub use yaserde::MaybeString;
|
pub use yaserde::MaybeString;
|
||||||
|
Loading…
Reference in New Issue
Block a user