From 0dda9a84b8b2e7106b5eb6980bedcd80fbd636be Mon Sep 17 00:00:00 2001 From: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com> Date: Mon, 31 Mar 2025 12:41:29 +0300 Subject: [PATCH 01/11] feat: add DeploymentConditionBuilder --- .../src/status/condition/deployment.rs | 191 ++++++++++++++++++ .../src/status/condition/mod.rs | 1 + 2 files changed, 192 insertions(+) create mode 100644 crates/stackable-operator/src/status/condition/deployment.rs diff --git a/crates/stackable-operator/src/status/condition/deployment.rs b/crates/stackable-operator/src/status/condition/deployment.rs new file mode 100644 index 000000000..79e372a49 --- /dev/null +++ b/crates/stackable-operator/src/status/condition/deployment.rs @@ -0,0 +1,191 @@ +use std::cmp; + +use k8s_openapi::api::apps::v1::Deployment; +use kube::ResourceExt; + +use crate::status::condition::{ + ClusterCondition, ClusterConditionSet, ClusterConditionStatus, ClusterConditionType, + ConditionBuilder, +}; + +/// Default implementation to build [`ClusterCondition`]s for +/// `Deployment` resources. +/// +/// Currently only the `ClusterConditionType::Available` is implemented. This will be extended +/// to support all `ClusterConditionType`s in the future. +#[derive(Default)] +pub struct DeploymentConditionBuilder { + deployments: Vec, +} + +impl ConditionBuilder for DeploymentConditionBuilder { + fn build_conditions(&self) -> ClusterConditionSet { + vec![self.available()].into() + } +} + +impl DeploymentConditionBuilder { + pub fn add(&mut self, dplmt: Deployment) { + self.deployments.push(dplmt); + } + + fn available(&self) -> ClusterCondition { + let mut available = ClusterConditionStatus::True; + let mut unavailable_resources = vec![]; + for dplmt in &self.deployments { + let current_status = Self::deployment_available(dplmt); + + if current_status != ClusterConditionStatus::True { + unavailable_resources.push(dplmt.name_any()) + } + + available = cmp::max(available, current_status); + } + + // We need to sort here to make sure roles and role groups are not changing position + // due to the HashMap (random order) logic. + unavailable_resources.sort(); + + let message = match available { + ClusterConditionStatus::True => { + "All Deployments have the requested amount of ready replicas.".to_string() + } + ClusterConditionStatus::False => { + format!("Deployment {unavailable_resources:?} missing ready replicas.") + } + ClusterConditionStatus::Unknown => { + "Deployment status cannot be determined.".to_string() + } + }; + + ClusterCondition { + reason: None, + message: Some(message), + status: available, + type_: ClusterConditionType::Available, + last_transition_time: None, + last_update_time: None, + } + } + + /// Returns a condition "Available: True" if the number of requested replicas matches + /// the number of available replicas. In addition, there needs to be at least one replica + /// available. + fn deployment_available(dplmt: &Deployment) -> ClusterConditionStatus { + let requested_replicas = dplmt + .spec + .as_ref() + .and_then(|spec| spec.replicas) + .unwrap_or_default(); + let available_replicas = dplmt + .status + .as_ref() + .and_then(|status| status.available_replicas) + .unwrap_or_default(); + + if requested_replicas == available_replicas && requested_replicas != 0 { + ClusterConditionStatus::True + } else { + ClusterConditionStatus::False + } + } +} + +#[cfg(test)] +mod tests { + use k8s_openapi::api::apps::v1::{Deployment, DeploymentSpec, DeploymentStatus}; + + use crate::status::condition::{ + deployment::DeploymentConditionBuilder, ClusterCondition, ClusterConditionStatus, + ClusterConditionType, ConditionBuilder, + }; + + fn build_deployment(spec_replicas: i32, available_replicas: i32) -> Deployment { + Deployment { + spec: Some(DeploymentSpec { + replicas: Some(spec_replicas), + ..DeploymentSpec::default() + }), + status: Some(DeploymentStatus { + available_replicas: Some(available_replicas), + ..DeploymentStatus::default() + }), + ..Deployment::default() + } + } + + #[test] + fn available() { + let dplmt = build_deployment(3, 3); + + assert_eq!( + DeploymentConditionBuilder::deployment_available(&dplmt), + ClusterConditionStatus::True + ); + } + + #[test] + fn unavailable() { + let dplmt = build_deployment(3, 2); + + assert_eq!( + DeploymentConditionBuilder::deployment_available(&dplmt), + ClusterConditionStatus::False + ); + + let dplmt = build_deployment(3, 4); + + assert_eq!( + DeploymentConditionBuilder::deployment_available(&dplmt), + ClusterConditionStatus::False + ); + } + + #[test] + fn condition_available() { + let mut dplmt_condition_builder = DeploymentConditionBuilder::default(); + dplmt_condition_builder.add(build_deployment(3, 3)); + + let conditions = dplmt_condition_builder.build_conditions(); + + let got = conditions + .conditions + .get::(ClusterConditionType::Available.into()) + .cloned() + .unwrap() + .unwrap(); + + let expected = ClusterCondition { + type_: ClusterConditionType::Available, + status: ClusterConditionStatus::True, + ..ClusterCondition::default() + }; + + assert_eq!(got.type_, expected.type_); + assert_eq!(got.status, expected.status); + } + + #[test] + fn condition_unavailable() { + let mut dplmt_condition_builder = DeploymentConditionBuilder::default(); + dplmt_condition_builder.add(build_deployment(3, 2)); + + let conditions = dplmt_condition_builder.build_conditions(); + + let got = conditions + .conditions + .get::(ClusterConditionType::Available.into()) + .cloned() + .unwrap() + .unwrap(); + + let expected = ClusterCondition { + type_: ClusterConditionType::Available, + status: ClusterConditionStatus::False, + ..ClusterCondition::default() + }; + + assert_eq!(got.type_, expected.type_); + assert_eq!(got.status, expected.status); + } +} diff --git a/crates/stackable-operator/src/status/condition/mod.rs b/crates/stackable-operator/src/status/condition/mod.rs index c22b3028e..379bbf5f4 100644 --- a/crates/stackable-operator/src/status/condition/mod.rs +++ b/crates/stackable-operator/src/status/condition/mod.rs @@ -1,4 +1,5 @@ pub mod daemonset; +pub mod deployment; pub mod operations; pub mod statefulset; From 9157290f42b533774df228d69672ada0d00132fd Mon Sep 17 00:00:00 2001 From: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com> Date: Mon, 31 Mar 2025 12:48:41 +0300 Subject: [PATCH 02/11] update changelog --- crates/stackable-operator/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/stackable-operator/CHANGELOG.md b/crates/stackable-operator/CHANGELOG.md index d674fc2a7..61e3ce464 100644 --- a/crates/stackable-operator/CHANGELOG.md +++ b/crates/stackable-operator/CHANGELOG.md @@ -7,8 +7,10 @@ All notable changes to this project will be documented in this file. ### Added - Add Deployments to `ClusterResource`s ([#992]). +- Add `DeploymentConditionBuilder` ([#993]). [#992]: https://github.com/stackabletech/operator-rs/pull/992 +[#993]: https://github.com/stackabletech/operator-rs/pull/993 ## [0.87.5] - 2025-03-19 From 92dd7696f7f21af08796112ac74f5d9a40219a48 Mon Sep 17 00:00:00 2001 From: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com> Date: Mon, 31 Mar 2025 12:59:17 +0300 Subject: [PATCH 03/11] fix cargo doc --- crates/stackable-operator/src/cluster_resources.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/stackable-operator/src/cluster_resources.rs b/crates/stackable-operator/src/cluster_resources.rs index 03dc2d9da..112fae625 100644 --- a/crates/stackable-operator/src/cluster_resources.rs +++ b/crates/stackable-operator/src/cluster_resources.rs @@ -27,10 +27,7 @@ use strum::Display; use tracing::{debug, info, warn}; #[cfg(doc)] -use crate::k8s_openapi::api::{ - apps::v1::Deployment, - core::v1::{NodeSelector, Pod}, -}; +use crate::k8s_openapi::api::core::v1::{NodeSelector, Pod}; use crate::{ client::{Client, GetApi}, commons::{ From ba6e9d8983db851d8053e3f5ae60f4c100ad6723 Mon Sep 17 00:00:00 2001 From: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:51:43 +0200 Subject: [PATCH 04/11] Update crates/stackable-operator/src/status/condition/deployment.rs Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> --- .../stackable-operator/src/status/condition/deployment.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/stackable-operator/src/status/condition/deployment.rs b/crates/stackable-operator/src/status/condition/deployment.rs index 79e372a49..b2b67d10d 100644 --- a/crates/stackable-operator/src/status/condition/deployment.rs +++ b/crates/stackable-operator/src/status/condition/deployment.rs @@ -32,11 +32,11 @@ impl DeploymentConditionBuilder { fn available(&self) -> ClusterCondition { let mut available = ClusterConditionStatus::True; let mut unavailable_resources = vec![]; - for dplmt in &self.deployments { - let current_status = Self::deployment_available(dplmt); + for deployment in &self.deployments { + let current_status = Self::deployment_available(deployment); if current_status != ClusterConditionStatus::True { - unavailable_resources.push(dplmt.name_any()) + unavailable_resources.push(deployment.name_any()) } available = cmp::max(available, current_status); From 0391a330f671f24c1a779dedf81ae67a4e38eefa Mon Sep 17 00:00:00 2001 From: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:51:55 +0200 Subject: [PATCH 05/11] Update crates/stackable-operator/src/status/condition/deployment.rs Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> --- .../stackable-operator/src/status/condition/deployment.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/stackable-operator/src/status/condition/deployment.rs b/crates/stackable-operator/src/status/condition/deployment.rs index b2b67d10d..b96653cd8 100644 --- a/crates/stackable-operator/src/status/condition/deployment.rs +++ b/crates/stackable-operator/src/status/condition/deployment.rs @@ -143,10 +143,10 @@ mod tests { #[test] fn condition_available() { - let mut dplmt_condition_builder = DeploymentConditionBuilder::default(); - dplmt_condition_builder.add(build_deployment(3, 3)); + let mut deployment_condition_builder = DeploymentConditionBuilder::default(); + deployment_condition_builder.add(build_deployment(3, 3)); - let conditions = dplmt_condition_builder.build_conditions(); + let conditions = deployment_condition_builder.build_conditions(); let got = conditions .conditions From c133d9d196eaa94c5578c7f4d8819cc4447ac04a Mon Sep 17 00:00:00 2001 From: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:52:04 +0200 Subject: [PATCH 06/11] Update crates/stackable-operator/src/status/condition/deployment.rs Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> --- .../stackable-operator/src/status/condition/deployment.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/stackable-operator/src/status/condition/deployment.rs b/crates/stackable-operator/src/status/condition/deployment.rs index b96653cd8..c37cd1725 100644 --- a/crates/stackable-operator/src/status/condition/deployment.rs +++ b/crates/stackable-operator/src/status/condition/deployment.rs @@ -167,10 +167,10 @@ mod tests { #[test] fn condition_unavailable() { - let mut dplmt_condition_builder = DeploymentConditionBuilder::default(); - dplmt_condition_builder.add(build_deployment(3, 2)); + let mut deployment_condition_builder = DeploymentConditionBuilder::default(); + deployment_condition_builder.add(build_deployment(3, 2)); - let conditions = dplmt_condition_builder.build_conditions(); + let conditions = deployment_condition_builder.build_conditions(); let got = conditions .conditions From 50ccbda20f4116d0ca3ca6f44b6fccb15894cee8 Mon Sep 17 00:00:00 2001 From: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:52:19 +0200 Subject: [PATCH 07/11] Update crates/stackable-operator/src/status/condition/deployment.rs Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> --- .../stackable-operator/src/status/condition/deployment.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/stackable-operator/src/status/condition/deployment.rs b/crates/stackable-operator/src/status/condition/deployment.rs index c37cd1725..bd379b6a5 100644 --- a/crates/stackable-operator/src/status/condition/deployment.rs +++ b/crates/stackable-operator/src/status/condition/deployment.rs @@ -126,17 +126,17 @@ mod tests { #[test] fn unavailable() { - let dplmt = build_deployment(3, 2); + let deployment = build_deployment(3, 2); assert_eq!( - DeploymentConditionBuilder::deployment_available(&dplmt), + DeploymentConditionBuilder::deployment_available(&deployment), ClusterConditionStatus::False ); let dplmt = build_deployment(3, 4); assert_eq!( - DeploymentConditionBuilder::deployment_available(&dplmt), + DeploymentConditionBuilder::deployment_available(&deployment), ClusterConditionStatus::False ); } From fd317ca7c739c69f9ac94f55c66f76abc3d1a78c Mon Sep 17 00:00:00 2001 From: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:52:30 +0200 Subject: [PATCH 08/11] Update crates/stackable-operator/src/status/condition/deployment.rs Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> --- crates/stackable-operator/src/status/condition/deployment.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/stackable-operator/src/status/condition/deployment.rs b/crates/stackable-operator/src/status/condition/deployment.rs index bd379b6a5..59570cae7 100644 --- a/crates/stackable-operator/src/status/condition/deployment.rs +++ b/crates/stackable-operator/src/status/condition/deployment.rs @@ -116,10 +116,10 @@ mod tests { #[test] fn available() { - let dplmt = build_deployment(3, 3); + let deployment = build_deployment(3, 3); assert_eq!( - DeploymentConditionBuilder::deployment_available(&dplmt), + DeploymentConditionBuilder::deployment_available(&deployment), ClusterConditionStatus::True ); } From 6163e0d32a026162db700bb001a576ef561825a9 Mon Sep 17 00:00:00 2001 From: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com> Date: Tue, 1 Apr 2025 10:59:10 +0200 Subject: [PATCH 09/11] rename var --- crates/stackable-operator/src/status/condition/deployment.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/stackable-operator/src/status/condition/deployment.rs b/crates/stackable-operator/src/status/condition/deployment.rs index 59570cae7..d6a0a19bf 100644 --- a/crates/stackable-operator/src/status/condition/deployment.rs +++ b/crates/stackable-operator/src/status/condition/deployment.rs @@ -133,7 +133,7 @@ mod tests { ClusterConditionStatus::False ); - let dplmt = build_deployment(3, 4); + let deployment = build_deployment(3, 4); assert_eq!( DeploymentConditionBuilder::deployment_available(&deployment), From 65b04be56ce9ac2b12b837b38d70e84183965ae5 Mon Sep 17 00:00:00 2001 From: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com> Date: Tue, 1 Apr 2025 11:03:19 +0200 Subject: [PATCH 10/11] Update crates/stackable-operator/src/status/condition/deployment.rs Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> --- crates/stackable-operator/src/status/condition/deployment.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/stackable-operator/src/status/condition/deployment.rs b/crates/stackable-operator/src/status/condition/deployment.rs index d6a0a19bf..f574be1c0 100644 --- a/crates/stackable-operator/src/status/condition/deployment.rs +++ b/crates/stackable-operator/src/status/condition/deployment.rs @@ -25,8 +25,8 @@ impl ConditionBuilder for DeploymentConditionBuilder { } impl DeploymentConditionBuilder { - pub fn add(&mut self, dplmt: Deployment) { - self.deployments.push(dplmt); + pub fn add(&mut self, deployment: Deployment) { + self.deployments.push(deployment); } fn available(&self) -> ClusterCondition { From d3515a8d0b41c6f195aa3048985190d3e8125216 Mon Sep 17 00:00:00 2001 From: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com> Date: Tue, 1 Apr 2025 11:03:31 +0200 Subject: [PATCH 11/11] Update crates/stackable-operator/src/status/condition/deployment.rs Co-authored-by: Nick <10092581+NickLarsenNZ@users.noreply.github.com> --- .../stackable-operator/src/status/condition/deployment.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/stackable-operator/src/status/condition/deployment.rs b/crates/stackable-operator/src/status/condition/deployment.rs index f574be1c0..c70180a93 100644 --- a/crates/stackable-operator/src/status/condition/deployment.rs +++ b/crates/stackable-operator/src/status/condition/deployment.rs @@ -71,13 +71,13 @@ impl DeploymentConditionBuilder { /// Returns a condition "Available: True" if the number of requested replicas matches /// the number of available replicas. In addition, there needs to be at least one replica /// available. - fn deployment_available(dplmt: &Deployment) -> ClusterConditionStatus { - let requested_replicas = dplmt + fn deployment_available(deployment: &Deployment) -> ClusterConditionStatus { + let requested_replicas = deployment .spec .as_ref() .and_then(|spec| spec.replicas) .unwrap_or_default(); - let available_replicas = dplmt + let available_replicas = deployment .status .as_ref() .and_then(|status| status.available_replicas)