docker_pyo3/
container.rs

1use chrono::{DateTime, Utc};
2use docker_api::conn::TtyChunk;
3use docker_api::models::{
4    ContainerInspect200Response, ContainerPrune200Response, ContainerSummary, ContainerWaitResponse,
5};
6use docker_api::opts::{
7    ContainerCreateOpts, ContainerListOpts, ContainerPruneOpts, ExecCreateOpts, LogsOpts,
8    PublishPort,
9};
10use docker_api::{Container, Containers};
11use futures_util::stream::StreamExt;
12use futures_util::TryStreamExt;
13use pyo3::exceptions;
14use pyo3::prelude::*;
15use pyo3::types::{PyDateTime, PyDelta, PyDict, PyList};
16use pythonize::pythonize;
17use std::{collections::HashMap, fs::File, io::Read};
18use tar::Archive;
19
20use crate::Pyo3Docker;
21
22#[pymodule]
23pub fn container(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
24    m.add_class::<Pyo3Containers>()?;
25    m.add_class::<Pyo3Container>()?;
26    Ok(())
27}
28
29#[derive(Debug)]
30#[pyclass(name = "Containers")]
31pub struct Pyo3Containers(pub Containers);
32
33#[derive(Debug)]
34#[pyclass(name = "Container")]
35pub struct Pyo3Container(pub Container);
36
37#[pymethods]
38impl Pyo3Containers {
39    #[new]
40    pub fn new(docker: Pyo3Docker) -> Self {
41        Pyo3Containers(Containers::new(docker.0))
42    }
43
44    fn get(&self, id: &str) -> Pyo3Container {
45        Pyo3Container(self.0.get(id))
46    }
47
48    #[pyo3(signature = (all=None, since=None, before=None, sized=None))]
49    fn list(
50        &self,
51        all: Option<bool>,
52        since: Option<String>,
53        before: Option<String>,
54        sized: Option<bool>,
55    ) -> Py<PyAny> {
56        let mut builder = ContainerListOpts::builder();
57
58        bo_setter!(all, builder);
59        bo_setter!(since, builder);
60        bo_setter!(before, builder);
61        bo_setter!(sized, builder);
62
63        let cs = __containers_list(&self.0, &builder.build());
64        pythonize_this!(cs)
65    }
66
67    fn prune(&self) -> PyResult<Py<PyAny>> {
68        let rv = __containers_prune(&self.0, &Default::default());
69
70        match rv {
71            Ok(rv) => Ok(pythonize_this!(rv)),
72            Err(rv) => Err(py_sys_exception!(rv)),
73        }
74    }
75    #[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))]
76    fn create(
77        &self,
78        image: &str,
79        attach_stderr: Option<bool>,
80        attach_stdin: Option<bool>,
81        attach_stdout: Option<bool>,
82        auto_remove: Option<bool>,
83        capabilities: Option<&Bound<'_, PyList>>,
84        command: Option<&Bound<'_, PyList>>,
85        cpu_shares: Option<u32>,
86        cpus: Option<f64>,
87        devices: Option<&Bound<'_, PyList>>,
88        entrypoint: Option<&Bound<'_, PyList>>,
89        env: Option<&Bound<'_, PyList>>,
90        expose: Option<&Bound<'_, PyList>>,
91        extra_hosts: Option<&Bound<'_, PyList>>,
92        labels: Option<&Bound<'_, PyDict>>,
93        links: Option<&Bound<'_, PyList>>,
94        log_driver: Option<&str>,
95        memory: Option<u64>,
96        memory_swap: Option<i64>,
97        name: Option<&str>,
98        nano_cpus: Option<u64>,
99        network_mode: Option<&str>,
100        privileged: Option<bool>,
101        publish: Option<&Bound<'_, PyList>>,
102        publish_all_ports: Option<bool>,
103        restart_policy: Option<&Bound<'_, PyDict>>, // name,maximum_retry_count,
104        security_options: Option<&Bound<'_, PyList>>,
105        stop_signal: Option<&str>,
106        stop_signal_num: Option<u64>,
107        stop_timeout: Option<&Bound<'_, PyDelta>>,
108        tty: Option<bool>,
109        user: Option<&str>,
110        userns_mode: Option<&str>,
111        volumes: Option<&Bound<'_, PyList>>,
112        volumes_from: Option<&Bound<'_, PyList>>,
113        working_dir: Option<&str>,
114    ) -> PyResult<Pyo3Container> {
115        let mut create_opts = ContainerCreateOpts::builder().image(image);
116
117        let links: Option<Vec<String>> = if links.is_some() {
118            links.unwrap().extract().unwrap()
119        } else {
120            None
121        };
122        let links: Option<Vec<&str>> = links
123            .as_ref()
124            .map(|v| v.iter().map(|s| s.as_str()).collect());
125
126        let capabilities_strings: Option<Vec<String>> = if capabilities.is_some() {
127            capabilities.unwrap().extract().unwrap()
128        } else {
129            None
130        };
131        let capabilities: Option<Vec<&str>> = capabilities_strings
132            .as_ref()
133            .map(|v| v.iter().map(|s| s.as_str()).collect());
134
135        let command_strings: Option<Vec<String>> = if command.is_some() {
136            command.unwrap().extract().unwrap()
137        } else {
138            None
139        };
140        let command: Option<Vec<&str>> = command_strings
141            .as_ref()
142            .map(|v| v.iter().map(|s| s.as_str()).collect());
143
144        let entrypoint_strings: Option<Vec<String>> = if entrypoint.is_some() {
145            entrypoint.unwrap().extract().unwrap()
146        } else {
147            None
148        };
149        let entrypoint: Option<Vec<&str>> = entrypoint_strings
150            .as_ref()
151            .map(|v| v.iter().map(|s| s.as_str()).collect());
152
153        let env_strings: Option<Vec<String>> = if env.is_some() {
154            env.unwrap().extract().unwrap()
155        } else {
156            None
157        };
158        let env: Option<Vec<&str>> = env_strings
159            .as_ref()
160            .map(|v| v.iter().map(|s| s.as_str()).collect());
161
162        let extra_hosts_strings: Option<Vec<String>> = if extra_hosts.is_some() {
163            extra_hosts.unwrap().extract().unwrap()
164        } else {
165            None
166        };
167        let extra_hosts: Option<Vec<&str>> = extra_hosts_strings
168            .as_ref()
169            .map(|v| v.iter().map(|s| s.as_str()).collect());
170
171        let security_options_strings: Option<Vec<String>> = if security_options.is_some() {
172            security_options.unwrap().extract().unwrap()
173        } else {
174            None
175        };
176        let security_options: Option<Vec<&str>> = security_options_strings
177            .as_ref()
178            .map(|v| v.iter().map(|s| s.as_str()).collect());
179
180        let volumes_strings: Option<Vec<String>> = if volumes.is_some() {
181            volumes.unwrap().extract().unwrap()
182        } else {
183            None
184        };
185        let volumes: Option<Vec<&str>> = volumes_strings
186            .as_ref()
187            .map(|v| v.iter().map(|s| s.as_str()).collect());
188
189        let volumes_from_strings: Option<Vec<String>> = if volumes_from.is_some() {
190            volumes_from.unwrap().extract().unwrap()
191        } else {
192            None
193        };
194        let volumes_from: Option<Vec<&str>> = volumes_from_strings
195            .as_ref()
196            .map(|v| v.iter().map(|s| s.as_str()).collect());
197
198        let devices_vec: Option<Vec<HashMap<String, String>>> = if devices.is_some() {
199            let list = devices.unwrap();
200            let mut result = Vec::new();
201            for item in list.iter() {
202                let dict: HashMap<String, String> = item.extract().unwrap();
203                result.push(dict);
204            }
205            Some(result)
206        } else {
207            None
208        };
209        let devices = devices_vec;
210
211        let labels_map: Option<HashMap<String, String>> = if labels.is_some() {
212            Some(labels.unwrap().extract().unwrap())
213        } else {
214            None
215        };
216        let labels: Option<HashMap<&str, &str>> = labels_map
217            .as_ref()
218            .map(|m| m.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect());
219
220        let stop_timeout_duration: Option<std::time::Duration> =
221            stop_timeout.map(|st| st.extract::<chrono::Duration>().unwrap().to_std().unwrap());
222        let stop_timeout = stop_timeout_duration;
223
224        bo_setter!(attach_stderr, create_opts);
225        bo_setter!(attach_stdin, create_opts);
226        bo_setter!(attach_stdout, create_opts);
227        bo_setter!(auto_remove, create_opts);
228        bo_setter!(cpu_shares, create_opts);
229        bo_setter!(cpus, create_opts);
230        bo_setter!(log_driver, create_opts);
231        bo_setter!(memory, create_opts);
232        bo_setter!(memory_swap, create_opts);
233        bo_setter!(name, create_opts);
234        bo_setter!(nano_cpus, create_opts);
235        bo_setter!(network_mode, create_opts);
236        bo_setter!(privileged, create_opts);
237        bo_setter!(stop_signal, create_opts);
238        bo_setter!(stop_signal_num, create_opts);
239        bo_setter!(tty, create_opts);
240        bo_setter!(user, create_opts);
241        bo_setter!(userns_mode, create_opts);
242        bo_setter!(working_dir, create_opts);
243
244        bo_setter!(devices, create_opts);
245        bo_setter!(links, create_opts);
246        bo_setter!(capabilities, create_opts);
247        bo_setter!(command, create_opts);
248        bo_setter!(entrypoint, create_opts);
249        bo_setter!(env, create_opts);
250        bo_setter!(extra_hosts, create_opts);
251        bo_setter!(security_options, create_opts);
252        bo_setter!(volumes, create_opts);
253        bo_setter!(volumes_from, create_opts);
254
255        bo_setter!(labels, create_opts);
256        bo_setter!(stop_timeout, create_opts);
257
258        // Handle expose - expects list of dicts like [{"srcport": 8080, "protocol": "tcp", "hostport": 8000}]
259        if let Some(expose_list) = expose {
260            for item in expose_list.iter() {
261                let port_dict: &Bound<'_, PyDict> = item.downcast()?;
262                let srcport: u32 = port_dict
263                    .get_item("srcport")?
264                    .expect("srcport required")
265                    .extract()?;
266                let hostport: u32 = port_dict
267                    .get_item("hostport")?
268                    .expect("hostport required")
269                    .extract()?;
270                let protocol: String = match port_dict.get_item("protocol")? {
271                    Some(p) => p.extract()?,
272                    None => "tcp".to_string(),
273                };
274
275                let publish_port = match protocol.as_str() {
276                    "tcp" => PublishPort::tcp(srcport),
277                    "udp" => PublishPort::udp(srcport),
278                    "sctp" => PublishPort::sctp(srcport),
279                    _ => {
280                        return Err(exceptions::PyValueError::new_err(format!(
281                            "unknown protocol: {}",
282                            protocol
283                        )))
284                    }
285                };
286
287                create_opts = create_opts.expose(publish_port, hostport);
288            }
289        }
290
291        // Handle publish - expects list of dicts like [{"port": 8080, "protocol": "tcp"}]
292        if let Some(publish_list) = publish {
293            for item in publish_list.iter() {
294                let port_dict: &Bound<'_, PyDict> = item.downcast()?;
295                let port: u32 = port_dict
296                    .get_item("port")?
297                    .expect("port required")
298                    .extract()?;
299                let protocol: String = match port_dict.get_item("protocol")? {
300                    Some(p) => p.extract()?,
301                    None => "tcp".to_string(),
302                };
303
304                let publish_port = match protocol.as_str() {
305                    "tcp" => PublishPort::tcp(port),
306                    "udp" => PublishPort::udp(port),
307                    "sctp" => PublishPort::sctp(port),
308                    _ => {
309                        return Err(exceptions::PyValueError::new_err(format!(
310                            "unknown protocol: {}",
311                            protocol
312                        )))
313                    }
314                };
315
316                create_opts = create_opts.publish(publish_port);
317            }
318        }
319
320        if publish_all_ports.is_some() && publish_all_ports.unwrap() {
321            create_opts = create_opts.publish_all_ports();
322        }
323
324        if restart_policy.is_some() {
325            let policy_dict = restart_policy.unwrap();
326            let name = policy_dict
327                .get_item("name")
328                .unwrap_or(None)
329                .expect("restart_policy requires 'name' key")
330                .extract::<String>()
331                .unwrap();
332            let max_retry = policy_dict
333                .get_item("maximum_retry_count")
334                .unwrap_or(None)
335                .map(|v| v.extract::<u64>().unwrap())
336                .unwrap_or(0);
337
338            create_opts = create_opts.restart_policy(&name, max_retry);
339        }
340
341        // bo_setter!(expose, create_opts);
342        // bo_setter!(publish, create_opts);
343
344        let rv = __containers_create(&self.0, &create_opts.build());
345        match rv {
346            Ok(rv) => Ok(Pyo3Container(rv)),
347            Err(rv) => Err(py_sys_exception!(rv)),
348        }
349    }
350}
351
352#[tokio::main]
353async fn __containers_list(
354    containers: &Containers,
355    opts: &ContainerListOpts,
356) -> Vec<ContainerSummary> {
357    let x = containers.list(opts).await;
358    x.unwrap()
359}
360
361#[tokio::main]
362async fn __containers_prune(
363    containers: &Containers,
364    opts: &ContainerPruneOpts,
365) -> Result<ContainerPrune200Response, docker_api::Error> {
366    containers.prune(opts).await
367}
368
369#[tokio::main]
370async fn __containers_create(
371    containers: &Containers,
372    opts: &ContainerCreateOpts,
373) -> Result<Container, docker_api::Error> {
374    containers.create(opts).await
375}
376
377#[pymethods]
378impl Pyo3Container {
379    #[new]
380    fn new(docker: Pyo3Docker, id: String) -> Self {
381        Pyo3Container(Container::new(docker.0, id))
382    }
383
384    fn id(&self) -> String {
385        self.0.id().to_string()
386    }
387
388    fn inspect(&self) -> PyResult<Py<PyAny>> {
389        let ci = __container_inspect(&self.0);
390        Ok(pythonize_this!(ci))
391    }
392    #[pyo3(signature = (stdout=None, stderr=None, timestamps=None, n_lines=None, all=None, since=None))]
393    fn logs(
394        &self,
395        stdout: Option<bool>,
396        stderr: Option<bool>,
397        timestamps: Option<bool>,
398        n_lines: Option<usize>,
399        all: Option<bool>,
400        since: Option<&Bound<'_, PyDateTime>>,
401    ) -> String {
402        let mut log_opts = LogsOpts::builder();
403
404        bo_setter!(stdout, log_opts);
405        bo_setter!(stderr, log_opts);
406        bo_setter!(timestamps, log_opts);
407        bo_setter!(n_lines, log_opts);
408
409        if all.is_some() && all.unwrap() {
410            // all needs to be called w/o a value
411            log_opts = log_opts.all();
412        }
413
414        if since.is_some() {
415            let rs_since: DateTime<Utc> = since.unwrap().extract().unwrap();
416            log_opts = log_opts.since(&rs_since);
417        }
418
419        __container_logs(&self.0, &log_opts.build())
420    }
421
422    fn remove(&self) -> PyResult<()> {
423        Err(exceptions::PyNotImplementedError::new_err(
424            "This method is not available yet.",
425        ))
426    }
427
428    fn delete(&self) -> PyResult<()> {
429        let rv = __container_delete(&self.0);
430        if rv.is_ok() {
431            Ok(())
432        } else {
433            Err(exceptions::PySystemError::new_err(
434                "Failed to delete container.",
435            ))
436        }
437    }
438
439    // fn top(&self) -> PyResult<()> {
440    //     Err(exceptions::PyNotImplementedError::new_err(
441    //         "This method is not available yet.",
442    //     ))
443    // }
444
445    // fn export(&self, docker_path: &str, local_path: &str) -> PyResult<()> {
446    //     let bytes = self.0.export();
447    //     let mut archive = Archive::new(&bytes[..]);
448    //     archive.unpack(local_path);
449
450    //     Ok(())
451    // }
452
453    fn start(&self) -> PyResult<()> {
454        let rv = __container_start(&self.0);
455
456        match rv {
457            Ok(_rv) => Ok(()),
458            Err(_rv) => Err(exceptions::PySystemError::new_err(
459                "Failed to start container",
460            )),
461        }
462    }
463
464    fn stop(&self, wait: Option<&Bound<'_, PyDelta>>) -> PyResult<()> {
465        let wait: Option<std::time::Duration> = wait.map(|wait| {
466            wait.extract::<chrono::Duration>()
467                .unwrap()
468                .to_std()
469                .unwrap()
470        });
471
472        let rv = __container_stop(&self.0, wait);
473        match rv {
474            Ok(_rv) => Ok(()),
475            Err(_rv) => Err(exceptions::PySystemError::new_err(
476                "Failed to start container",
477            )),
478        }
479    }
480
481    fn restart(&self, wait: Option<&Bound<'_, PyDelta>>) -> PyResult<()> {
482        let wait: Option<std::time::Duration> = wait.map(|wait| {
483            wait.extract::<chrono::Duration>()
484                .unwrap()
485                .to_std()
486                .unwrap()
487        });
488
489        let rv = __container_restart(&self.0, wait);
490        match rv {
491            Ok(_rv) => Ok(()),
492            Err(_rv) => Err(exceptions::PySystemError::new_err(
493                "Failed to stop container",
494            )),
495        }
496    }
497
498    fn kill(&self, signal: Option<&str>) -> PyResult<()> {
499        let rv = __container_kill(&self.0, signal);
500        match rv {
501            Ok(_rv) => Ok(()),
502            Err(_rv) => Err(exceptions::PySystemError::new_err(
503                "Failed to kill container",
504            )),
505        }
506    }
507
508    fn rename(&self, name: &str) -> PyResult<()> {
509        let rv = __container_rename(&self.0, name);
510        match rv {
511            Ok(_rv) => Ok(()),
512            Err(_rv) => Err(exceptions::PySystemError::new_err(
513                "Failed to rename container",
514            )),
515        }
516    }
517
518    fn pause(&self) -> PyResult<()> {
519        let rv = __container_pause(&self.0);
520        match rv {
521            Ok(_rv) => Ok(()),
522            Err(_rv) => Err(exceptions::PySystemError::new_err(
523                "Failed to pause container",
524            )),
525        }
526    }
527
528    fn unpause(&self) -> PyResult<()> {
529        let rv = __container_unpause(&self.0);
530        match rv {
531            Ok(_rv) => Ok(()),
532            Err(_rv) => Err(exceptions::PySystemError::new_err(
533                "Failed to unpause container",
534            )),
535        }
536    }
537
538    fn wait(&self) -> Py<PyAny> {
539        let rv = __container_wait(&self.0).unwrap();
540        pythonize_this!(rv)
541    }
542
543    fn exec(
544        &self,
545        command: &Bound<'_, PyList>,
546        env: Option<&Bound<'_, PyList>>,
547        attach_stdout: Option<bool>,
548        attach_stderr: Option<bool>,
549        detach_keys: Option<&str>,
550        tty: Option<bool>,
551        privileged: Option<bool>,
552        user: Option<&str>,
553        working_dir: Option<&str>,
554    ) -> PyResult<()> {
555        let command_strings: Vec<String> = command.extract().unwrap();
556        let command: Vec<&str> = command_strings.iter().map(|s| s.as_str()).collect();
557        let mut exec_opts = ExecCreateOpts::builder().command(command);
558
559        if env.is_some() {
560            let env_strings: Vec<String> = env.unwrap().extract().unwrap();
561            let env: Vec<&str> = env_strings.iter().map(|s| s.as_str()).collect();
562            exec_opts = exec_opts.env(env);
563        }
564
565        bo_setter!(attach_stdout, exec_opts);
566        bo_setter!(attach_stderr, exec_opts);
567        bo_setter!(tty, exec_opts);
568        bo_setter!(detach_keys, exec_opts);
569        bo_setter!(privileged, exec_opts);
570        bo_setter!(user, exec_opts);
571        bo_setter!(working_dir, exec_opts);
572
573        let rv = __container_exec(&self.0, exec_opts.build());
574        let rv = rv.unwrap();
575        match rv {
576            Ok(_rv) => Ok(()),
577            Err(rv) => Err(exceptions::PySystemError::new_err(format!(
578                "Failed to exec container {rv}"
579            ))),
580        }
581    }
582
583    fn copy_from(&self, src: &str, dst: &str) -> PyResult<()> {
584        let rv = __container_copy_from(&self.0, src);
585
586        match rv {
587            Ok(rv) => {
588                let mut archive = Archive::new(&rv[..]);
589                let r = archive.unpack(dst);
590                match r {
591                    Ok(_r) => Ok(()),
592                    Err(r) => Err(exceptions::PySystemError::new_err(format!("{r}"))),
593                }
594            }
595            Err(rv) => Err(exceptions::PySystemError::new_err(format!("{rv}"))),
596        }
597    }
598
599    fn copy_file_into(&self, src: &str, dst: &str) -> PyResult<()> {
600        let mut file = File::open(src).unwrap();
601        let mut bytes = Vec::new();
602        file.read_to_end(&mut bytes)
603            .expect("Cannot read file on the localhost.");
604
605        let rv = __container_copy_file_into(&self.0, dst, &bytes);
606
607        match rv {
608            Ok(_rv) => Ok(()),
609            Err(rv) => Err(exceptions::PySystemError::new_err(format!("{rv}"))),
610        }
611    }
612
613    fn stat_file(&self, path: &str) -> Py<PyAny> {
614        let rv = __container_stat_file(&self.0, path).unwrap();
615        pythonize_this!(rv)
616    }
617
618    fn commit(&self) -> PyResult<()> {
619        Err(exceptions::PyNotImplementedError::new_err(
620            "This method is not available yet.",
621        ))
622    }
623
624    fn __repr__(&self) -> String {
625        let inspect = __container_inspect(&self.0);
626        format!(
627            "Container(id: {}, name: {}, status: {})",
628            inspect.id.unwrap(),
629            inspect.name.unwrap(),
630            inspect.state.unwrap().status.unwrap()
631        )
632    }
633
634    fn __string__(&self) -> String {
635        self.__repr__()
636    }
637}
638
639#[tokio::main]
640async fn __container_inspect(container: &Container) -> ContainerInspect200Response {
641    let c = container.inspect().await;
642    c.unwrap()
643}
644
645#[tokio::main]
646async fn __container_logs(container: &Container, log_opts: &LogsOpts) -> String {
647    let log_stream = container.logs(log_opts);
648
649    let log = log_stream
650        .map(|chunk| match chunk {
651            Ok(chunk) => chunk.to_vec(),
652            Err(e) => {
653                eprintln!("Error: {e}");
654                vec![]
655            }
656        })
657        .collect::<Vec<_>>()
658        .await
659        .into_iter()
660        .flatten()
661        .collect::<Vec<_>>();
662
663    format!("{}", String::from_utf8_lossy(&log))
664}
665
666#[tokio::main]
667async fn __container_delete(container: &Container) -> Result<String, docker_api::Error> {
668    container.delete().await
669}
670
671#[tokio::main]
672async fn __container_start(container: &Container) -> Result<(), docker_api::Error> {
673    container.start().await
674}
675
676#[tokio::main]
677async fn __container_stop(
678    container: &Container,
679    wait: Option<std::time::Duration>,
680) -> Result<(), docker_api::Error> {
681    container.stop(wait).await
682}
683
684#[tokio::main]
685async fn __container_restart(
686    container: &Container,
687    wait: Option<std::time::Duration>,
688) -> Result<(), docker_api::Error> {
689    container.restart(wait).await
690}
691
692#[tokio::main]
693async fn __container_kill(
694    container: &Container,
695    signal: Option<&str>,
696) -> Result<(), docker_api::Error> {
697    container.kill(signal).await
698}
699
700#[tokio::main]
701async fn __container_rename(container: &Container, name: &str) -> Result<(), docker_api::Error> {
702    container.rename(name).await
703}
704
705#[tokio::main]
706async fn __container_pause(container: &Container) -> Result<(), docker_api::Error> {
707    container.pause().await
708}
709
710#[tokio::main]
711async fn __container_unpause(container: &Container) -> Result<(), docker_api::Error> {
712    container.unpause().await
713}
714
715#[tokio::main]
716async fn __container_wait(
717    container: &Container,
718) -> Result<ContainerWaitResponse, docker_api::Error> {
719    container.wait().await
720}
721
722#[tokio::main]
723async fn __container_exec(
724    container: &Container,
725    exec_opts: ExecCreateOpts,
726) -> Option<Result<TtyChunk, docker_api::conn::Error>> {
727    container.exec(&exec_opts).next().await
728}
729
730#[tokio::main]
731async fn __container_copy_from(
732    container: &Container,
733    path: &str,
734) -> Result<Vec<u8>, docker_api::Error> {
735    container.copy_from(path).try_concat().await
736}
737
738#[tokio::main]
739async fn __container_copy_file_into(
740    container: &Container,
741    dst: &str,
742    bytes: &Vec<u8>,
743) -> Result<(), docker_api::Error> {
744    container.copy_file_into(dst, bytes).await
745}
746
747#[tokio::main]
748async fn __container_stat_file(
749    container: &Container,
750    src: &str,
751) -> Result<String, docker_api::Error> {
752    container.stat_file(src).await
753}