Skip to content

Commit b064f7a

Browse files
maltesanderrazvan
andcommitted
Add status field support (#571)
## Description Fixes stackabletech/issues#362 Co-authored-by: Razvan-Daniel Mihai <84674+razvan@users.noreply.github.com>
1 parent 8e7e522 commit b064f7a

File tree

7 files changed

+1018
-0
lines changed

7 files changed

+1018
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ All notable changes to this project will be documented in this file.
66

77
### Added
88

9+
- status::condition module to compute the cluster resource status ([#571]).
910
- Helper function to build RBAC resources ([#572]).
1011
- Add `ClusterResourceApplyStrategy` to `ClusterResource` ([#573]).
1112
- Add `ClusterOperation` common struct with `reconcilation_paused` and `stopped` flags ([#573]).
1213

14+
[#571]: https://github.com/stackabletech/operator-rs/pull/571
1315
[#572]: https://github.com/stackabletech/operator-rs/pull/572
1416
[#573]: https://github.com/stackabletech/operator-rs/pull/573
1517

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub mod pod_utils;
1717
pub mod product_config_utils;
1818
pub mod product_logging;
1919
pub mod role_utils;
20+
pub mod status;
2021
pub mod utils;
2122
pub mod validation;
2223
pub mod yaml;

src/status/condition/daemonset.rs

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
use crate::status::condition::{
2+
ClusterCondition, ClusterConditionSet, ClusterConditionStatus, ClusterConditionType,
3+
ConditionBuilder,
4+
};
5+
6+
use k8s_openapi::api::apps::v1::DaemonSet;
7+
use kube::ResourceExt;
8+
use std::cmp;
9+
10+
/// Default implementation to build [`ClusterCondition`]s for
11+
/// `DaemonSet` resources.
12+
///
13+
/// Currently only the `ClusterConditionType::Available` is implemented. This will be extended
14+
/// to support all `ClusterConditionType`s in the future.
15+
#[derive(Default)]
16+
pub struct DaemonSetConditionBuilder {
17+
daemon_sets: Vec<DaemonSet>,
18+
}
19+
20+
impl ConditionBuilder for DaemonSetConditionBuilder {
21+
fn build_conditions(&self) -> ClusterConditionSet {
22+
vec![self.available()].into()
23+
}
24+
}
25+
26+
impl DaemonSetConditionBuilder {
27+
pub fn add(&mut self, ds: DaemonSet) {
28+
self.daemon_sets.push(ds);
29+
}
30+
31+
fn available(&self) -> ClusterCondition {
32+
let mut available = ClusterConditionStatus::True;
33+
let mut unavailable_resources = vec![];
34+
35+
for ds in &self.daemon_sets {
36+
let current_status = Self::daemon_set_available(ds);
37+
38+
if current_status != ClusterConditionStatus::True {
39+
unavailable_resources.push(ds.name_any())
40+
}
41+
42+
available = cmp::max(available, current_status);
43+
}
44+
45+
// We need to sort here to make sure roles and role groups are not changing position
46+
// due to the HashMap (random order) logic.
47+
unavailable_resources.sort();
48+
49+
let message = match available {
50+
ClusterConditionStatus::True => {
51+
"All DaemonSet have the requested amount of ready replicas.".to_string()
52+
}
53+
ClusterConditionStatus::False => {
54+
format!("DaemonSet {unavailable_resources:?} missing ready replicas.")
55+
}
56+
ClusterConditionStatus::Unknown => "DaemonSet status cannot be determined.".to_string(),
57+
};
58+
59+
ClusterCondition {
60+
reason: None,
61+
message: Some(message),
62+
status: available,
63+
type_: ClusterConditionType::Available,
64+
last_transition_time: None,
65+
last_update_time: None,
66+
}
67+
}
68+
69+
/// Returns a condition "Available: True" if the number of ready Pods is greater than zero.
70+
/// This is an heuristic that doesn't take into consideration if *all* eligible nodes have
71+
/// running Pods.
72+
/// Other fields of the daemon set status have been considered and discarded for being even less
73+
/// reliable/informative.
74+
fn daemon_set_available(ds: &DaemonSet) -> ClusterConditionStatus {
75+
let number_ready = ds
76+
.status
77+
.as_ref()
78+
.map(|status| status.number_ready)
79+
.unwrap_or_default();
80+
81+
if number_ready > 0 {
82+
ClusterConditionStatus::True
83+
} else {
84+
ClusterConditionStatus::False
85+
}
86+
}
87+
}
88+
89+
#[cfg(test)]
90+
mod test {
91+
use crate::status::condition::daemonset::DaemonSetConditionBuilder;
92+
use crate::status::condition::{
93+
ClusterCondition, ClusterConditionStatus, ClusterConditionType, ConditionBuilder,
94+
};
95+
use k8s_openapi::api::apps::v1::{DaemonSet, DaemonSetStatus};
96+
97+
fn build_ds(number_ready: i32) -> DaemonSet {
98+
DaemonSet {
99+
status: Some(DaemonSetStatus {
100+
number_ready,
101+
..DaemonSetStatus::default()
102+
}),
103+
..DaemonSet::default()
104+
}
105+
}
106+
107+
#[test]
108+
fn test_daemon_set_available_true() {
109+
let ds = build_ds(1);
110+
111+
assert_eq!(
112+
DaemonSetConditionBuilder::daemon_set_available(&ds),
113+
ClusterConditionStatus::True
114+
);
115+
}
116+
117+
#[test]
118+
fn test_daemon_set_available_false() {
119+
let ds = build_ds(0);
120+
assert_eq!(
121+
DaemonSetConditionBuilder::daemon_set_available(&ds),
122+
ClusterConditionStatus::False
123+
);
124+
}
125+
126+
#[test]
127+
fn test_daemon_set_available_condition_true() {
128+
let mut ds_condition_builder = DaemonSetConditionBuilder::default();
129+
ds_condition_builder.add(build_ds(1));
130+
131+
let conditions = ds_condition_builder.build_conditions();
132+
133+
let got = conditions
134+
.conditions
135+
.get::<usize>(ClusterConditionType::Available.into())
136+
.cloned()
137+
.unwrap()
138+
.unwrap();
139+
140+
let expected = ClusterCondition {
141+
type_: ClusterConditionType::Available,
142+
status: ClusterConditionStatus::True,
143+
..ClusterCondition::default()
144+
};
145+
146+
assert_eq!(got.type_, expected.type_);
147+
assert_eq!(got.status, expected.status);
148+
}
149+
150+
#[test]
151+
fn test_daemon_set_available_condition_false() {
152+
let mut ds_condition_builder = DaemonSetConditionBuilder::default();
153+
ds_condition_builder.add(build_ds(0));
154+
155+
let conditions = ds_condition_builder.build_conditions();
156+
157+
let got = conditions
158+
.conditions
159+
.get::<usize>(ClusterConditionType::Available.into())
160+
.cloned()
161+
.unwrap()
162+
.unwrap();
163+
164+
let expected = ClusterCondition {
165+
type_: ClusterConditionType::Available,
166+
status: ClusterConditionStatus::False,
167+
..ClusterCondition::default()
168+
};
169+
170+
assert_eq!(got.type_, expected.type_);
171+
assert_eq!(got.status, expected.status);
172+
}
173+
}

0 commit comments

Comments
 (0)