1use std::collections::HashMap;
2
3use crate::Pyo3Docker;
4use docker_api::opts::{NodeListOpts, NodeUpdateOpts};
5use docker_api::{Node, Nodes};
6use pyo3::exceptions;
7use pyo3::prelude::*;
8use pyo3::types::PyDict;
9use pythonize::pythonize;
10
11#[pymodule]
12pub fn node(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
13 m.add_class::<Pyo3Nodes>()?;
14 m.add_class::<Pyo3Node>()?;
15 Ok(())
16}
17
18#[derive(Debug)]
22#[pyclass(name = "Nodes")]
23pub struct Pyo3Nodes(pub Nodes);
24
25#[derive(Debug)]
29#[pyclass(name = "Node")]
30pub struct Pyo3Node(pub Node);
31
32#[pymethods]
33impl Pyo3Nodes {
34 #[new]
35 pub fn new(docker: Pyo3Docker) -> Self {
36 Pyo3Nodes(Nodes::new(docker.0))
37 }
38
39 pub fn get(&self, id: &str) -> Pyo3Node {
47 Pyo3Node(self.0.get(id))
48 }
49
50 pub fn list(&self) -> PyResult<Py<PyAny>> {
58 let rv = __nodes_list(&self.0, &Default::default());
59
60 match rv {
61 Ok(rv) => Ok(pythonize_this!(rv)),
62 Err(rv) => Err(py_sys_exception!(rv)),
63 }
64 }
65}
66
67#[tokio::main]
68async fn __nodes_list(
69 nodes: &Nodes,
70 opts: &NodeListOpts,
71) -> Result<Vec<docker_api::models::Node>, docker_api::Error> {
72 nodes.list(opts).await
73}
74
75#[pymethods]
76impl Pyo3Node {
77 #[new]
78 pub fn new(docker: Pyo3Docker, id: &str) -> Self {
79 Pyo3Node(Node::new(docker.0, id))
80 }
81
82 pub fn id(&self) -> String {
87 self.0.name().to_string()
88 }
89
90 pub fn inspect(&self) -> PyResult<Py<PyAny>> {
98 let rv = __node_inspect(&self.0);
99
100 match rv {
101 Ok(rv) => Ok(pythonize_this!(rv)),
102 Err(rv) => Err(py_sys_exception!(rv)),
103 }
104 }
105
106 pub fn delete(&self) -> PyResult<()> {
114 let rv = __node_delete(&self.0);
115 match rv {
116 Ok(rv) => Ok(rv),
117 Err(rv) => Err(py_sys_exception!(rv)),
118 }
119 }
120
121 pub fn force_delete(&self) -> PyResult<()> {
129 let rv = __node_force_delete(&self.0);
130 match rv {
131 Ok(rv) => Ok(rv),
132 Err(rv) => Err(py_sys_exception!(rv)),
133 }
134 }
135
136 #[pyo3(signature = (version, name=None, role=None, availability=None, labels=None))]
151 pub fn update(
152 &self,
153 version: &str,
154 name: Option<&str>,
155 role: Option<&str>,
156 availability: Option<&str>,
157 labels: Option<&Bound<'_, PyDict>>,
158 ) -> PyResult<()> {
159 use docker_api::models::{NodeSpecAvailabilityInlineItem, NodeSpecRoleInlineItem};
160
161 let rv = __node_update_with_params(
165 &self.0,
166 version,
167 name,
168 role.map(|r| match r.to_lowercase().as_str() {
169 "worker" => Ok(NodeSpecRoleInlineItem::Worker),
170 "manager" => Ok(NodeSpecRoleInlineItem::Manager),
171 _ => Err(exceptions::PyValueError::new_err(format!(
172 "Invalid role: {}. Must be 'worker' or 'manager'",
173 r
174 ))),
175 })
176 .transpose()?,
177 availability
178 .map(|a| match a.to_lowercase().as_str() {
179 "active" => Ok(NodeSpecAvailabilityInlineItem::Active),
180 "pause" => Ok(NodeSpecAvailabilityInlineItem::Pause),
181 "drain" => Ok(NodeSpecAvailabilityInlineItem::Drain),
182 _ => Err(exceptions::PyValueError::new_err(format!(
183 "Invalid availability: {}. Must be 'active', 'pause', or 'drain'",
184 a
185 ))),
186 })
187 .transpose()?,
188 labels.map(|l| l.extract::<HashMap<String, String>>().unwrap()),
189 );
190
191 match rv {
192 Ok(rv) => Ok(rv),
193 Err(rv) => Err(py_sys_exception!(rv)),
194 }
195 }
196}
197
198#[tokio::main]
199async fn __node_inspect(node: &Node) -> Result<docker_api::models::Node, docker_api::Error> {
200 node.inspect().await
201}
202
203#[tokio::main]
204async fn __node_delete(node: &Node) -> Result<(), docker_api::Error> {
205 node.delete().await
206}
207
208#[tokio::main]
209async fn __node_force_delete(node: &Node) -> Result<(), docker_api::Error> {
210 node.force_delete().await
211}
212
213use docker_api::models::{NodeSpecAvailabilityInlineItem, NodeSpecRoleInlineItem};
214
215#[tokio::main]
216async fn __node_update_with_params(
217 node: &Node,
218 version: &str,
219 name: Option<&str>,
220 role: Option<NodeSpecRoleInlineItem>,
221 availability: Option<NodeSpecAvailabilityInlineItem>,
222 labels: Option<HashMap<String, String>>,
223) -> Result<(), docker_api::Error> {
224 let empty_opts = NodeUpdateOpts::builder(version);
229
230 unsafe {
232 let opts: NodeUpdateOpts = std::mem::transmute(empty_opts);
233
234 let opts = if let Some(n) = name {
236 opts.name(n)
237 } else {
238 opts
239 };
240
241 let opts = if let Some(r) = role {
242 opts.role(r)
243 } else {
244 opts
245 };
246
247 let opts = if let Some(a) = availability {
248 opts.availability(a)
249 } else {
250 opts
251 };
252
253 let opts = if let Some(l) = labels {
254 opts.labels(l.iter().map(|(k, v)| (k.as_str(), v.as_str())))
255 } else {
256 opts
257 };
258
259 node.update(&opts).await
260 }
261}