1use chrono::{DateTime, Utc};
2use docker_api::conn::TtyChunk;
3use docker_api::models::{
4 ContainerChanges200Response, ContainerInspect200Response, ContainerPrune200Response,
5 ContainerSummary, ContainerTop200Response, ContainerWaitResponse,
6};
7use docker_api::opts::{
8 ContainerCommitOpts, ContainerCreateOpts, ContainerListOpts, ContainerPruneOpts,
9 ContainerRestartOpts, ContainerStopOpts, ExecCreateOpts, ExecStartOpts, LogsOpts, PublishPort,
10};
11use docker_api::{Container, Containers, Exec};
12use futures_util::stream::StreamExt;
13use futures_util::TryStreamExt;
14use pyo3::exceptions;
15use pyo3::prelude::*;
16use pyo3::types::{PyDateTime, PyDelta, PyDict, PyList};
17use pythonize::pythonize;
18use std::{collections::HashMap, fs::File, io::Read};
19use tar::Archive;
20
21use crate::Pyo3Docker;
22
23#[pymodule]
24pub fn container(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
25 m.add_class::<Pyo3Containers>()?;
26 m.add_class::<Pyo3Container>()?;
27 Ok(())
28}
29
30#[derive(Debug)]
32#[pyclass(name = "Containers")]
33pub struct Pyo3Containers {
34 containers: Containers,
35 docker: docker_api::Docker,
36}
37
38#[derive(Debug)]
40#[pyclass(name = "Container")]
41pub struct Pyo3Container {
42 container: Container,
43 docker: docker_api::Docker,
44}
45
46#[pymethods]
47impl Pyo3Containers {
48 #[new]
49 pub fn new(docker: Pyo3Docker) -> Self {
50 Pyo3Containers {
51 containers: Containers::new(docker.0.clone()),
52 docker: docker.0,
53 }
54 }
55
56 fn get(&self, id: &str) -> Pyo3Container {
64 Pyo3Container {
65 container: self.containers.get(id),
66 docker: self.docker.clone(),
67 }
68 }
69
70 #[pyo3(signature = (all=None, since=None, before=None, sized=None))]
81 fn list(
82 &self,
83 all: Option<bool>,
84 since: Option<String>,
85 before: Option<String>,
86 sized: Option<bool>,
87 ) -> Py<PyAny> {
88 let mut builder = ContainerListOpts::builder();
89
90 bo_setter!(all, builder);
91 bo_setter!(since, builder);
92 bo_setter!(before, builder);
93 bo_setter!(sized, builder);
94
95 let cs = __containers_list(&self.containers, &builder.build());
96 pythonize_this!(cs)
97 }
98
99 fn prune(&self) -> PyResult<Py<PyAny>> {
104 let rv = __containers_prune(&self.containers, &Default::default());
105
106 match rv {
107 Ok(rv) => Ok(pythonize_this!(rv)),
108 Err(rv) => Err(py_sys_exception!(rv)),
109 }
110 }
111
112 #[pyo3(signature = (image, *, attach_stderr=None, attach_stdin=None, attach_stdout=None, auto_remove=None, capabilities=None, command=None, cpu_shares=None, cpus=None, devices=None, entrypoint=None, env=None, expose=None, extra_hosts=None, labels=None, links=None, log_driver=None, memory=None, memory_swap=None, name=None, nano_cpus=None, network_mode=None, privileged=None, publish=None, publish_all_ports=None, restart_policy=None, security_options=None, stop_signal=None, stop_signal_num=None, stop_timeout=None, tty=None, user=None, userns_mode=None, volumes=None, volumes_from=None, working_dir=None))]
155 fn create(
156 &self,
157 image: &str,
158 attach_stderr: Option<bool>,
159 attach_stdin: Option<bool>,
160 attach_stdout: Option<bool>,
161 auto_remove: Option<bool>,
162 capabilities: Option<&Bound<'_, PyList>>,
163 command: Option<&Bound<'_, PyList>>,
164 cpu_shares: Option<u32>,
165 cpus: Option<f64>,
166 devices: Option<&Bound<'_, PyList>>,
167 entrypoint: Option<&Bound<'_, PyList>>,
168 env: Option<&Bound<'_, PyList>>,
169 expose: Option<&Bound<'_, PyList>>,
170 extra_hosts: Option<&Bound<'_, PyList>>,
171 labels: Option<&Bound<'_, PyDict>>,
172 links: Option<&Bound<'_, PyList>>,
173 log_driver: Option<&str>,
174 memory: Option<u64>,
175 memory_swap: Option<i64>,
176 name: Option<&str>,
177 nano_cpus: Option<u64>,
178 network_mode: Option<&str>,
179 privileged: Option<bool>,
180 publish: Option<&Bound<'_, PyList>>,
181 publish_all_ports: Option<bool>,
182 restart_policy: Option<&Bound<'_, PyDict>>, security_options: Option<&Bound<'_, PyList>>,
184 stop_signal: Option<&str>,
185 stop_signal_num: Option<u64>,
186 stop_timeout: Option<&Bound<'_, PyDelta>>,
187 tty: Option<bool>,
188 user: Option<&str>,
189 userns_mode: Option<&str>,
190 volumes: Option<&Bound<'_, PyList>>,
191 volumes_from: Option<&Bound<'_, PyList>>,
192 working_dir: Option<&str>,
193 ) -> PyResult<Pyo3Container> {
194 let mut create_opts = ContainerCreateOpts::builder().image(image);
195
196 let links: Option<Vec<String>> = if links.is_some() {
197 links.unwrap().extract().unwrap()
198 } else {
199 None
200 };
201 let links: Option<Vec<&str>> = links
202 .as_ref()
203 .map(|v| v.iter().map(|s| s.as_str()).collect());
204
205 let capabilities_strings: Option<Vec<String>> = if capabilities.is_some() {
206 capabilities.unwrap().extract().unwrap()
207 } else {
208 None
209 };
210 let capabilities: Option<Vec<&str>> = capabilities_strings
211 .as_ref()
212 .map(|v| v.iter().map(|s| s.as_str()).collect());
213
214 let command_strings: Option<Vec<String>> = if command.is_some() {
215 command.unwrap().extract().unwrap()
216 } else {
217 None
218 };
219 let command: Option<Vec<&str>> = command_strings
220 .as_ref()
221 .map(|v| v.iter().map(|s| s.as_str()).collect());
222
223 let entrypoint_strings: Option<Vec<String>> = if entrypoint.is_some() {
224 entrypoint.unwrap().extract().unwrap()
225 } else {
226 None
227 };
228 let entrypoint: Option<Vec<&str>> = entrypoint_strings
229 .as_ref()
230 .map(|v| v.iter().map(|s| s.as_str()).collect());
231
232 let env_strings: Option<Vec<String>> = if env.is_some() {
233 env.unwrap().extract().unwrap()
234 } else {
235 None
236 };
237 let env: Option<Vec<&str>> = env_strings
238 .as_ref()
239 .map(|v| v.iter().map(|s| s.as_str()).collect());
240
241 let extra_hosts_strings: Option<Vec<String>> = if extra_hosts.is_some() {
242 extra_hosts.unwrap().extract().unwrap()
243 } else {
244 None
245 };
246 let extra_hosts: Option<Vec<&str>> = extra_hosts_strings
247 .as_ref()
248 .map(|v| v.iter().map(|s| s.as_str()).collect());
249
250 let security_options_strings: Option<Vec<String>> = if security_options.is_some() {
251 security_options.unwrap().extract().unwrap()
252 } else {
253 None
254 };
255 let security_options: Option<Vec<&str>> = security_options_strings
256 .as_ref()
257 .map(|v| v.iter().map(|s| s.as_str()).collect());
258
259 let volumes_strings: Option<Vec<String>> = if volumes.is_some() {
260 volumes.unwrap().extract().unwrap()
261 } else {
262 None
263 };
264 let volumes: Option<Vec<&str>> = volumes_strings
265 .as_ref()
266 .map(|v| v.iter().map(|s| s.as_str()).collect());
267
268 let volumes_from_strings: Option<Vec<String>> = if volumes_from.is_some() {
269 volumes_from.unwrap().extract().unwrap()
270 } else {
271 None
272 };
273 let volumes_from: Option<Vec<&str>> = volumes_from_strings
274 .as_ref()
275 .map(|v| v.iter().map(|s| s.as_str()).collect());
276
277 let devices_vec: Option<Vec<HashMap<String, String>>> = if devices.is_some() {
278 let list = devices.unwrap();
279 let mut result = Vec::new();
280 for item in list.iter() {
281 let dict: HashMap<String, String> = item.extract().unwrap();
282 result.push(dict);
283 }
284 Some(result)
285 } else {
286 None
287 };
288 let devices = devices_vec;
289
290 let labels_map: Option<HashMap<String, String>> = if labels.is_some() {
291 Some(labels.unwrap().extract().unwrap())
292 } else {
293 None
294 };
295 let labels: Option<HashMap<&str, &str>> = labels_map
296 .as_ref()
297 .map(|m| m.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect());
298
299 let stop_timeout_duration: Option<std::time::Duration> =
300 stop_timeout.map(|st| st.extract::<chrono::Duration>().unwrap().to_std().unwrap());
301 let stop_timeout = stop_timeout_duration;
302
303 bo_setter!(attach_stderr, create_opts);
304 bo_setter!(attach_stdin, create_opts);
305 bo_setter!(attach_stdout, create_opts);
306 bo_setter!(auto_remove, create_opts);
307 bo_setter!(cpu_shares, create_opts);
308 bo_setter!(cpus, create_opts);
309 bo_setter!(log_driver, create_opts);
310 bo_setter!(memory, create_opts);
311 bo_setter!(memory_swap, create_opts);
312 bo_setter!(name, create_opts);
313 bo_setter!(nano_cpus, create_opts);
314 bo_setter!(network_mode, create_opts);
315 bo_setter!(privileged, create_opts);
316 bo_setter!(stop_signal, create_opts);
317 bo_setter!(stop_signal_num, create_opts);
318 bo_setter!(tty, create_opts);
319 bo_setter!(user, create_opts);
320 bo_setter!(userns_mode, create_opts);
321 bo_setter!(working_dir, create_opts);
322
323 bo_setter!(devices, create_opts);
324 bo_setter!(links, create_opts);
325 bo_setter!(capabilities, create_opts);
326 bo_setter!(command, create_opts);
327 bo_setter!(entrypoint, create_opts);
328 bo_setter!(env, create_opts);
329 bo_setter!(extra_hosts, create_opts);
330 bo_setter!(security_options, create_opts);
331 bo_setter!(volumes, create_opts);
332 bo_setter!(volumes_from, create_opts);
333
334 bo_setter!(labels, create_opts);
335 bo_setter!(stop_timeout, create_opts);
336
337 if let Some(expose_list) = expose {
339 for item in expose_list.iter() {
340 let port_dict: &Bound<'_, PyDict> = item.cast()?;
341 let srcport: u32 = port_dict
342 .get_item("srcport")?
343 .expect("srcport required")
344 .extract()?;
345 let hostport: u32 = port_dict
346 .get_item("hostport")?
347 .expect("hostport required")
348 .extract()?;
349 let protocol: String = match port_dict.get_item("protocol")? {
350 Some(p) => p.extract()?,
351 None => "tcp".to_string(),
352 };
353
354 let publish_port = match protocol.as_str() {
355 "tcp" => PublishPort::tcp(srcport),
356 "udp" => PublishPort::udp(srcport),
357 "sctp" => PublishPort::sctp(srcport),
358 _ => {
359 return Err(exceptions::PyValueError::new_err(format!(
360 "unknown protocol: {}",
361 protocol
362 )))
363 }
364 };
365
366 create_opts = create_opts.expose(publish_port, hostport);
367 }
368 }
369
370 if let Some(publish_list) = publish {
372 for item in publish_list.iter() {
373 let port_dict: &Bound<'_, PyDict> = item.cast()?;
374 let port: u32 = port_dict
375 .get_item("port")?
376 .expect("port required")
377 .extract()?;
378 let protocol: String = match port_dict.get_item("protocol")? {
379 Some(p) => p.extract()?,
380 None => "tcp".to_string(),
381 };
382
383 let publish_port = match protocol.as_str() {
384 "tcp" => PublishPort::tcp(port),
385 "udp" => PublishPort::udp(port),
386 "sctp" => PublishPort::sctp(port),
387 _ => {
388 return Err(exceptions::PyValueError::new_err(format!(
389 "unknown protocol: {}",
390 protocol
391 )))
392 }
393 };
394
395 create_opts = create_opts.publish(publish_port);
396 }
397 }
398
399 if publish_all_ports.is_some() && publish_all_ports.unwrap() {
400 create_opts = create_opts.publish_all_ports();
401 }
402
403 if restart_policy.is_some() {
404 let policy_dict = restart_policy.unwrap();
405 let name = policy_dict
406 .get_item("name")
407 .unwrap_or(None)
408 .expect("restart_policy requires 'name' key")
409 .extract::<String>()
410 .unwrap();
411 let max_retry = policy_dict
412 .get_item("maximum_retry_count")
413 .unwrap_or(None)
414 .map(|v| v.extract::<u64>().unwrap())
415 .unwrap_or(0);
416
417 create_opts = create_opts.restart_policy(&name, max_retry);
418 }
419
420 let rv = __containers_create(&self.containers, &create_opts.build());
424 match rv {
425 Ok(container) => Ok(Pyo3Container {
426 container,
427 docker: self.docker.clone(),
428 }),
429 Err(rv) => Err(py_sys_exception!(rv)),
430 }
431 }
432}
433
434#[tokio::main]
435async fn __containers_list(
436 containers: &Containers,
437 opts: &ContainerListOpts,
438) -> Vec<ContainerSummary> {
439 let x = containers.list(opts).await;
440 x.unwrap()
441}
442
443#[tokio::main]
444async fn __containers_prune(
445 containers: &Containers,
446 opts: &ContainerPruneOpts,
447) -> Result<ContainerPrune200Response, docker_api::Error> {
448 containers.prune(opts).await
449}
450
451#[tokio::main]
452async fn __containers_create(
453 containers: &Containers,
454 opts: &ContainerCreateOpts,
455) -> Result<Container, docker_api::Error> {
456 containers.create(opts).await
457}
458
459#[pymethods]
460impl Pyo3Container {
461 #[new]
462 fn new(docker: Pyo3Docker, id: String) -> Self {
463 Pyo3Container {
464 container: Container::new(docker.0.clone(), id),
465 docker: docker.0,
466 }
467 }
468
469 fn id(&self) -> String {
474 self.container.id().to_string()
475 }
476
477 fn inspect(&self) -> PyResult<Py<PyAny>> {
482 let ci = __container_inspect(&self.container);
483 Ok(pythonize_this!(ci))
484 }
485
486 #[pyo3(signature = (stdout=None, stderr=None, timestamps=None, n_lines=None, all=None, since=None))]
499 fn logs(
500 &self,
501 stdout: Option<bool>,
502 stderr: Option<bool>,
503 timestamps: Option<bool>,
504 n_lines: Option<usize>,
505 all: Option<bool>,
506 since: Option<&Bound<'_, PyDateTime>>,
507 ) -> String {
508 let mut log_opts = LogsOpts::builder();
509
510 bo_setter!(stdout, log_opts);
511 bo_setter!(stderr, log_opts);
512 bo_setter!(timestamps, log_opts);
513 bo_setter!(n_lines, log_opts);
514
515 if all.is_some() && all.unwrap() {
516 log_opts = log_opts.all();
518 }
519
520 if since.is_some() {
521 let rs_since: DateTime<Utc> = since.unwrap().extract().unwrap();
522 log_opts = log_opts.since(&rs_since);
523 }
524
525 __container_logs(&self.container, &log_opts.build())
526 }
527
528 fn remove(&self) -> PyResult<()> {
530 Err(exceptions::PyNotImplementedError::new_err(
531 "This method is not available yet.",
532 ))
533 }
534
535 fn delete(&self) -> PyResult<()> {
543 let rv = __container_delete(&self.container);
544 if rv.is_ok() {
545 Ok(())
546 } else {
547 Err(exceptions::PySystemError::new_err(
548 "Failed to delete container.",
549 ))
550 }
551 }
552
553 fn start(&self) -> PyResult<()> {
575 let rv = __container_start(&self.container);
576
577 match rv {
578 Ok(_rv) => Ok(()),
579 Err(_rv) => Err(exceptions::PySystemError::new_err(
580 "Failed to start container",
581 )),
582 }
583 }
584
585 fn stop(&self, wait: Option<&Bound<'_, PyDelta>>) -> PyResult<()> {
596 let wait: Option<std::time::Duration> = wait.map(|wait| {
597 wait.extract::<chrono::Duration>()
598 .unwrap()
599 .to_std()
600 .unwrap()
601 });
602
603 let rv = __container_stop(&self.container, wait);
604 match rv {
605 Ok(_rv) => Ok(()),
606 Err(_rv) => Err(exceptions::PySystemError::new_err(
607 "Failed to start container",
608 )),
609 }
610 }
611
612 fn restart(&self, wait: Option<&Bound<'_, PyDelta>>) -> PyResult<()> {
623 let wait: Option<std::time::Duration> = wait.map(|wait| {
624 wait.extract::<chrono::Duration>()
625 .unwrap()
626 .to_std()
627 .unwrap()
628 });
629
630 let rv = __container_restart(&self.container, wait);
631 match rv {
632 Ok(_rv) => Ok(()),
633 Err(_rv) => Err(exceptions::PySystemError::new_err(
634 "Failed to stop container",
635 )),
636 }
637 }
638
639 fn kill(&self, signal: Option<&str>) -> PyResult<()> {
650 let rv = __container_kill(&self.container, signal);
651 match rv {
652 Ok(_rv) => Ok(()),
653 Err(_rv) => Err(exceptions::PySystemError::new_err(
654 "Failed to kill container",
655 )),
656 }
657 }
658
659 fn rename(&self, name: &str) -> PyResult<()> {
670 let rv = __container_rename(&self.container, name);
671 match rv {
672 Ok(_rv) => Ok(()),
673 Err(_rv) => Err(exceptions::PySystemError::new_err(
674 "Failed to rename container",
675 )),
676 }
677 }
678
679 fn pause(&self) -> PyResult<()> {
687 let rv = __container_pause(&self.container);
688 match rv {
689 Ok(_rv) => Ok(()),
690 Err(_rv) => Err(exceptions::PySystemError::new_err(
691 "Failed to pause container",
692 )),
693 }
694 }
695
696 fn unpause(&self) -> PyResult<()> {
704 let rv = __container_unpause(&self.container);
705 match rv {
706 Ok(_rv) => Ok(()),
707 Err(_rv) => Err(exceptions::PySystemError::new_err(
708 "Failed to unpause container",
709 )),
710 }
711 }
712
713 fn wait(&self) -> Py<PyAny> {
718 let rv = __container_wait(&self.container).unwrap();
719 pythonize_this!(rv)
720 }
721
722 #[pyo3(signature = (stream=None))]
734 fn stats(&self, stream: Option<bool>) -> PyResult<Py<PyAny>> {
735 let stream = stream.unwrap_or(false);
736 let rv = __container_stats(&self.container, stream);
737 match rv {
738 Ok(rv) => Ok(pythonize_this!(rv)),
739 Err(rv) => Err(py_sys_exception!(rv)),
740 }
741 }
742
743 fn attach(&self) -> PyResult<String> {
754 let rv = __container_attach(&self.container);
755 match rv {
756 Ok(rv) => Ok(rv),
757 Err(rv) => Err(py_sys_exception!(rv)),
758 }
759 }
760
761 fn changes(&self) -> PyResult<Py<PyAny>> {
772 let rv = __container_changes(&self.container);
773 match rv {
774 Ok(rv) => Ok(pythonize_this!(rv)),
775 Err(rv) => Err(py_sys_exception!(rv)),
776 }
777 }
778
779 fn export(&self, path: &str) -> PyResult<()> {
790 let rv = __container_export(&self.container);
791 match rv {
792 Ok(bytes) => {
793 use std::io::Write;
794 let mut file = File::create(path)
795 .map_err(|e| exceptions::PySystemError::new_err(format!("{e}")))?;
796 file.write_all(&bytes)
797 .map_err(|e| exceptions::PySystemError::new_err(format!("{e}")))?;
798 Ok(())
799 }
800 Err(rv) => Err(py_sys_exception!(rv)),
801 }
802 }
803
804 #[pyo3(signature = (ps_args=None))]
815 fn top(&self, ps_args: Option<&str>) -> PyResult<Py<PyAny>> {
816 let rv = __container_top(&self.container, ps_args);
817 match rv {
818 Ok(rv) => Ok(pythonize_this!(rv)),
819 Err(rv) => Err(py_sys_exception!(rv)),
820 }
821 }
822
823 fn exec(
842 &self,
843 command: &Bound<'_, PyList>,
844 env: Option<&Bound<'_, PyList>>,
845 attach_stdout: Option<bool>,
846 attach_stderr: Option<bool>,
847 detach_keys: Option<&str>,
848 tty: Option<bool>,
849 privileged: Option<bool>,
850 user: Option<&str>,
851 working_dir: Option<&str>,
852 ) -> PyResult<()> {
853 let command_strings: Vec<String> = command.extract().unwrap();
854 let command: Vec<&str> = command_strings.iter().map(|s| s.as_str()).collect();
855 let mut exec_opts = ExecCreateOpts::builder().command(command);
856
857 if env.is_some() {
858 let env_strings: Vec<String> = env.unwrap().extract().unwrap();
859 let env: Vec<&str> = env_strings.iter().map(|s| s.as_str()).collect();
860 exec_opts = exec_opts.env(env);
861 }
862
863 bo_setter!(attach_stdout, exec_opts);
864 bo_setter!(attach_stderr, exec_opts);
865 bo_setter!(tty, exec_opts);
866 bo_setter!(detach_keys, exec_opts);
867 bo_setter!(privileged, exec_opts);
868 bo_setter!(user, exec_opts);
869 bo_setter!(working_dir, exec_opts);
870
871 let rv = __container_exec(&self.container, exec_opts.build());
872 let rv = rv.unwrap();
873 match rv {
874 Ok(_rv) => Ok(()),
875 Err(rv) => Err(exceptions::PySystemError::new_err(format!(
876 "Failed to exec container {rv}"
877 ))),
878 }
879 }
880
881 #[pyo3(signature = (command, env=None, attach_stdout=None, attach_stderr=None, detach_keys=None, tty=None, privileged=None, user=None, working_dir=None))]
909 fn exec_create(
910 &self,
911 command: &Bound<'_, PyList>,
912 env: Option<&Bound<'_, PyList>>,
913 attach_stdout: Option<bool>,
914 attach_stderr: Option<bool>,
915 detach_keys: Option<&str>,
916 tty: Option<bool>,
917 privileged: Option<bool>,
918 user: Option<&str>,
919 working_dir: Option<&str>,
920 ) -> PyResult<String> {
921 let command_strings: Vec<String> = command.extract().unwrap();
922 let command: Vec<&str> = command_strings.iter().map(|s| s.as_str()).collect();
923 let mut exec_opts = ExecCreateOpts::builder().command(command);
924
925 if env.is_some() {
926 let env_strings: Vec<String> = env.unwrap().extract().unwrap();
927 let env: Vec<&str> = env_strings.iter().map(|s| s.as_str()).collect();
928 exec_opts = exec_opts.env(env);
929 }
930
931 bo_setter!(attach_stdout, exec_opts);
932 bo_setter!(attach_stderr, exec_opts);
933 bo_setter!(tty, exec_opts);
934 bo_setter!(detach_keys, exec_opts);
935 bo_setter!(privileged, exec_opts);
936 bo_setter!(user, exec_opts);
937 bo_setter!(working_dir, exec_opts);
938
939 let rv = __container_exec_create(
940 self.docker.clone(),
941 self.container.id().as_ref(),
942 exec_opts.build(),
943 );
944 match rv {
945 Ok(exec_id) => Ok(exec_id),
946 Err(rv) => Err(py_sys_exception!(rv)),
947 }
948 }
949
950 fn copy_from(&self, src: &str, dst: &str) -> PyResult<()> {
951 let rv = __container_copy_from(&self.container, src);
952
953 match rv {
954 Ok(rv) => {
955 let mut archive = Archive::new(&rv[..]);
956 let r = archive.unpack(dst);
957 match r {
958 Ok(_r) => Ok(()),
959 Err(r) => Err(exceptions::PySystemError::new_err(format!("{r}"))),
960 }
961 }
962 Err(rv) => Err(exceptions::PySystemError::new_err(format!("{rv}"))),
963 }
964 }
965
966 fn copy_file_into(&self, src: &str, dst: &str) -> PyResult<()> {
967 let mut file = File::open(src).unwrap();
968 let mut bytes = Vec::new();
969 file.read_to_end(&mut bytes)
970 .expect("Cannot read file on the localhost.");
971
972 let rv = __container_copy_file_into(&self.container, dst, &bytes);
973
974 match rv {
975 Ok(_rv) => Ok(()),
976 Err(rv) => Err(exceptions::PySystemError::new_err(format!("{rv}"))),
977 }
978 }
979
980 fn stat_file(&self, path: &str) -> Py<PyAny> {
981 let rv = __container_stat_file(&self.container, path).unwrap();
982 pythonize_this!(rv)
983 }
984
985 #[pyo3(signature = (repo=None, tag=None, comment=None, author=None, pause=None, changes=None))]
1001 fn commit(
1002 &self,
1003 repo: Option<&str>,
1004 tag: Option<&str>,
1005 comment: Option<&str>,
1006 author: Option<&str>,
1007 pause: Option<bool>,
1008 changes: Option<&str>,
1009 ) -> PyResult<String> {
1010 let mut opts = ContainerCommitOpts::builder();
1011
1012 bo_setter!(repo, opts);
1013 bo_setter!(tag, opts);
1014 bo_setter!(comment, opts);
1015 bo_setter!(author, opts);
1016 bo_setter!(pause, opts);
1017 bo_setter!(changes, opts);
1018
1019 let rv = __container_commit(&self.container, &opts.build());
1020 match rv {
1021 Ok(rv) => Ok(rv),
1022 Err(rv) => Err(py_sys_exception!(rv)),
1023 }
1024 }
1025
1026 fn __repr__(&self) -> String {
1027 let inspect = __container_inspect(&self.container);
1028 format!(
1029 "Container(id: {}, name: {}, status: {})",
1030 inspect.id.unwrap(),
1031 inspect.name.unwrap(),
1032 inspect.state.unwrap().status.unwrap()
1033 )
1034 }
1035
1036 fn __string__(&self) -> String {
1037 self.__repr__()
1038 }
1039}
1040
1041#[tokio::main]
1042async fn __container_inspect(container: &Container) -> ContainerInspect200Response {
1043 let c = container.inspect().await;
1044 c.unwrap()
1045}
1046
1047#[tokio::main]
1048async fn __container_logs(container: &Container, log_opts: &LogsOpts) -> String {
1049 let log_stream = container.logs(log_opts);
1050
1051 let log = log_stream
1052 .map(|chunk| match chunk {
1053 Ok(chunk) => chunk.to_vec(),
1054 Err(e) => {
1055 eprintln!("Error: {e}");
1056 vec![]
1057 }
1058 })
1059 .collect::<Vec<_>>()
1060 .await
1061 .into_iter()
1062 .flatten()
1063 .collect::<Vec<_>>();
1064
1065 format!("{}", String::from_utf8_lossy(&log))
1066}
1067
1068#[tokio::main]
1069async fn __container_delete(container: &Container) -> Result<String, docker_api::Error> {
1070 container.delete().await
1071}
1072
1073#[tokio::main]
1074async fn __container_start(container: &Container) -> Result<(), docker_api::Error> {
1075 container.start().await
1076}
1077
1078#[tokio::main]
1079async fn __container_stop(
1080 container: &Container,
1081 wait: Option<std::time::Duration>,
1082) -> Result<(), docker_api::Error> {
1083 let mut opts = ContainerStopOpts::builder();
1084 if let Some(w) = wait {
1085 opts = opts.wait(w);
1086 }
1087 container.stop(&opts.build()).await
1088}
1089
1090#[tokio::main]
1091async fn __container_restart(
1092 container: &Container,
1093 wait: Option<std::time::Duration>,
1094) -> Result<(), docker_api::Error> {
1095 let mut opts = ContainerRestartOpts::builder();
1096 if let Some(w) = wait {
1097 opts = opts.wait(w);
1098 }
1099 container.restart(&opts.build()).await
1100}
1101
1102#[tokio::main]
1103async fn __container_kill(
1104 container: &Container,
1105 signal: Option<&str>,
1106) -> Result<(), docker_api::Error> {
1107 container.kill(signal).await
1108}
1109
1110#[tokio::main]
1111async fn __container_rename(container: &Container, name: &str) -> Result<(), docker_api::Error> {
1112 container.rename(name).await
1113}
1114
1115#[tokio::main]
1116async fn __container_pause(container: &Container) -> Result<(), docker_api::Error> {
1117 container.pause().await
1118}
1119
1120#[tokio::main]
1121async fn __container_unpause(container: &Container) -> Result<(), docker_api::Error> {
1122 container.unpause().await
1123}
1124
1125#[tokio::main]
1126async fn __container_wait(
1127 container: &Container,
1128) -> Result<ContainerWaitResponse, docker_api::Error> {
1129 container.wait().await
1130}
1131
1132#[tokio::main]
1133async fn __container_stats(
1134 container: &Container,
1135 stream: bool,
1136) -> Result<serde_json::Value, docker_api::Error> {
1137 let mut stats_stream = container.stats();
1138
1139 if stream {
1140 let mut stats_vec: Vec<serde_json::Value> = Vec::new();
1142 let mut count = 0;
1143 while let Some(stat_result) = stats_stream.next().await {
1144 match stat_result {
1145 Ok(stat) => {
1146 stats_vec.push(stat);
1147 count += 1;
1148 if count >= 10 {
1149 break;
1151 }
1152 }
1153 Err(e) => return Err(e),
1154 }
1155 }
1156 Ok(serde_json::Value::Array(stats_vec))
1157 } else {
1158 match stats_stream.next().await {
1160 Some(Ok(stat)) => Ok(stat),
1161 Some(Err(e)) => Err(e),
1162 None => Ok(serde_json::Value::Null),
1163 }
1164 }
1165}
1166
1167#[tokio::main]
1168async fn __container_attach(container: &Container) -> Result<String, docker_api::Error> {
1169 use futures_util::StreamExt;
1170
1171 let mut multiplexer = container.attach().await?;
1172 let mut output = Vec::new();
1173
1174 let mut count = 0;
1176 while let Some(chunk_result) = multiplexer.next().await {
1177 match chunk_result {
1178 Ok(chunk) => {
1179 output.extend_from_slice(&chunk.to_vec());
1180 count += 1;
1181 if count >= 100 {
1182 break;
1184 }
1185 }
1186 Err(_) => break,
1187 }
1188 }
1189
1190 Ok(String::from_utf8_lossy(&output).to_string())
1191}
1192
1193#[tokio::main]
1194async fn __container_changes(
1195 container: &Container,
1196) -> Result<Option<ContainerChanges200Response>, docker_api::Error> {
1197 container.changes().await
1198}
1199
1200#[tokio::main]
1201async fn __container_export(container: &Container) -> Result<Vec<u8>, docker_api::Error> {
1202 container.export().try_concat().await
1203}
1204
1205#[tokio::main]
1206async fn __container_top(
1207 container: &Container,
1208 ps_args: Option<&str>,
1209) -> Result<ContainerTop200Response, docker_api::Error> {
1210 container.top(ps_args).await
1211}
1212
1213#[tokio::main]
1214async fn __container_commit(
1215 container: &Container,
1216 opts: &ContainerCommitOpts,
1217) -> Result<String, docker_api::Error> {
1218 container.commit(opts, None).await
1219}
1220
1221#[tokio::main]
1222async fn __container_exec(
1223 container: &Container,
1224 exec_opts: ExecCreateOpts,
1225) -> Option<Result<TtyChunk, docker_api::conn::Error>> {
1226 let start_opts = ExecStartOpts::builder().build();
1227 match container.exec(&exec_opts, &start_opts).await {
1228 Ok(mut multiplexer) => multiplexer.next().await,
1229 Err(_) => None,
1230 }
1231}
1232
1233#[tokio::main]
1234async fn __container_copy_from(
1235 container: &Container,
1236 path: &str,
1237) -> Result<Vec<u8>, docker_api::Error> {
1238 container.copy_from(path).try_concat().await
1239}
1240
1241#[tokio::main]
1242async fn __container_copy_file_into(
1243 container: &Container,
1244 dst: &str,
1245 bytes: &Vec<u8>,
1246) -> Result<(), docker_api::Error> {
1247 container.copy_file_into(dst, bytes).await
1248}
1249
1250#[tokio::main]
1251async fn __container_stat_file(
1252 container: &Container,
1253 src: &str,
1254) -> Result<String, docker_api::Error> {
1255 container.stat_file(src).await
1256}
1257
1258#[tokio::main]
1259async fn __container_exec_create(
1260 docker: docker_api::Docker,
1261 container_id: &str,
1262 exec_opts: ExecCreateOpts,
1263) -> Result<String, docker_api::Error> {
1264 let exec = Exec::create(docker, container_id, &exec_opts).await?;
1266 let inspect = exec.inspect().await?;
1268 Ok(inspect.id.unwrap_or_default())
1269}