Skip to main content

muster/domain/process/
stop.rs

1use std::time::Duration;
2
3use getset::{Getters, WithSetters};
4use serde::{Deserialize, Serialize};
5use strum::Display;
6use typed_builder::TypedBuilder;
7
8/// Default time a command may spend shutting down before force-kill.
9const DEFAULT_GRACE_PERIOD: Duration = Duration::from_secs(5);
10
11/// Graceful signal sent before a command is force-killed.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Display)]
13#[serde(rename_all = "snake_case")]
14#[strum(serialize_all = "snake_case")]
15pub enum StopSignal {
16    /// Requests the conventional service shutdown path (SIGTERM on Unix).
17    Terminate,
18    /// Requests the conventional interactive interrupt path (SIGINT on Unix).
19    Interrupt,
20}
21
22/// Graceful shutdown policy for one command process.
23#[derive(
24    Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Getters, WithSetters, TypedBuilder,
25)]
26#[set_with]
27pub struct StopPolicy {
28    /// Graceful signal sent to the command's process group.
29    #[getset(get = "pub", set_with = "pub")]
30    signal: StopSignal,
31    /// Time allowed for graceful exit before an unconditional force-kill.
32    #[serde(with = "humantime_serde")]
33    #[getset(get = "pub", set_with = "pub")]
34    grace_period: Duration,
35}
36
37impl Default for StopPolicy {
38    /// Returns the default command shutdown policy: SIGTERM with five seconds
39    /// available for cleanup.
40    fn default() -> Self {
41        Self::builder()
42            .signal(StopSignal::Terminate)
43            .grace_period(DEFAULT_GRACE_PERIOD)
44            .build()
45    }
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    /// Human-readable durations deserialize into the domain policy.
53    #[test]
54    fn parses_a_human_readable_grace_period() {
55        let policy: StopPolicy =
56            serde_yaml_ng::from_str("signal: terminate\ngrace_period: 5s\n").unwrap();
57
58        assert_eq!(*policy.signal(), StopSignal::Terminate);
59        assert_eq!(*policy.grace_period(), Duration::from_secs(5));
60    }
61
62    /// Commands default to a conventional service shutdown request.
63    #[test]
64    fn defaults_to_terminate_with_a_bounded_grace_period() {
65        let policy = StopPolicy::default();
66
67        assert_eq!(*policy.signal(), StopSignal::Terminate);
68        assert_eq!(*policy.grace_period(), DEFAULT_GRACE_PERIOD);
69    }
70}