docker_wrapper/command/swarm/
join.rs1use crate::command::{CommandExecutor, CommandOutput, DockerCommand};
4use crate::error::Result;
5use async_trait::async_trait;
6
7#[derive(Debug, Clone)]
9pub struct SwarmJoinResult {
10 pub success: bool,
12 pub output: String,
14}
15
16impl SwarmJoinResult {
17 fn parse(output: &CommandOutput) -> Self {
19 Self {
20 success: output.success,
21 output: output.stdout.clone(),
22 }
23 }
24}
25
26#[derive(Debug, Clone)]
28pub struct SwarmJoinCommand {
29 remote_addr: String,
31 token: String,
33 advertise_addr: Option<String>,
35 availability: Option<String>,
37 data_path_addr: Option<String>,
39 listen_addr: Option<String>,
41 pub executor: CommandExecutor,
43}
44
45impl SwarmJoinCommand {
46 #[must_use]
52 pub fn new(token: impl Into<String>, remote_addr: impl Into<String>) -> Self {
53 Self {
54 remote_addr: remote_addr.into(),
55 token: token.into(),
56 advertise_addr: None,
57 availability: None,
58 data_path_addr: None,
59 listen_addr: None,
60 executor: CommandExecutor::new(),
61 }
62 }
63
64 #[must_use]
66 pub fn advertise_addr(mut self, addr: impl Into<String>) -> Self {
67 self.advertise_addr = Some(addr.into());
68 self
69 }
70
71 #[must_use]
73 pub fn availability(mut self, availability: impl Into<String>) -> Self {
74 self.availability = Some(availability.into());
75 self
76 }
77
78 #[must_use]
80 pub fn data_path_addr(mut self, addr: impl Into<String>) -> Self {
81 self.data_path_addr = Some(addr.into());
82 self
83 }
84
85 #[must_use]
87 pub fn listen_addr(mut self, addr: impl Into<String>) -> Self {
88 self.listen_addr = Some(addr.into());
89 self
90 }
91
92 fn build_args(&self) -> Vec<String> {
94 let mut args = vec!["swarm".to_string(), "join".to_string()];
95
96 if let Some(ref addr) = self.advertise_addr {
97 args.push("--advertise-addr".to_string());
98 args.push(addr.clone());
99 }
100
101 if let Some(ref availability) = self.availability {
102 args.push("--availability".to_string());
103 args.push(availability.clone());
104 }
105
106 if let Some(ref addr) = self.data_path_addr {
107 args.push("--data-path-addr".to_string());
108 args.push(addr.clone());
109 }
110
111 if let Some(ref addr) = self.listen_addr {
112 args.push("--listen-addr".to_string());
113 args.push(addr.clone());
114 }
115
116 args.push("--token".to_string());
118 args.push(self.token.clone());
119
120 args.push(self.remote_addr.clone());
122
123 args
124 }
125}
126
127#[async_trait]
128impl DockerCommand for SwarmJoinCommand {
129 type Output = SwarmJoinResult;
130
131 fn get_executor(&self) -> &CommandExecutor {
132 &self.executor
133 }
134
135 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
136 &mut self.executor
137 }
138
139 fn build_command_args(&self) -> Vec<String> {
140 self.build_args()
141 }
142
143 async fn execute(&self) -> Result<Self::Output> {
144 let args = self.build_args();
145 let output = self.execute_command(args).await?;
146 Ok(SwarmJoinResult::parse(&output))
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 #[test]
155 fn test_swarm_join_basic() {
156 let cmd = SwarmJoinCommand::new("SWMTKN-1-xxx", "192.168.1.1:2377");
157 let args = cmd.build_args();
158 assert_eq!(args[0], "swarm");
159 assert_eq!(args[1], "join");
160 assert!(args.contains(&"--token".to_string()));
161 assert!(args.contains(&"SWMTKN-1-xxx".to_string()));
162 assert!(args.contains(&"192.168.1.1:2377".to_string()));
163 }
164
165 #[test]
166 fn test_swarm_join_with_options() {
167 let cmd = SwarmJoinCommand::new("SWMTKN-1-xxx", "192.168.1.1:2377")
168 .advertise_addr("192.168.1.2:2377")
169 .availability("active")
170 .data_path_addr("192.168.1.2")
171 .listen_addr("0.0.0.0:2377");
172
173 let args = cmd.build_args();
174 assert!(args.contains(&"--advertise-addr".to_string()));
175 assert!(args.contains(&"--availability".to_string()));
176 assert!(args.contains(&"--data-path-addr".to_string()));
177 assert!(args.contains(&"--listen-addr".to_string()));
178 }
179}