ironflow_ops_docker/containers/
manage.rs1use std::collections::HashMap;
4
5use async_trait::async_trait;
6use bollard::Docker;
7use bollard::query_parameters::{
8 InspectContainerOptions, KillContainerOptions, ListContainersOptions, RemoveContainerOptions,
9};
10use ironflow_core::error::OperationError;
11use ironflow_core::operation::{Operation, OperationContext, TypedOperation};
12use serde::{Deserialize, Serialize};
13use serde_json::Value;
14
15use crate::containers::DockerRef;
16use crate::helpers::{docker_error, to_value};
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct ContainerKillOutput {
25 pub container: String,
27}
28
29pub struct ContainerKill {
43 docker: Docker,
44 container: String,
45 signal: Option<String>,
46}
47
48impl ContainerKill {
49 pub fn new(client: impl Into<DockerRef>, container: impl Into<String>) -> Self {
51 Self {
52 docker: client.into().0,
53 container: container.into(),
54 signal: None,
55 }
56 }
57
58 pub fn signal(mut self, signal: impl Into<String>) -> Self {
60 self.signal = Some(signal.into());
61 self
62 }
63
64 pub async fn run(
70 &self,
71 _ctx: &OperationContext,
72 ) -> Result<ContainerKillOutput, OperationError> {
73 let options = KillContainerOptions {
74 signal: self.signal.clone().unwrap_or_else(|| "SIGKILL".to_string()),
75 };
76 self.docker
77 .kill_container(&self.container, Some(options))
78 .await
79 .map_err(docker_error)?;
80 Ok(ContainerKillOutput {
81 container: self.container.clone(),
82 })
83 }
84}
85
86#[async_trait]
87impl Operation for ContainerKill {
88 fn kind(&self) -> &str {
89 "docker"
90 }
91
92 async fn execute(&self, ctx: &OperationContext) -> Result<Value, OperationError> {
93 to_value(&self.run(ctx).await?)
94 }
95
96 fn input(&self) -> Option<Value> {
97 Some(serde_json::json!({
98 "operation": "container_kill",
99 "container": self.container,
100 }))
101 }
102}
103
104impl TypedOperation for ContainerKill {
105 type Output = ContainerKillOutput;
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
114pub struct ContainerRemoveOutput {
115 pub container: String,
117}
118
119pub struct ContainerRemove {
133 docker: Docker,
134 container: String,
135 force: bool,
136 volumes: bool,
137}
138
139impl ContainerRemove {
140 pub fn new(client: impl Into<DockerRef>, container: impl Into<String>) -> Self {
142 Self {
143 docker: client.into().0,
144 container: container.into(),
145 force: false,
146 volumes: false,
147 }
148 }
149
150 pub fn force(mut self) -> Self {
152 self.force = true;
153 self
154 }
155
156 pub fn volumes(mut self) -> Self {
158 self.volumes = true;
159 self
160 }
161
162 pub async fn run(
168 &self,
169 _ctx: &OperationContext,
170 ) -> Result<ContainerRemoveOutput, OperationError> {
171 let options = RemoveContainerOptions {
172 force: self.force,
173 v: self.volumes,
174 ..Default::default()
175 };
176 self.docker
177 .remove_container(&self.container, Some(options))
178 .await
179 .map_err(docker_error)?;
180 Ok(ContainerRemoveOutput {
181 container: self.container.clone(),
182 })
183 }
184}
185
186#[async_trait]
187impl Operation for ContainerRemove {
188 fn kind(&self) -> &str {
189 "docker"
190 }
191
192 async fn execute(&self, ctx: &OperationContext) -> Result<Value, OperationError> {
193 to_value(&self.run(ctx).await?)
194 }
195
196 fn input(&self) -> Option<Value> {
197 Some(serde_json::json!({
198 "operation": "container_remove",
199 "container": self.container,
200 "force": self.force,
201 }))
202 }
203}
204
205impl TypedOperation for ContainerRemove {
206 type Output = ContainerRemoveOutput;
207}
208
209#[derive(Debug, Clone, Serialize, Deserialize)]
215pub struct ContainerInspectOutput {
216 pub data: Value,
218}
219
220pub struct ContainerInspect {
234 docker: Docker,
235 container: String,
236}
237
238impl ContainerInspect {
239 pub fn new(client: impl Into<DockerRef>, container: impl Into<String>) -> Self {
241 Self {
242 docker: client.into().0,
243 container: container.into(),
244 }
245 }
246
247 pub async fn run(
253 &self,
254 _ctx: &OperationContext,
255 ) -> Result<ContainerInspectOutput, OperationError> {
256 let options = InspectContainerOptions { size: false };
257 let response = self
258 .docker
259 .inspect_container(&self.container, Some(options))
260 .await
261 .map_err(docker_error)?;
262 let data = serde_json::to_value(&response).map_err(|e| OperationError::External {
263 origin: "docker".to_string(),
264 message: e.to_string(),
265 })?;
266 Ok(ContainerInspectOutput { data })
267 }
268}
269
270#[async_trait]
271impl Operation for ContainerInspect {
272 fn kind(&self) -> &str {
273 "docker"
274 }
275
276 async fn execute(&self, ctx: &OperationContext) -> Result<Value, OperationError> {
277 to_value(&self.run(ctx).await?)
278 }
279
280 fn input(&self) -> Option<Value> {
281 Some(serde_json::json!({
282 "operation": "container_inspect",
283 "container": self.container,
284 }))
285 }
286}
287
288impl TypedOperation for ContainerInspect {
289 type Output = ContainerInspectOutput;
290}
291
292#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct ContainerListEntry {
299 pub id: String,
301 pub names: Vec<String>,
303 pub image: String,
305 pub state: String,
307 pub status: String,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
313pub struct ContainerListOutput {
314 pub containers: Vec<ContainerListEntry>,
316}
317
318pub struct ContainerList {
332 docker: Docker,
333 all: bool,
334 filters: HashMap<String, Vec<String>>,
335}
336
337impl ContainerList {
338 pub fn new(client: impl Into<DockerRef>) -> Self {
340 Self {
341 docker: client.into().0,
342 all: false,
343 filters: HashMap::new(),
344 }
345 }
346
347 pub fn all(mut self) -> Self {
349 self.all = true;
350 self
351 }
352
353 pub fn filter(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
355 self.filters
356 .entry(key.into())
357 .or_default()
358 .push(value.into());
359 self
360 }
361
362 pub async fn run(
369 &self,
370 _ctx: &OperationContext,
371 ) -> Result<ContainerListOutput, OperationError> {
372 let options = ListContainersOptions {
373 all: self.all,
374 filters: Some(self.filters.clone()),
375 ..Default::default()
376 };
377 let containers = self
378 .docker
379 .list_containers(Some(options))
380 .await
381 .map_err(docker_error)?;
382 let entries = containers
383 .into_iter()
384 .map(|c| ContainerListEntry {
385 id: c.id.unwrap_or_default(),
386 names: c.names.unwrap_or_default(),
387 image: c.image.unwrap_or_default(),
388 state: c.state.map(|s| s.to_string()).unwrap_or_default(),
389 status: c.status.unwrap_or_default(),
390 })
391 .collect();
392 Ok(ContainerListOutput {
393 containers: entries,
394 })
395 }
396}
397
398#[async_trait]
399impl Operation for ContainerList {
400 fn kind(&self) -> &str {
401 "docker"
402 }
403
404 async fn execute(&self, ctx: &OperationContext) -> Result<Value, OperationError> {
405 to_value(&self.run(ctx).await?)
406 }
407
408 fn input(&self) -> Option<Value> {
409 Some(serde_json::json!({
410 "operation": "container_list",
411 "all": self.all,
412 }))
413 }
414}
415
416impl TypedOperation for ContainerList {
417 type Output = ContainerListOutput;
418}