docker_wrapper/command/context/
create.rs

1//! Docker context create command implementation.
2
3use crate::command::{CommandExecutor, CommandOutput, DockerCommand};
4use crate::error::Result;
5use async_trait::async_trait;
6
7/// Docker context create command builder
8///
9/// Create a new Docker context.
10///
11/// # Example
12///
13/// ```no_run
14/// use docker_wrapper::{ContextCreateCommand, DockerCommand};
15///
16/// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
17/// // Create a context for a remote Docker daemon
18/// ContextCreateCommand::new("production")
19///     .description("Production environment")
20///     .docker_host("ssh://user@remote-host")
21///     .execute()
22///     .await?;
23/// # Ok(())
24/// # }
25/// ```
26#[derive(Debug, Clone)]
27pub struct ContextCreateCommand {
28    /// Context name
29    name: String,
30    /// Context description
31    description: Option<String>,
32    /// Docker host
33    docker_host: Option<String>,
34    /// Default stack orchestrator
35    default_stack_orchestrator: Option<String>,
36    /// Docker API endpoint
37    docker_api_endpoint: Option<String>,
38    /// Kubernetes config file
39    kubernetes_config_file: Option<String>,
40    /// Kubernetes context
41    kubernetes_context: Option<String>,
42    /// Kubernetes namespace
43    kubernetes_namespace: Option<String>,
44    /// Kubernetes API endpoint
45    kubernetes_api_endpoint: Option<String>,
46    /// Create context from existing context
47    from: Option<String>,
48    /// Command executor
49    pub executor: CommandExecutor,
50}
51
52impl ContextCreateCommand {
53    /// Create a new context create command
54    #[must_use]
55    pub fn new(name: impl Into<String>) -> Self {
56        Self {
57            name: name.into(),
58            description: None,
59            docker_host: None,
60            default_stack_orchestrator: None,
61            docker_api_endpoint: None,
62            kubernetes_config_file: None,
63            kubernetes_context: None,
64            kubernetes_namespace: None,
65            kubernetes_api_endpoint: None,
66            from: None,
67            executor: CommandExecutor::new(),
68        }
69    }
70
71    /// Set context description
72    #[must_use]
73    pub fn description(mut self, desc: impl Into<String>) -> Self {
74        self.description = Some(desc.into());
75        self
76    }
77
78    /// Set Docker host
79    #[must_use]
80    pub fn docker_host(mut self, host: impl Into<String>) -> Self {
81        self.docker_host = Some(host.into());
82        self
83    }
84
85    /// Set default stack orchestrator (swarm|kubernetes|all)
86    #[must_use]
87    pub fn default_stack_orchestrator(mut self, orchestrator: impl Into<String>) -> Self {
88        self.default_stack_orchestrator = Some(orchestrator.into());
89        self
90    }
91
92    /// Set Docker API endpoint
93    #[must_use]
94    pub fn docker_api_endpoint(mut self, endpoint: impl Into<String>) -> Self {
95        self.docker_api_endpoint = Some(endpoint.into());
96        self
97    }
98
99    /// Set Kubernetes config file
100    #[must_use]
101    pub fn kubernetes_config_file(mut self, file: impl Into<String>) -> Self {
102        self.kubernetes_config_file = Some(file.into());
103        self
104    }
105
106    /// Set Kubernetes context
107    #[must_use]
108    pub fn kubernetes_context(mut self, context: impl Into<String>) -> Self {
109        self.kubernetes_context = Some(context.into());
110        self
111    }
112
113    /// Set Kubernetes namespace
114    #[must_use]
115    pub fn kubernetes_namespace(mut self, namespace: impl Into<String>) -> Self {
116        self.kubernetes_namespace = Some(namespace.into());
117        self
118    }
119
120    /// Set Kubernetes API endpoint
121    #[must_use]
122    pub fn kubernetes_api_endpoint(mut self, endpoint: impl Into<String>) -> Self {
123        self.kubernetes_api_endpoint = Some(endpoint.into());
124        self
125    }
126
127    /// Create context from existing context
128    #[must_use]
129    pub fn from(mut self, context: impl Into<String>) -> Self {
130        self.from = Some(context.into());
131        self
132    }
133}
134
135#[async_trait]
136impl DockerCommand for ContextCreateCommand {
137    type Output = CommandOutput;
138
139    fn get_executor(&self) -> &CommandExecutor {
140        &self.executor
141    }
142
143    fn get_executor_mut(&mut self) -> &mut CommandExecutor {
144        &mut self.executor
145    }
146
147    fn build_command_args(&self) -> Vec<String> {
148        let mut args = vec!["context".to_string(), "create".to_string()];
149
150        if let Some(desc) = &self.description {
151            args.push("--description".to_string());
152            args.push(desc.clone());
153        }
154
155        if let Some(host) = &self.docker_host {
156            args.push("--docker".to_string());
157            args.push(format!("host={host}"));
158        }
159
160        if let Some(orchestrator) = &self.default_stack_orchestrator {
161            args.push("--default-stack-orchestrator".to_string());
162            args.push(orchestrator.clone());
163        }
164
165        if let Some(endpoint) = &self.docker_api_endpoint {
166            args.push("--docker".to_string());
167            args.push(format!("api-endpoint={endpoint}"));
168        }
169
170        if let Some(file) = &self.kubernetes_config_file {
171            args.push("--kubernetes".to_string());
172            args.push(format!("config-file={file}"));
173        }
174
175        if let Some(context) = &self.kubernetes_context {
176            args.push("--kubernetes".to_string());
177            args.push(format!("context={context}"));
178        }
179
180        if let Some(namespace) = &self.kubernetes_namespace {
181            args.push("--kubernetes".to_string());
182            args.push(format!("namespace={namespace}"));
183        }
184
185        if let Some(endpoint) = &self.kubernetes_api_endpoint {
186            args.push("--kubernetes".to_string());
187            args.push(format!("api-endpoint={endpoint}"));
188        }
189
190        if let Some(from) = &self.from {
191            args.push("--from".to_string());
192            args.push(from.clone());
193        }
194
195        args.push(self.name.clone());
196
197        args.extend(self.executor.raw_args.clone());
198        args
199    }
200
201    async fn execute(&self) -> Result<Self::Output> {
202        let args = self.build_command_args();
203        self.execute_command(args).await
204    }
205}
206
207#[cfg(test)]
208mod tests {
209    use super::*;
210
211    #[test]
212    fn test_context_create_basic() {
213        let cmd = ContextCreateCommand::new("test-context");
214        let args = cmd.build_command_args();
215        assert_eq!(args[0], "context");
216        assert_eq!(args[1], "create");
217        assert!(args.contains(&"test-context".to_string()));
218    }
219
220    #[test]
221    fn test_context_create_with_description() {
222        let cmd =
223            ContextCreateCommand::new("test-context").description("Test context for development");
224        let args = cmd.build_command_args();
225        assert!(args.contains(&"--description".to_string()));
226        assert!(args.contains(&"Test context for development".to_string()));
227    }
228
229    #[test]
230    fn test_context_create_with_docker_host() {
231        let cmd = ContextCreateCommand::new("remote").docker_host("ssh://user@remote-host");
232        let args = cmd.build_command_args();
233        assert!(args.contains(&"--docker".to_string()));
234        assert!(args.contains(&"host=ssh://user@remote-host".to_string()));
235    }
236
237    #[test]
238    fn test_context_create_from_existing() {
239        let cmd = ContextCreateCommand::new("new-context").from("default");
240        let args = cmd.build_command_args();
241        assert!(args.contains(&"--from".to_string()));
242        assert!(args.contains(&"default".to_string()));
243    }
244}