docker_wrapper/command/swarm/
unlock_key.rs

1//! Docker swarm unlock-key command implementation.
2
3use crate::command::{CommandExecutor, CommandOutput, DockerCommand};
4use crate::error::Result;
5use async_trait::async_trait;
6
7/// Result of swarm unlock-key command
8#[derive(Debug, Clone)]
9pub struct SwarmUnlockKeyResult {
10    /// The unlock key
11    pub key: Option<String>,
12    /// Raw output from the command
13    pub output: String,
14}
15
16impl SwarmUnlockKeyResult {
17    /// Parse the swarm unlock-key output
18    fn parse(output: &CommandOutput, quiet: bool) -> Self {
19        let stdout = output.stdout.trim();
20
21        let key = if quiet {
22            // In quiet mode, output is just the key
23            Some(stdout.to_string())
24        } else {
25            // Normal mode: parse the key from output
26            // Output format:
27            // To unlock a swarm manager after it restarts, run the `docker swarm unlock`
28            // command and provide the following key:
29            //
30            //     SWMKEY-1-...
31
32            let mut found_key = None;
33            for line in stdout.lines() {
34                let trimmed = line.trim();
35                if trimmed.starts_with("SWMKEY-") {
36                    found_key = Some(trimmed.to_string());
37                    break;
38                }
39            }
40            found_key
41        };
42
43        Self {
44            key,
45            output: stdout.to_string(),
46        }
47    }
48}
49
50/// Docker swarm unlock-key command builder
51///
52/// Manage the unlock key for a locked swarm.
53#[derive(Debug, Clone, Default)]
54pub struct SwarmUnlockKeyCommand {
55    /// Only display the key (no instructions)
56    quiet: bool,
57    /// Rotate the unlock key
58    rotate: bool,
59    /// Command executor
60    pub executor: CommandExecutor,
61}
62
63impl SwarmUnlockKeyCommand {
64    /// Create a new swarm unlock-key command
65    #[must_use]
66    pub fn new() -> Self {
67        Self::default()
68    }
69
70    /// Only display the key (no instructions)
71    #[must_use]
72    pub fn quiet(mut self) -> Self {
73        self.quiet = true;
74        self
75    }
76
77    /// Rotate the unlock key
78    #[must_use]
79    pub fn rotate(mut self) -> Self {
80        self.rotate = true;
81        self
82    }
83
84    /// Build the command arguments
85    fn build_args(&self) -> Vec<String> {
86        let mut args = vec!["swarm".to_string(), "unlock-key".to_string()];
87
88        if self.quiet {
89            args.push("--quiet".to_string());
90        }
91
92        if self.rotate {
93            args.push("--rotate".to_string());
94        }
95
96        args
97    }
98}
99
100#[async_trait]
101impl DockerCommand for SwarmUnlockKeyCommand {
102    type Output = SwarmUnlockKeyResult;
103
104    fn get_executor(&self) -> &CommandExecutor {
105        &self.executor
106    }
107
108    fn get_executor_mut(&mut self) -> &mut CommandExecutor {
109        &mut self.executor
110    }
111
112    fn build_command_args(&self) -> Vec<String> {
113        self.build_args()
114    }
115
116    async fn execute(&self) -> Result<Self::Output> {
117        let args = self.build_args();
118        let output = self.execute_command(args).await?;
119        Ok(SwarmUnlockKeyResult::parse(&output, self.quiet))
120    }
121}
122
123#[cfg(test)]
124mod tests {
125    use super::*;
126
127    #[test]
128    fn test_swarm_unlock_key_basic() {
129        let cmd = SwarmUnlockKeyCommand::new();
130        let args = cmd.build_args();
131        assert_eq!(args, vec!["swarm", "unlock-key"]);
132    }
133
134    #[test]
135    fn test_swarm_unlock_key_quiet() {
136        let cmd = SwarmUnlockKeyCommand::new().quiet();
137        let args = cmd.build_args();
138        assert!(args.contains(&"--quiet".to_string()));
139    }
140
141    #[test]
142    fn test_swarm_unlock_key_rotate() {
143        let cmd = SwarmUnlockKeyCommand::new().rotate();
144        let args = cmd.build_args();
145        assert!(args.contains(&"--rotate".to_string()));
146    }
147
148    #[test]
149    fn test_swarm_unlock_key_all_options() {
150        let cmd = SwarmUnlockKeyCommand::new().quiet().rotate();
151        let args = cmd.build_args();
152        assert_eq!(args, vec!["swarm", "unlock-key", "--quiet", "--rotate"]);
153    }
154}