docker_wrapper/command/
kill.rs1use super::{CommandExecutor, CommandOutput, DockerCommand};
6use crate::error::{Error, Result};
7use async_trait::async_trait;
8
9#[derive(Debug, Clone)]
11pub struct KillCommand {
12 containers: Vec<String>,
14 signal: Option<String>,
16 pub executor: CommandExecutor,
18}
19
20impl KillCommand {
21 #[must_use]
23 pub fn new(container: impl Into<String>) -> Self {
24 Self {
25 containers: vec![container.into()],
26 signal: None,
27 executor: CommandExecutor::new(),
28 }
29 }
30
31 #[must_use]
33 pub fn new_multiple(containers: Vec<impl Into<String>>) -> Self {
34 Self {
35 containers: containers.into_iter().map(Into::into).collect(),
36 signal: None,
37 executor: CommandExecutor::new(),
38 }
39 }
40
41 #[must_use]
43 pub fn container(mut self, container: impl Into<String>) -> Self {
44 self.containers.push(container.into());
45 self
46 }
47
48 #[must_use]
50 pub fn signal(mut self, signal: impl Into<String>) -> Self {
51 self.signal = Some(signal.into());
52 self
53 }
54
55 pub async fn run(&self) -> Result<KillResult> {
64 let output = self.execute().await?;
65
66 let killed: Vec<String> = output
68 .stdout
69 .lines()
70 .filter(|line| !line.is_empty())
71 .map(String::from)
72 .collect();
73
74 Ok(KillResult {
75 killed,
76 signal: self.signal.clone(),
77 output,
78 })
79 }
80}
81
82#[async_trait]
83impl DockerCommand for KillCommand {
84 type Output = CommandOutput;
85
86 fn build_command_args(&self) -> Vec<String> {
87 let mut args = vec!["kill".to_string()];
88
89 if let Some(ref sig) = self.signal {
90 args.push("--signal".to_string());
91 args.push(sig.clone());
92 }
93
94 args.extend(self.containers.clone());
96
97 args.extend(self.executor.raw_args.clone());
98 args
99 }
100
101 fn get_executor(&self) -> &CommandExecutor {
102 &self.executor
103 }
104
105 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
106 &mut self.executor
107 }
108
109 async fn execute(&self) -> Result<Self::Output> {
110 if self.containers.is_empty() {
111 return Err(Error::invalid_config("No containers specified for kill"));
112 }
113
114 let args = self.build_command_args();
115 let command_name = args[0].clone();
116 let command_args = args[1..].to_vec();
117 self.executor
118 .execute_command(&command_name, command_args)
119 .await
120 }
121}
122
123#[derive(Debug, Clone)]
125pub struct KillResult {
126 pub killed: Vec<String>,
128 pub signal: Option<String>,
130 pub output: CommandOutput,
132}
133
134impl KillResult {
135 #[must_use]
137 pub fn all_killed(&self) -> bool {
138 self.output.success
139 }
140
141 #[must_use]
143 pub fn count(&self) -> usize {
144 self.killed.len()
145 }
146
147 #[must_use]
149 pub fn signal_sent(&self) -> &str {
150 self.signal.as_deref().unwrap_or("SIGKILL")
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn test_kill_single_container() {
160 let cmd = KillCommand::new("test-container");
161 let args = cmd.build_command_args();
162 assert_eq!(args, vec!["kill", "test-container"]);
163 }
164
165 #[test]
166 fn test_kill_multiple_containers() {
167 let cmd = KillCommand::new_multiple(vec!["container1", "container2", "container3"]);
168 let args = cmd.build_command_args();
169 assert_eq!(args, vec!["kill", "container1", "container2", "container3"]);
170 }
171
172 #[test]
173 fn test_kill_with_signal() {
174 let cmd = KillCommand::new("test-container").signal("SIGTERM");
175 let args = cmd.build_command_args();
176 assert_eq!(args, vec!["kill", "--signal", "SIGTERM", "test-container"]);
177 }
178
179 #[test]
180 fn test_kill_with_numeric_signal() {
181 let cmd = KillCommand::new("test-container").signal("9");
182 let args = cmd.build_command_args();
183 assert_eq!(args, vec!["kill", "--signal", "9", "test-container"]);
184 }
185
186 #[test]
187 fn test_kill_builder_chain() {
188 let cmd = KillCommand::new("container1")
189 .container("container2")
190 .container("container3")
191 .signal("SIGTERM");
192 let args = cmd.build_command_args();
193 assert_eq!(
194 args,
195 vec![
196 "kill",
197 "--signal",
198 "SIGTERM",
199 "container1",
200 "container2",
201 "container3"
202 ]
203 );
204 }
205}