Skip to main content

ironflow_ops_docker/containers/
lifecycle.rs

1//! Container lifecycle operations: start, stop, restart.
2
3use async_trait::async_trait;
4use bollard::Docker;
5use bollard::query_parameters::{RestartContainerOptions, StopContainerOptions};
6use ironflow_core::error::OperationError;
7use ironflow_core::operation::{Operation, OperationContext, TypedOperation};
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10
11use crate::containers::DockerRef;
12use crate::helpers::{docker_error, to_value};
13
14// ---------------------------------------------------------------------------
15// ContainerStart
16// ---------------------------------------------------------------------------
17
18/// Output of a container start.
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct ContainerStartOutput {
21    /// The container ID or name.
22    pub container: String,
23}
24
25/// Start a stopped container.
26///
27/// # Examples
28///
29/// ```no_run
30/// use ironflow_ops_docker::containers::ContainerStart;
31/// use ironflow_ops_docker::DockerClient;
32/// use ironflow_core::operation::Operation;
33///
34/// let client = DockerClient::connect_local().unwrap();
35/// let op = ContainerStart::new(&client, "my-container");
36/// assert_eq!(op.kind(), "docker");
37/// ```
38pub struct ContainerStart {
39    docker: Docker,
40    container: String,
41}
42
43impl ContainerStart {
44    /// Create a new container-start operation.
45    pub fn new(client: impl Into<DockerRef>, container: impl Into<String>) -> Self {
46        Self {
47            docker: client.into().0,
48            container: container.into(),
49        }
50    }
51
52    /// Execute and return a typed result.
53    ///
54    /// # Errors
55    ///
56    /// Returns [`OperationError::External`] if the container does not exist or
57    /// cannot be started.
58    pub async fn run(
59        &self,
60        _ctx: &OperationContext,
61    ) -> Result<ContainerStartOutput, OperationError> {
62        self.docker
63            .start_container(
64                &self.container,
65                None::<bollard::query_parameters::StartContainerOptions>,
66            )
67            .await
68            .map_err(docker_error)?;
69        Ok(ContainerStartOutput {
70            container: self.container.clone(),
71        })
72    }
73}
74
75#[async_trait]
76impl Operation for ContainerStart {
77    fn kind(&self) -> &str {
78        "docker"
79    }
80
81    async fn execute(&self, ctx: &OperationContext) -> Result<Value, OperationError> {
82        to_value(&self.run(ctx).await?)
83    }
84
85    fn input(&self) -> Option<Value> {
86        Some(serde_json::json!({
87            "operation": "container_start",
88            "container": self.container,
89        }))
90    }
91}
92
93impl TypedOperation for ContainerStart {
94    type Output = ContainerStartOutput;
95}
96
97// ---------------------------------------------------------------------------
98// ContainerStop
99// ---------------------------------------------------------------------------
100
101/// Output of a container stop.
102#[derive(Debug, Clone, Serialize, Deserialize)]
103pub struct ContainerStopOutput {
104    /// The container ID or name.
105    pub container: String,
106}
107
108/// Stop a running container.
109///
110/// # Examples
111///
112/// ```no_run
113/// use ironflow_ops_docker::containers::ContainerStop;
114/// use ironflow_ops_docker::DockerClient;
115/// use ironflow_core::operation::Operation;
116///
117/// let client = DockerClient::connect_local().unwrap();
118/// let op = ContainerStop::new(&client, "my-container");
119/// assert_eq!(op.kind(), "docker");
120/// ```
121pub struct ContainerStop {
122    docker: Docker,
123    container: String,
124    timeout: Option<i64>,
125}
126
127impl ContainerStop {
128    /// Create a new container-stop operation.
129    pub fn new(client: impl Into<DockerRef>, container: impl Into<String>) -> Self {
130        Self {
131            docker: client.into().0,
132            container: container.into(),
133            timeout: None,
134        }
135    }
136
137    /// Set the timeout in seconds before killing the container.
138    pub fn timeout(mut self, secs: i64) -> Self {
139        self.timeout = Some(secs);
140        self
141    }
142
143    /// Execute and return a typed result.
144    ///
145    /// # Errors
146    ///
147    /// Returns [`OperationError::External`] if the container does not exist.
148    pub async fn run(
149        &self,
150        _ctx: &OperationContext,
151    ) -> Result<ContainerStopOutput, OperationError> {
152        let options = StopContainerOptions {
153            t: Some(self.timeout.unwrap_or(10) as i32),
154            signal: None,
155        };
156        self.docker
157            .stop_container(&self.container, Some(options))
158            .await
159            .map_err(docker_error)?;
160        Ok(ContainerStopOutput {
161            container: self.container.clone(),
162        })
163    }
164}
165
166#[async_trait]
167impl Operation for ContainerStop {
168    fn kind(&self) -> &str {
169        "docker"
170    }
171
172    async fn execute(&self, ctx: &OperationContext) -> Result<Value, OperationError> {
173        to_value(&self.run(ctx).await?)
174    }
175
176    fn input(&self) -> Option<Value> {
177        Some(serde_json::json!({
178            "operation": "container_stop",
179            "container": self.container,
180        }))
181    }
182}
183
184impl TypedOperation for ContainerStop {
185    type Output = ContainerStopOutput;
186}
187
188// ---------------------------------------------------------------------------
189// ContainerRestart
190// ---------------------------------------------------------------------------
191
192/// Output of a container restart.
193#[derive(Debug, Clone, Serialize, Deserialize)]
194pub struct ContainerRestartOutput {
195    /// The container ID or name.
196    pub container: String,
197}
198
199/// Restart a container.
200///
201/// # Examples
202///
203/// ```no_run
204/// use ironflow_ops_docker::containers::ContainerRestart;
205/// use ironflow_ops_docker::DockerClient;
206/// use ironflow_core::operation::Operation;
207///
208/// let client = DockerClient::connect_local().unwrap();
209/// let op = ContainerRestart::new(&client, "my-container");
210/// assert_eq!(op.kind(), "docker");
211/// ```
212pub struct ContainerRestart {
213    docker: Docker,
214    container: String,
215    timeout: Option<i64>,
216}
217
218impl ContainerRestart {
219    /// Create a new container-restart operation.
220    pub fn new(client: impl Into<DockerRef>, container: impl Into<String>) -> Self {
221        Self {
222            docker: client.into().0,
223            container: container.into(),
224            timeout: None,
225        }
226    }
227
228    /// Set the timeout in seconds before killing the container.
229    pub fn timeout(mut self, secs: i64) -> Self {
230        self.timeout = Some(secs);
231        self
232    }
233
234    /// Execute and return a typed result.
235    ///
236    /// # Errors
237    ///
238    /// Returns [`OperationError::External`] if the container does not exist.
239    pub async fn run(
240        &self,
241        _ctx: &OperationContext,
242    ) -> Result<ContainerRestartOutput, OperationError> {
243        let options = RestartContainerOptions {
244            t: Some(self.timeout.unwrap_or(10) as i32),
245            signal: None,
246        };
247        self.docker
248            .restart_container(&self.container, Some(options))
249            .await
250            .map_err(docker_error)?;
251        Ok(ContainerRestartOutput {
252            container: self.container.clone(),
253        })
254    }
255}
256
257#[async_trait]
258impl Operation for ContainerRestart {
259    fn kind(&self) -> &str {
260        "docker"
261    }
262
263    async fn execute(&self, ctx: &OperationContext) -> Result<Value, OperationError> {
264        to_value(&self.run(ctx).await?)
265    }
266
267    fn input(&self) -> Option<Value> {
268        Some(serde_json::json!({
269            "operation": "container_restart",
270            "container": self.container,
271        }))
272    }
273}
274
275impl TypedOperation for ContainerRestart {
276    type Output = ContainerRestartOutput;
277}