diff --git a/Cargo.lock b/Cargo.lock index a4e31ee..4cf88dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1262,6 +1262,22 @@ dependencies = [ "url", ] +[[package]] +name = "brocade-switch-oricom-configuration" +version = "0.1.0" +dependencies = [ + "async-trait", + "brocade", + "env_logger", + "harmony", + "harmony_cli", + "harmony_macros", + "harmony_types", + "log", + "serde", + "tokio", +] + [[package]] name = "brotli" version = "8.0.2" @@ -2889,6 +2905,15 @@ dependencies = [ "url", ] +[[package]] +name = "example-okd-system-reserved" +version = "0.1.0" +dependencies = [ + "harmony", + "harmony_cli", + "tokio", +] + [[package]] name = "example-openbao" version = "0.1.0" diff --git a/examples/okd_system_reserved/Cargo.toml b/examples/okd_system_reserved/Cargo.toml new file mode 100644 index 0000000..9384d10 --- /dev/null +++ b/examples/okd_system_reserved/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "example-okd-system-reserved" +edition = "2024" +version.workspace = true +readme.workspace = true +license.workspace = true +publish = false + +[dependencies] +harmony = { path = "../../harmony" } +harmony_cli = { path = "../../harmony_cli" } +tokio = { workspace = true } diff --git a/examples/okd_system_reserved/env.sh b/examples/okd_system_reserved/env.sh new file mode 100644 index 0000000..8b1c9de --- /dev/null +++ b/examples/okd_system_reserved/env.sh @@ -0,0 +1,5 @@ +export HARMONY_SECRET_NAMESPACE=okd-system-reserved-example +export HARMONY_SECRET_STORE=file +export HARMONY_DATABASE_URL=sqlite://harmony_okd_system_reserved.sqlite +export HARMONY_USE_LOCAL_K3D=false +export RUST_LOG=harmony=debug diff --git a/examples/okd_system_reserved/src/main.rs b/examples/okd_system_reserved/src/main.rs new file mode 100644 index 0000000..ce88718 --- /dev/null +++ b/examples/okd_system_reserved/src/main.rs @@ -0,0 +1,25 @@ +use harmony::{ + inventory::Inventory, + modules::okd::system_reserved_score::{MachineConfigPool, SystemReservedScore}, + topology::K8sAnywhereTopology, +}; + +#[tokio::main] +async fn main() { + harmony_cli::cli_logger::init(); + + let master_score = SystemReservedScore { + pool: MachineConfigPool::Master, + memory: "0.5Gi".to_string(), + ..Default::default() + }; + + harmony_cli::run( + Inventory::autoload(), + K8sAnywhereTopology::from_env(), + vec![Box::new(master_score)], + None, + ) + .await + .unwrap(); +} diff --git a/harmony/src/modules/okd/crd/kubelet_config.rs b/harmony/src/modules/okd/crd/kubelet_config.rs new file mode 100644 index 0000000..a9fcbdd --- /dev/null +++ b/harmony/src/modules/okd/crd/kubelet_config.rs @@ -0,0 +1,47 @@ +use k8s_openapi::apimachinery::pkg::apis::meta::v1::LabelSelector; +use kube::{CustomResource, api::ObjectMeta}; +use serde::{Deserialize, Serialize}; + +#[derive(CustomResource, Deserialize, Serialize, Clone, Debug)] +#[kube( + group = "machineconfiguration.openshift.io", + version = "v1", + kind = "KubeletConfig", + plural = "kubeletconfigs", + namespaced = false, + schema = "disabled" +)] +#[serde(rename_all = "camelCase")] +pub struct KubeletConfigSpec { + pub machine_config_pool_selector: LabelSelector, + pub kubelet_config: KubeletConfigInner, +} + +impl Default for KubeletConfig { + fn default() -> Self { + Self { + metadata: ObjectMeta::default(), + spec: KubeletConfigSpec { + machine_config_pool_selector: LabelSelector::default(), + kubelet_config: KubeletConfigInner::default(), + }, + } + } +} + +#[derive(Deserialize, Serialize, Clone, Debug, Default)] +#[serde(rename_all = "camelCase")] +pub struct KubeletConfigInner { + #[serde(skip_serializing_if = "Option::is_none")] + pub system_reserved: Option, +} + +#[derive(Deserialize, Serialize, Clone, Debug, Default)] +pub struct SystemReserved { + #[serde(skip_serializing_if = "Option::is_none")] + pub memory: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub cpu: Option, + #[serde(rename = "ephemeral-storage", skip_serializing_if = "Option::is_none")] + pub ephemeral_storage: Option, +} diff --git a/harmony/src/modules/okd/crd/mod.rs b/harmony/src/modules/okd/crd/mod.rs index ba1d1a4..dae9c51 100644 --- a/harmony/src/modules/okd/crd/mod.rs +++ b/harmony/src/modules/okd/crd/mod.rs @@ -1,3 +1,4 @@ pub mod ingresses_config; +pub mod kubelet_config; pub mod nmstate; pub mod route; diff --git a/harmony/src/modules/okd/mod.rs b/harmony/src/modules/okd/mod.rs index 5d6d18d..e1719e9 100644 --- a/harmony/src/modules/okd/mod.rs +++ b/harmony/src/modules/okd/mod.rs @@ -26,3 +26,4 @@ pub use bootstrap_06_installation_report::*; pub use bootstrap_persist_network_bond::*; pub mod crd; pub mod host_network; +pub mod system_reserved_score; diff --git a/harmony/src/modules/okd/system_reserved_score.rs b/harmony/src/modules/okd/system_reserved_score.rs new file mode 100644 index 0000000..4ec1d83 --- /dev/null +++ b/harmony/src/modules/okd/system_reserved_score.rs @@ -0,0 +1,98 @@ +use std::collections::BTreeMap; + +use k8s_openapi::apimachinery::pkg::apis::meta::v1::LabelSelector; +use kube::api::ObjectMeta; +use serde::Serialize; + +use crate::{ + interpret::Interpret, + modules::{ + k8s::resource::K8sResourceScore, + okd::crd::kubelet_config::{ + KubeletConfig, KubeletConfigInner, KubeletConfigSpec, SystemReserved, + }, + }, + score::Score, + topology::{K8sclient, Topology}, +}; + +#[derive(Debug, Clone, Copy, Serialize)] +pub enum MachineConfigPool { + Master, + Worker, +} + +impl MachineConfigPool { + fn label_key(&self) -> &'static str { + match self { + Self::Master => "pools.operator.machineconfiguration.openshift.io/master", + Self::Worker => "pools.operator.machineconfiguration.openshift.io/worker", + } + } + + fn default_resource_name(&self) -> &'static str { + match self { + Self::Master => "master-system-reserved", + Self::Worker => "worker-system-reserved", + } + } +} + +#[derive(Debug, Clone, Serialize)] +pub struct SystemReservedScore { + pub pool: MachineConfigPool, + pub memory: String, + pub cpu: Option, + pub ephemeral_storage: Option, + pub resource_name: Option, +} + +impl Default for SystemReservedScore { + fn default() -> Self { + Self { + pool: MachineConfigPool::Master, + memory: "3.5Gi".to_string(), + cpu: None, + ephemeral_storage: None, + resource_name: None, + } + } +} + +impl Score for SystemReservedScore { + fn name(&self) -> String { + "SystemReservedScore".to_string() + } + + fn create_interpret(&self) -> Box> { + let mut match_labels = BTreeMap::new(); + match_labels.insert(self.pool.label_key().to_string(), String::new()); + + let name = self + .resource_name + .clone() + .unwrap_or_else(|| self.pool.default_resource_name().to_string()); + + let kc = KubeletConfig { + metadata: ObjectMeta { + name: Some(name), + ..Default::default() + }, + spec: KubeletConfigSpec { + machine_config_pool_selector: LabelSelector { + match_labels: Some(match_labels), + match_expressions: None, + }, + kubelet_config: KubeletConfigInner { + system_reserved: Some(SystemReserved { + memory: Some(self.memory.clone()), + cpu: self.cpu.clone(), + ephemeral_storage: self.ephemeral_storage.clone(), + }), + }, + }, + }; + + K8sResourceScore::single(kc, None).create_interpret() + } +}