Skip to content

Commit 08b25fa

Browse files
committed
WIP
1 parent 113c4ee commit 08b25fa

File tree

2 files changed

+260
-0
lines changed

2 files changed

+260
-0
lines changed

crates/stackable-operator/src/builder/pod/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use crate::{
2929
};
3030

3131
pub mod container;
32+
pub mod probe;
3233
pub mod resources;
3334
pub mod security;
3435
pub mod volume;
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
use k8s_openapi::{
2+
api::core::v1::{ExecAction, GRPCAction, HTTPGetAction, Probe, TCPSocketAction},
3+
apimachinery::pkg::util::intstr::IntOrString,
4+
};
5+
6+
use crate::time::Duration;
7+
8+
#[derive(Debug)]
9+
pub struct ProbeBuilder<Action, Period> {
10+
action: Action,
11+
period: Period,
12+
13+
success_threshold: i32,
14+
failure_threshold: i32,
15+
timeout: Duration,
16+
initial_delay: Duration,
17+
termination_grace_period: Duration,
18+
}
19+
20+
impl Default for ProbeBuilder<(), ()> {
21+
fn default() -> Self {
22+
Self {
23+
action: (),
24+
period: (),
25+
// The following values match the Kubernetes default
26+
success_threshold: 1,
27+
failure_threshold: 1,
28+
timeout: Duration::from_secs(1),
29+
initial_delay: Duration::from_secs(0),
30+
termination_grace_period: Duration::from_secs(0),
31+
}
32+
}
33+
}
34+
35+
pub enum ProbeAction {
36+
Exec(ExecAction),
37+
Grpc(GRPCAction),
38+
HttpGet(HTTPGetAction),
39+
TcpSocket(TCPSocketAction),
40+
}
41+
42+
impl<Period> ProbeBuilder<(), Period> {
43+
/// This probe action executes the specified command
44+
pub fn with_exec_action_helper(
45+
self,
46+
command: impl IntoIterator<Item = impl Into<String>>,
47+
) -> ProbeBuilder<ProbeAction, Period> {
48+
self.with_exec_action(ExecAction {
49+
command: Some(command.into_iter().map(Into::into).collect()),
50+
})
51+
}
52+
53+
/// There is a convenience helper: [`Self::with_exec_action_helper`].
54+
pub fn with_exec_action(self, exec_action: ExecAction) -> ProbeBuilder<ProbeAction, Period> {
55+
self.with_action(ProbeAction::Exec(exec_action))
56+
}
57+
58+
pub fn with_grpc_action(self, grpc_action: GRPCAction) -> ProbeBuilder<ProbeAction, Period> {
59+
self.with_action(ProbeAction::Grpc(grpc_action))
60+
}
61+
62+
/// This probe action does an HTTP GET request to the specified port. Optionally, you can
63+
/// configure the path, otherwise the Kubernetes default is used.
64+
pub fn with_http_get_action_helper(
65+
self,
66+
port: u16,
67+
path: Option<impl Into<String>>,
68+
) -> ProbeBuilder<ProbeAction, Period> {
69+
self.with_http_get_action(HTTPGetAction {
70+
path: path.map(Into::into),
71+
port: IntOrString::Int(port.into()),
72+
..Default::default()
73+
})
74+
}
75+
76+
/// There is a convenience helper: [`Self::with_http_get_action_helper`].
77+
pub fn with_http_get_action(
78+
self,
79+
http_get_action: HTTPGetAction,
80+
) -> ProbeBuilder<ProbeAction, Period> {
81+
self.with_action(ProbeAction::HttpGet(http_get_action))
82+
}
83+
84+
pub fn with_tcp_socket_action(
85+
self,
86+
tcp_socket_action: TCPSocketAction,
87+
) -> ProbeBuilder<ProbeAction, Period> {
88+
self.with_action(ProbeAction::TcpSocket(tcp_socket_action))
89+
}
90+
91+
/// Action-specific functions (e.g. [`Self::with_exec_action`] or [`Self::with_http_get_action`])
92+
/// are recommended instead.
93+
pub fn with_action(self, action: ProbeAction) -> ProbeBuilder<ProbeAction, Period> {
94+
let Self {
95+
action: (),
96+
period,
97+
success_threshold,
98+
failure_threshold,
99+
timeout,
100+
initial_delay,
101+
termination_grace_period,
102+
} = self;
103+
104+
ProbeBuilder {
105+
action,
106+
period,
107+
success_threshold,
108+
failure_threshold,
109+
timeout,
110+
initial_delay,
111+
termination_grace_period,
112+
}
113+
}
114+
}
115+
116+
impl ProbeBuilder<ProbeAction, ()> {
117+
/// The period/interval in which the probe should be executed.
118+
pub fn with_period(self, period: Duration) -> ProbeBuilder<ProbeAction, Duration> {
119+
let Self {
120+
action,
121+
period: (),
122+
success_threshold,
123+
failure_threshold,
124+
timeout,
125+
initial_delay,
126+
termination_grace_period,
127+
} = self;
128+
129+
ProbeBuilder {
130+
action,
131+
period,
132+
success_threshold,
133+
failure_threshold,
134+
timeout,
135+
initial_delay,
136+
termination_grace_period,
137+
}
138+
}
139+
}
140+
141+
// success_threshold: i32,
142+
// failure_threshold: i32,
143+
// timeout: Duration,
144+
// initial_delay: Duration,
145+
// termination_grace_period: Duration,
146+
147+
impl ProbeBuilder<ProbeAction, Duration> {
148+
/// How often the probe must succeed before being considered successful.
149+
pub fn with_success_threshold(mut self, success_threshold: i32) -> Self {
150+
self.success_threshold = success_threshold;
151+
self
152+
}
153+
154+
/// The duration the probe needs to succeed before being considered successful.
155+
///
156+
/// This internally calculates the needed success threshold based on the period and passes that
157+
/// to [`Self::with_success_threshold`].
158+
pub fn with_success_threshold_duration(self, success_threshold_duration: Duration) -> Self {
159+
let success_threshold = success_threshold_duration.div_duration_f32(*self.period);
160+
// SAFETY: Returning an Result here would hurt the builder ergonomics and having such big
161+
// numbers does not have any real world effect.
162+
let success_threshold = success_threshold.ceil() as i32;
163+
self.with_success_threshold(success_threshold)
164+
}
165+
166+
/// How often the probe must fail before being considered failed.
167+
pub fn with_failure_threshold(mut self, failure_threshold: i32) -> Self {
168+
self.failure_threshold = failure_threshold;
169+
self
170+
}
171+
172+
/// The duration the probe needs to fail before being considered failed.
173+
///
174+
/// This internally calculates the needed failure threshold based on the period and passes that
175+
/// to [`Self::with_failure_threshold`].
176+
pub fn with_failure_threshold_duration(self, failure_threshold_duration: Duration) -> Self {
177+
let failure_threshold = failure_threshold_duration.div_duration_f32(*self.period);
178+
// SAFETY: Returning an Result here would hurt the builder ergonomics and having such big
179+
// numbers does not have any real world effect.
180+
let failure_threshold = failure_threshold.ceil() as i32;
181+
self.with_failure_threshold(failure_threshold)
182+
}
183+
184+
pub fn build(self) -> Probe {
185+
let mut probe = Probe {
186+
exec: None,
187+
failure_threshold: Some(self.failure_threshold),
188+
grpc: None,
189+
http_get: None,
190+
initial_delay_seconds: Some(
191+
self.initial_delay
192+
.as_secs()
193+
.try_into()
194+
.expect("TODO Error handling"),
195+
),
196+
period_seconds: Some(
197+
self.period
198+
.as_secs()
199+
.try_into()
200+
.expect("TODO Error handling"),
201+
),
202+
success_threshold: Some(self.success_threshold),
203+
tcp_socket: None,
204+
termination_grace_period_seconds: Some(
205+
self.termination_grace_period
206+
.as_secs()
207+
.try_into()
208+
.expect("TODO Error handling"),
209+
),
210+
timeout_seconds: Some(
211+
self.timeout
212+
.as_secs()
213+
.try_into()
214+
.expect("TODO Error handling"),
215+
),
216+
};
217+
218+
match self.action {
219+
ProbeAction::Exec(exec_action) => probe.exec = Some(exec_action),
220+
ProbeAction::Grpc(grpc_action) => probe.grpc = Some(grpc_action),
221+
ProbeAction::HttpGet(http_get_action) => probe.http_get = Some(http_get_action),
222+
ProbeAction::TcpSocket(tcp_socket_action) => probe.tcp_socket = Some(tcp_socket_action),
223+
}
224+
225+
probe
226+
}
227+
}
228+
229+
#[cfg(test)]
230+
mod tests {
231+
use super::*;
232+
233+
#[test]
234+
fn test_probe_builder() {
235+
let probe = ProbeBuilder::default()
236+
.with_exec_action_helper(["sleep", "1"])
237+
.with_period(Duration::from_secs(5))
238+
.with_failure_threshold_duration(Duration::from_secs(33))
239+
.build();
240+
241+
assert_eq!(
242+
probe,
243+
Probe {
244+
exec: Some(ExecAction {
245+
command: Some(vec!["sleep".to_owned(), "1".to_owned()])
246+
}),
247+
failure_threshold: Some(7),
248+
grpc: None,
249+
http_get: None,
250+
initial_delay_seconds: Some(0),
251+
period_seconds: Some(5),
252+
success_threshold: Some(1),
253+
tcp_socket: None,
254+
termination_grace_period_seconds: Some(0),
255+
timeout_seconds: Some(1),
256+
}
257+
);
258+
}
259+
}

0 commit comments

Comments
 (0)