docker_wrapper/command/context/
update.rs

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