docker_wrapper/command/swarm/
ca.rs1use crate::command::{CommandExecutor, CommandOutput, DockerCommand};
4use crate::error::Result;
5use async_trait::async_trait;
6
7#[derive(Debug, Clone)]
9pub struct SwarmCaResult {
10 pub certificate: Option<String>,
12 pub output: String,
14}
15
16impl SwarmCaResult {
17 fn parse(output: &CommandOutput) -> Self {
19 let stdout = output.stdout.trim();
20
21 let certificate = if stdout.contains("-----BEGIN CERTIFICATE-----") {
23 Some(stdout.to_string())
24 } else {
25 None
26 };
27
28 Self {
29 certificate,
30 output: stdout.to_string(),
31 }
32 }
33}
34
35#[derive(Debug, Clone, Default)]
39pub struct SwarmCaCommand {
40 ca_cert: Option<String>,
42 ca_key: Option<String>,
44 cert_expiry: Option<String>,
46 detach: bool,
48 external_ca: Option<String>,
50 quiet: bool,
52 rotate: bool,
54 pub executor: CommandExecutor,
56}
57
58impl SwarmCaCommand {
59 #[must_use]
61 pub fn new() -> Self {
62 Self::default()
63 }
64
65 #[must_use]
67 pub fn ca_cert(mut self, path: impl Into<String>) -> Self {
68 self.ca_cert = Some(path.into());
69 self
70 }
71
72 #[must_use]
74 pub fn ca_key(mut self, path: impl Into<String>) -> Self {
75 self.ca_key = Some(path.into());
76 self
77 }
78
79 #[must_use]
81 pub fn cert_expiry(mut self, expiry: impl Into<String>) -> Self {
82 self.cert_expiry = Some(expiry.into());
83 self
84 }
85
86 #[must_use]
88 pub fn detach(mut self) -> Self {
89 self.detach = true;
90 self
91 }
92
93 #[must_use]
95 pub fn external_ca(mut self, spec: impl Into<String>) -> Self {
96 self.external_ca = Some(spec.into());
97 self
98 }
99
100 #[must_use]
102 pub fn quiet(mut self) -> Self {
103 self.quiet = true;
104 self
105 }
106
107 #[must_use]
109 pub fn rotate(mut self) -> Self {
110 self.rotate = true;
111 self
112 }
113
114 fn build_args(&self) -> Vec<String> {
116 let mut args = vec!["swarm".to_string(), "ca".to_string()];
117
118 if let Some(ref path) = self.ca_cert {
119 args.push("--ca-cert".to_string());
120 args.push(path.clone());
121 }
122
123 if let Some(ref path) = self.ca_key {
124 args.push("--ca-key".to_string());
125 args.push(path.clone());
126 }
127
128 if let Some(ref expiry) = self.cert_expiry {
129 args.push("--cert-expiry".to_string());
130 args.push(expiry.clone());
131 }
132
133 if self.detach {
134 args.push("--detach".to_string());
135 }
136
137 if let Some(ref spec) = self.external_ca {
138 args.push("--external-ca".to_string());
139 args.push(spec.clone());
140 }
141
142 if self.quiet {
143 args.push("--quiet".to_string());
144 }
145
146 if self.rotate {
147 args.push("--rotate".to_string());
148 }
149
150 args
151 }
152}
153
154#[async_trait]
155impl DockerCommand for SwarmCaCommand {
156 type Output = SwarmCaResult;
157
158 fn get_executor(&self) -> &CommandExecutor {
159 &self.executor
160 }
161
162 fn get_executor_mut(&mut self) -> &mut CommandExecutor {
163 &mut self.executor
164 }
165
166 fn build_command_args(&self) -> Vec<String> {
167 self.build_args()
168 }
169
170 async fn execute(&self) -> Result<Self::Output> {
171 let args = self.build_args();
172 let output = self.execute_command(args).await?;
173 Ok(SwarmCaResult::parse(&output))
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::*;
180
181 #[test]
182 fn test_swarm_ca_basic() {
183 let cmd = SwarmCaCommand::new();
184 let args = cmd.build_args();
185 assert_eq!(args, vec!["swarm", "ca"]);
186 }
187
188 #[test]
189 fn test_swarm_ca_rotate() {
190 let cmd = SwarmCaCommand::new().rotate();
191 let args = cmd.build_args();
192 assert!(args.contains(&"--rotate".to_string()));
193 }
194
195 #[test]
196 fn test_swarm_ca_with_cert_and_key() {
197 let cmd = SwarmCaCommand::new()
198 .ca_cert("/path/to/cert.pem")
199 .ca_key("/path/to/key.pem");
200 let args = cmd.build_args();
201 assert!(args.contains(&"--ca-cert".to_string()));
202 assert!(args.contains(&"/path/to/cert.pem".to_string()));
203 assert!(args.contains(&"--ca-key".to_string()));
204 assert!(args.contains(&"/path/to/key.pem".to_string()));
205 }
206
207 #[test]
208 fn test_swarm_ca_all_options() {
209 let cmd = SwarmCaCommand::new()
210 .ca_cert("/cert.pem")
211 .ca_key("/key.pem")
212 .cert_expiry("90d")
213 .detach()
214 .external_ca("protocol=cfssl,url=https://ca.example.com")
215 .quiet()
216 .rotate();
217
218 let args = cmd.build_args();
219 assert!(args.contains(&"--ca-cert".to_string()));
220 assert!(args.contains(&"--ca-key".to_string()));
221 assert!(args.contains(&"--cert-expiry".to_string()));
222 assert!(args.contains(&"--detach".to_string()));
223 assert!(args.contains(&"--external-ca".to_string()));
224 assert!(args.contains(&"--quiet".to_string()));
225 assert!(args.contains(&"--rotate".to_string()));
226 }
227}