docker_pyo3/
task.rs

1use crate::Pyo3Docker;
2use chrono::{DateTime, Utc};
3use docker_api::opts::LogsOpts;
4use docker_api::task::TaskListOpts;
5use docker_api::{Task, Tasks};
6use futures_util::stream::StreamExt;
7use pyo3::exceptions;
8use pyo3::prelude::*;
9use pyo3::types::PyDateTime;
10use pythonize::pythonize;
11
12#[pymodule]
13pub fn task(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
14    m.add_class::<Pyo3Tasks>()?;
15    m.add_class::<Pyo3Task>()?;
16    Ok(())
17}
18
19/// Interface for managing Docker Swarm tasks collection.
20///
21/// Tasks are individual units of work running on swarm nodes as part of a service.
22/// Swarm mode must be enabled for these operations to work.
23#[derive(Debug)]
24#[pyclass(name = "Tasks")]
25pub struct Pyo3Tasks {
26    tasks: Tasks,
27}
28
29/// Represents an individual Docker Swarm task.
30///
31/// A task is a container instance running as part of a swarm service.
32/// Swarm mode must be enabled for these operations to work.
33#[derive(Debug)]
34#[pyclass(name = "Task")]
35pub struct Pyo3Task {
36    task: Task,
37}
38
39#[pymethods]
40impl Pyo3Tasks {
41    #[new]
42    pub fn new(docker: Pyo3Docker) -> Self {
43        Pyo3Tasks {
44            tasks: Tasks::new(docker.0),
45        }
46    }
47
48    /// Get a specific task by ID.
49    ///
50    /// Args:
51    ///     id: Task ID
52    ///
53    /// Returns:
54    ///     Task: Task instance
55    pub fn get(&self, id: &str) -> Pyo3Task {
56        Pyo3Task {
57            task: self.tasks.get(id),
58        }
59    }
60
61    /// List all tasks in the swarm.
62    ///
63    /// Returns:
64    ///     list[dict]: List of task information dictionaries
65    ///
66    /// Raises:
67    ///     SystemError: If the operation fails (e.g., swarm not initialized)
68    pub fn list(&self) -> PyResult<Py<PyAny>> {
69        let rv = __tasks_list(&self.tasks, &Default::default());
70
71        match rv {
72            Ok(rv) => Ok(pythonize_this!(rv)),
73            Err(rv) => Err(py_sys_exception!(rv)),
74        }
75    }
76}
77
78#[tokio::main]
79async fn __tasks_list(
80    tasks: &Tasks,
81    opts: &TaskListOpts,
82) -> Result<Vec<docker_api::models::Task>, docker_api::Error> {
83    tasks.list(opts).await
84}
85
86#[pymethods]
87impl Pyo3Task {
88    #[new]
89    pub fn new(docker: Pyo3Docker, id: &str) -> Self {
90        Pyo3Task {
91            task: Task::new(docker.0, id),
92        }
93    }
94
95    /// Get the task ID.
96    ///
97    /// Returns:
98    ///     str: Task ID
99    pub fn id(&self) -> String {
100        self.task.id().to_string()
101    }
102
103    /// Inspect the task to get detailed information.
104    ///
105    /// Returns:
106    ///     dict: Detailed task information including status, spec, assigned node, etc.
107    ///
108    /// Raises:
109    ///     SystemError: If the operation fails
110    pub fn inspect(&self) -> PyResult<Py<PyAny>> {
111        let rv = __task_inspect(&self.task);
112
113        match rv {
114            Ok(rv) => Ok(pythonize_this!(rv)),
115            Err(rv) => Err(py_sys_exception!(rv)),
116        }
117    }
118
119    /// Get task logs.
120    ///
121    /// Args:
122    ///     stdout: Include stdout
123    ///     stderr: Include stderr
124    ///     timestamps: Include timestamps
125    ///     n_lines: Number of lines to return from the end of logs
126    ///     all: Return all logs
127    ///     since: Only return logs since this datetime
128    ///
129    /// Returns:
130    ///     str: Task logs
131    #[pyo3(signature = (stdout=None, stderr=None, timestamps=None, n_lines=None, all=None, since=None))]
132    pub fn logs(
133        &self,
134        stdout: Option<bool>,
135        stderr: Option<bool>,
136        timestamps: Option<bool>,
137        n_lines: Option<usize>,
138        all: Option<bool>,
139        since: Option<&Bound<'_, PyDateTime>>,
140    ) -> String {
141        let mut log_opts = LogsOpts::builder();
142
143        bo_setter!(stdout, log_opts);
144        bo_setter!(stderr, log_opts);
145        bo_setter!(timestamps, log_opts);
146        bo_setter!(n_lines, log_opts);
147
148        if all.is_some() && all.unwrap() {
149            log_opts = log_opts.all();
150        }
151
152        if since.is_some() {
153            let rs_since: DateTime<Utc> = since.unwrap().extract().unwrap();
154            log_opts = log_opts.since(&rs_since);
155        }
156
157        __task_logs(&self.task, &log_opts.build())
158    }
159}
160
161#[tokio::main]
162async fn __task_inspect(task: &Task) -> Result<docker_api::models::Task, docker_api::Error> {
163    task.inspect().await
164}
165
166#[tokio::main]
167async fn __task_logs(task: &Task, log_opts: &LogsOpts) -> String {
168    let log_stream = task.logs(log_opts);
169
170    let log = log_stream
171        .map(|chunk| match chunk {
172            Ok(chunk) => chunk.to_vec(),
173            Err(e) => {
174                eprintln!("Error: {e}");
175                vec![]
176            }
177        })
178        .collect::<Vec<_>>()
179        .await
180        .into_iter()
181        .flatten()
182        .collect::<Vec<_>>();
183
184    format!("{}", String::from_utf8_lossy(&log))
185}