podman_api/api/
containers.rs

1use crate::{
2    api::{ApiResource, Exec},
3    conn::{tty, Headers, Payload},
4    models, opts, Result, Stream, TryStreamExt, Value,
5};
6
7use containers_api::url;
8use std::path::Path;
9
10impl_api_ty!(
11    Container => id
12);
13
14impl Container {
15    api_doc! {
16    Container => StartLibpod
17    |
18    /// Start this container.
19    ///
20    /// Parameters:
21    ///  * detach_keys - Override the key sequence for detaching a container. Format is a single
22    ///                  character [a-Z] or ctrl- where is one of: a-z, @, ^, [, , or _.
23    ///
24    /// Examples:
25    ///
26    /// ```no_run
27    /// async {
28    ///     use podman_api::Podman;
29    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
30    ///
31    ///     if let Err(e) = podman.containers().get("79c93f220e3e").start(None).await {
32    ///         eprintln!("{}", e);
33    ///     }
34    /// };
35    /// ```
36    pub async fn start(&self, detach_keys: Option<String>) -> Result<()> {
37        let ep = url::construct_ep(
38            format!("/libpod/containers/{}/start", &self.id),
39            detach_keys.map(|d| url::encoded_pair("detachKeys", d)),
40        );
41        self.podman
42            .post(&ep, Payload::empty(), Headers::none())
43            .await
44            .map(|_| ())
45    }}
46
47    api_doc! {
48    Container => StopLibpod
49    |
50    /// Stop this container.
51    ///
52    /// Examples:
53    ///
54    /// ```no_run
55    /// async {
56    ///     use podman_api::Podman;
57    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
58    ///
59    ///     if let Err(e) = podman.containers().get("79c93f220e3e").stop(&Default::default()).await {
60    ///         eprintln!("{}", e);
61    ///     }
62    /// };
63    /// ```
64    pub async fn stop(&self, opts: &opts::ContainerStopOpts) -> Result<()> {
65        let ep = url::construct_ep(
66            format!("/libpod/containers/{}/stop", &self.id),
67            opts.serialize(),
68        );
69        self.podman
70            .post(&ep, Payload::empty(), Headers::none())
71            .await
72            .map(|_| ())
73    }}
74
75    api_doc! {
76    Container => InspectLibpod
77    |
78    /// Return low-level information about this container.
79    ///
80    /// Examples:
81    ///
82    /// ```no_run
83    /// async {
84    ///     use podman_api::Podman;
85    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
86    ///
87    ///     match podman.containers().get("79c93f220e3e").inspect().await {
88    ///         Ok(info) => println!("{:?}", info),
89    ///         Err(e) => eprintln!("{}", e),
90    ///     }
91    /// };
92    /// ```
93    pub async fn inspect(&self) -> Result<models::ContainerInspectResponseLibpod> {
94        let ep = url::construct_ep(
95            format!("/libpod/containers/{}/json", &self.id),
96            Some(url::encoded_pair("size", "true")),
97        );
98        self.podman.get_json(&ep).await
99    }}
100
101    api_doc! {
102    Container => KillLibpod
103    |
104    /// Send a signal to this container.
105    ///
106    /// Examples:
107    ///
108    /// ```no_run
109    /// async {
110    ///     use podman_api::Podman;
111    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
112    ///
113    ///     if let Err(e) = podman.containers().get("79c93f220e3e").send_signal("INT").await {
114    ///         eprintln!("{}", e);
115    ///     }
116    /// };
117    /// ```
118    pub async fn send_signal(&self, signal: impl Into<String>) -> Result<()> {
119        let signal = signal.into();
120        let base_path = format!("/libpod/containers/{}/kill", &self.id);
121        let ep = if signal.is_empty() {
122            base_path
123        } else {
124            url::construct_ep(base_path, Some(url::encoded_pair("signal", signal)))
125        };
126        self.podman
127            .post(&ep, Payload::empty(), Headers::none())
128            .await
129            .map(|_| ())
130    }}
131
132    api_doc! {
133    Container => KillLibpod
134    |
135    /// Kill this container.
136    ///
137    /// Examples:
138    ///
139    /// ```no_run
140    /// async {
141    ///     use podman_api::Podman;
142    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
143    ///
144    ///     if let Err(e) = podman.containers().get("79c93f220e3e").kill().await {
145    ///         eprintln!("{}", e);
146    ///     }
147    /// };
148    /// ```
149    pub async fn kill(&self) -> Result<()> {
150        self.send_signal("").await
151    }}
152
153    api_doc! {
154    Container => PauseLibpod
155    |
156    /// Use the cgroups freezer to suspend all processes in this container.
157    ///
158    /// Examples:
159    ///
160    /// ```no_run
161    /// async {
162    ///     use podman_api::Podman;
163    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
164    ///
165    ///     if let Err(e) = podman.containers().get("79c93f220e3e").pause().await {
166    ///         eprintln!("{}", e);
167    ///     }
168    /// };
169    /// ```
170    pub async fn pause(&self) -> Result<()> {
171        self.podman
172            .post(
173                &format!("/libpod/containers/{}/pause", &self.id),
174                Payload::empty(),
175                Headers::none(),
176            )
177            .await
178            .map(|_| ())
179    }}
180
181    api_doc! {
182    Container => UnpauseLibpod
183    |
184    /// Unpause this container
185    ///
186    /// Examples:
187    ///
188    /// ```no_run
189    /// async {
190    ///     use podman_api::Podman;
191    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
192    ///
193    ///     if let Err(e) = podman.containers().get("79c93f220e3e").unpause().await {
194    ///         eprintln!("{}", e);
195    ///     }
196    /// };
197    /// ```
198    pub async fn unpause(&self) -> Result<()> {
199        self.podman
200            .post(
201                &format!("/libpod/containers/{}/unpause", &self.id),
202                Payload::empty(),
203                Headers::none(),
204            )
205            .await
206            .map(|_| ())
207    }}
208
209    api_doc! {
210    Container => RestartLibpod
211    |
212    /// Restart this container with a timeout.
213    ///
214    /// Parameters:
215    ///  * t - number of seconds to wait before killing the container
216    ///
217    /// Examples:
218    ///
219    /// ```no_run
220    /// async {
221    ///     use podman_api::Podman;
222    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
223    ///
224    ///     if let Err(e) = podman.containers().get("79c93f220e3e").restart_with_timeout(20).await {
225    ///         eprintln!("{}", e);
226    ///     }
227    /// };
228    /// ```
229    pub async fn restart_with_timeout(&self, t: usize) -> Result<()> {
230        let ep = url::construct_ep(
231            format!("/libpod/containers/{}/restart", &self.id),
232            Some(url::encoded_pair("t", t.to_string())),
233        );
234        self.podman
235            .post(&ep, Payload::empty(), Headers::none())
236            .await
237            .map(|_| ())
238    }}
239
240    api_doc! {
241    Container => RestartLibpod
242    |
243    /// Restart this container.
244    ///
245    /// Examples:
246    ///
247    /// ```no_run
248    /// async {
249    ///     use podman_api::Podman;
250    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
251    ///
252    ///     if let Err(e) = podman.containers().get("79c93f220e3e").restart().await {
253    ///         eprintln!("{}", e);
254    ///     }
255    /// };
256    /// ```
257    pub async fn restart(&self) -> Result<()> {
258        let ep = format!("/libpod/containers/{}/restart", &self.id);
259        self.podman
260            .post(&ep, Payload::empty(), Headers::none())
261            .await
262            .map(|_| ())
263    }}
264
265    api_doc! {
266    Container => DeleteLibpod
267    |
268    /// Delete this container.
269    ///
270    /// Examples:
271    ///
272    /// ```no_run
273    /// async {
274    ///     use podman_api::Podman;
275    ///     use podman_api::opts::ContainerDeleteOpts;
276    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
277    ///
278    ///     if let Err(e) = podman
279    ///         .containers()
280    ///         .get("79c93f220e3e")
281    ///         .delete(&ContainerDeleteOpts::builder().volumes(true).build())
282    ///         .await
283    ///     {
284    ///         eprintln!("{}", e);
285    ///     }
286    /// };
287    /// ```
288    pub async fn delete(&self, opts: &opts::ContainerDeleteOpts) -> Result<()> {
289        let ep = url::construct_ep(format!("/libpod/containers/{}", &self.id), opts.serialize());
290        self.podman.delete(&ep).await.map(|_| ())
291    }}
292
293    api_doc! {
294    Container => DeleteLibpod
295    |
296    /// Force remove this container.
297    ///
298    /// Examples:
299    ///
300    /// ```no_run
301    /// async {
302    ///     use podman_api::Podman;
303    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
304    ///
305    ///     if let Err(e) = podman.containers().get("79c93f220e3e").remove().await {
306    ///         eprintln!("{}", e);
307    ///     }
308    /// };
309    /// ```
310    pub async fn remove(&self) -> Result<()> {
311        self.delete(&opts::ContainerDeleteOpts::builder().force(true).build())
312            .await
313    }}
314
315    api_doc! {
316    Container => MountLibpod
317    |
318    /// Mount this container to the filesystem.
319    ///
320    /// Examples:
321    ///
322    /// ```no_run
323    /// async {
324    ///     use podman_api::Podman;
325    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
326    ///
327    ///     match podman.containers().get("79c93f220e3e").mount().await {
328    ///         Ok(path) => println!("mounted container path {}", path.display()),
329    ///         Err(e) => eprintln!("{}", e),
330    ///     }
331    /// };
332    /// ```
333    pub async fn mount(&self) -> Result<std::path::PathBuf> {
334        self.podman
335            .post_string(
336                &format!("/libpod/containers/{}/mount", &self.id),
337                Payload::empty(),
338                Headers::none(),
339            )
340            .await
341            .map(|resp| resp.trim().into())
342    }}
343
344    api_doc! {
345    Container => UnmountLibpod
346    |
347    /// Unmount this container from the filesystem.
348    ///
349    /// Examples:
350    ///
351    /// ```no_run
352    /// async {
353    ///     use podman_api::Podman;
354    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
355    ///
356    ///     if let Err(e) = podman.containers().get("79c93f220e3e").unmount().await {
357    ///         eprintln!("{}", e);
358    ///     }
359    /// };
360    /// ```
361    pub async fn unmount(&self) -> Result<()> {
362        self.podman
363            .post(
364                &format!("/libpod/containers/{}/unmount", &self.id),
365                Payload::empty(),
366                Headers::none(),
367            )
368            .await
369            .map(|_| ())
370    }}
371
372    api_doc! {
373    Container => CheckpointLibpod
374    |
375    /// Checkpoint this container returning the checkpoint image in a tar.gz.
376    ///
377    /// Examples:
378    ///
379    /// ```no_run
380    /// use futures_util::StreamExt;
381    /// async {
382    ///     use podman_api::Podman;
383    ///     use podman_api::opts::ContainerCheckpointOpts;
384    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
385    ///
386    ///     let container = podman.containers().get("79c93f220e3e");
387    ///     let mut export_stream = container.checkpoint_export(
388    ///         &ContainerCheckpointOpts::builder()
389    ///             .print_stats(true)
390    ///             .build(),
391    ///     );
392    ///
393    ///     while let Some(tar_gz_chunk) = export_stream.next().await {
394    ///         println!("{:?}", tar_gz_chunk);
395    ///     }
396    /// };
397    /// ```
398    pub fn checkpoint_export(
399        &self,
400        opts: &opts::ContainerCheckpointOpts,
401    ) -> impl Stream<Item = Result<Vec<u8>>> + Unpin + '_ {
402        let ep = url::construct_ep(
403            format!("/libpod/containers/{}/checkpoint", &self.id),
404            opts.for_export().serialize(),
405        );
406        Box::pin(
407            self.podman
408                .post_stream(ep, Payload::empty(), Headers::none())
409                .map_ok(|c| c.to_vec()),
410        )
411    }}
412
413    api_doc! {
414    Container => CheckpointLibpod
415    |
416    /// Checkpoint this container.
417    ///
418    /// Examples:
419    ///
420    /// ```no_run
421    /// use futures_util::StreamExt;
422    /// async {
423    ///     use podman_api::Podman;
424    ///     use podman_api::opts::ContainerCheckpointOpts;
425    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
426    ///
427    ///     let container = podman.containers().get("79c93f220e3e");
428    ///     let mut export_stream = container.checkpoint_export(
429    ///         &ContainerCheckpointOpts::builder()
430    ///             .print_stats(true)
431    ///             .build(),
432    ///     );
433    ///
434    ///     while let Some(tarball_chunk) = export_stream.next().await {
435    ///         println!("{:?}", tarball_chunk);
436    ///     }
437    /// };
438    /// ```
439    pub async fn checkpoint(
440        &self,
441        opts: &opts::ContainerCheckpointOpts,
442    ) -> Result<Value> {
443        let ep = url::construct_ep(
444            format!("/libpod/containers/{}/checkpoint", &self.id),
445            opts.serialize(),
446        );
447        self.podman
448            .post_json(&ep, Payload::empty(), Headers::none())
449            .await
450    }}
451
452    api_doc! {
453    Container => ImageCommitLibpod
454    |
455    /// Create a new image from this container.
456    ///
457    /// Examples:
458    ///
459    /// ```no_run
460    /// async {
461    ///     use podman_api::Podman;
462    ///     use podman_api::opts::ContainerCommitOpts;
463    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
464    ///
465    ///     if let Err(e) = podman
466    ///         .containers()
467    ///         .get("79c93f220e3e")
468    ///         .commit(
469    ///             &ContainerCommitOpts::builder()
470    ///                 .pause(true)
471    ///                 .repo("image-name")
472    ///                 .tag("1.0.0")
473    ///                 .build(),
474    ///         )
475    ///         .await
476    ///     {
477    ///         eprintln!("{}", e);
478    ///     }
479    /// };
480    /// ```
481    pub async fn commit(&self, opts: &opts::ContainerCommitOpts) -> Result<()> {
482        let opts = opts.for_container(self.id.clone());
483        let ep = url::construct_ep("/libpod/commit", opts.serialize());
484        self.podman
485            .post(&ep, Payload::empty(), Headers::none())
486            .await
487            .map(|_| ())
488    }}
489
490    api_doc! {
491    Container => ExecLibpod
492    |
493    /// Create an exec session to run a command inside this container. Exec sessions will be
494    /// automatically removed 5 minutes after they exit.
495    ///
496    /// This endpoint only creates the exec. To start it use [Exec::start](Exec::start).
497    ///
498    /// Examples:
499    ///
500    /// ```no_run
501    /// async {
502    ///     use podman_api::Podman;
503    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
504    ///
505    ///     let exec = podman
506    ///     .containers()
507    ///     .get("79c93f220e3e")
508    ///     .create_exec(
509    ///         &podman_api::opts::ExecCreateOpts::builder()
510    ///             .command(["cat", "/some/path/in/container"])
511    ///             .attach_stdout(true)
512    ///             .attach_stderr(true)
513    ///             .build(),
514    ///     )
515    ///     .await
516    ///     .unwrap();
517    /// };
518    /// ```
519    pub async fn create_exec(&self, opts: &opts::ExecCreateOpts) -> Result<Exec> {
520        let ep = format!("/libpod/containers/{}/exec", self.id);
521
522        self.podman
523            .post_json(&ep, Payload::Json(opts.serialize_vec()?), Headers::none())
524            .await
525            .map(|resp: models::IdResponse| {
526                let is_tty = opts.params.get("Tty").and_then(|v| v.as_bool()).unwrap_or_default();
527                if is_tty {
528                    Exec::new_tty(self.podman.clone(), resp.id)
529                } else {
530                    Exec::new_raw(self.podman.clone(), resp.id)
531                }
532            })
533    }}
534
535    api_doc! {
536    Container => RenameLibpod
537    |
538    /// Change the name of this container.
539    ///
540    /// Parameters:
541    ///  * new_name - new name to give for this container
542    ///
543    /// Examples:
544    ///
545    /// ```no_run
546    /// async {
547    ///     use podman_api::Podman;
548    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
549    ///
550    ///     if let Err(e) = podman.containers().get("79c93f220e3e").rename("my-container").await {
551    ///         eprintln!("{}", e);
552    ///     }
553    /// };
554    /// ```
555    pub async fn rename(&self, new_name: impl AsRef<str>) -> Result<()> {
556        let ep = url::construct_ep(
557            format!("/libpod/containers/{}/rename", &self.id),
558            Some(url::encoded_pair("name", new_name.as_ref())),
559        );
560        self.podman
561            .post(&ep, Payload::empty(), Headers::none())
562            .await
563            .map(|_| ())
564    }}
565
566    api_doc! {
567    Container => InitLibpod
568    |
569    /// Performs all tasks necessary for initializing the container but does not start the container.
570    ///
571    /// Examples:
572    ///
573    /// ```no_run
574    /// async {
575    ///     use podman_api::Podman;
576    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
577    ///
578    ///     if let Err(e) = podman.containers().get("79c93f220e3e").init().await {
579    ///         eprintln!("{}", e);
580    ///     }
581    /// };
582    /// ```
583    pub async fn init(&self) -> Result<()> {
584        self.podman
585            .post(
586                &format!("/libpod/containers/{}/init", &self.id),
587                Payload::empty(),
588                Headers::none(),
589            )
590            .await
591            .map(|_| ())
592    }}
593
594    api_doc! {
595    Container => WaitLibpod
596    |
597    /// Wait for this container to meet a given condition.
598    ///
599    /// Examples:
600    ///
601    /// ```no_run
602    /// async {
603    ///     use podman_api::Podman;
604    ///     use podman_api::opts::ContainerWaitOpts;
605    ///     use podman_api::models::ContainerStatus;
606    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
607    ///
608    ///     if let Err(e) = podman
609    ///         .containers()
610    ///         .get("79c93f220e3e")
611    ///         .wait(
612    ///             &ContainerWaitOpts::builder()
613    ///                 .conditions([ContainerStatus::Configured])
614    ///                 .interval("300ms")
615    ///                 .build(),
616    ///         )
617    ///         .await
618    ///     {
619    ///         eprintln!("{}", e);
620    ///     }
621    /// };
622    /// ```
623    pub async fn wait(&self, opts: &opts::ContainerWaitOpts) -> Result<()> {
624        let ep = url::construct_ep(
625            format!("/libpod/containers/{}/wait", &self.id),
626            opts.serialize(),
627        );
628        self.podman
629            .post(&ep, Payload::empty(), Headers::none())
630            .await
631            .map(|_| ())
632    }}
633
634    api_doc! {
635    Container => ExistsLibpod
636    |
637    /// Quick way to determine if a container exists by name or ID
638    ///
639    /// Examples:
640    ///
641    /// ```no_run
642    /// async {
643    ///     use podman_api::Podman;
644    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
645    ///
646    ///     match podman.containers().get("79c93f220e3e").exists().await {
647    ///         Ok(exists) => if exists {
648    ///             println!("container exists!");
649    ///         } else {
650    ///             println!("container doesn't exists!");
651    ///         },
652    ///         Err(e) => eprintln!("check failed: {}", e),
653    ///     }
654    /// };
655    /// ```
656    pub async fn exists(&self) -> Result<bool> {
657        self.podman
658            .resource_exists(ApiResource::Containers, &self.id)
659            .await
660    }}
661
662    api_doc! {
663    Container => AttachLibpod
664    |
665    /// Attach to this container.
666    ///
667    /// Examples:
668    ///
669    /// ```no_run
670    /// use futures_util::StreamExt;
671    /// async {
672    ///     use podman_api::Podman;
673    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
674    ///     let container = podman.containers().get("79c93f220e3e");
675    ///     let tty_multiplexer = container
676    ///             .attach(&Default::default())
677    ///             .await
678    ///             .unwrap();
679    ///     let (mut reader, _writer) = tty_multiplexer.split();
680    ///
681    ///     while let Some(tty_result) = reader.next().await {
682    ///         match tty_result {
683    ///             Ok(chunk) => println!("{:?}", chunk),
684    ///             Err(e) => eprintln!("Error: {}", e),
685    ///         }
686    ///     }
687    /// };
688    /// ```
689    pub async fn attach(&self, opts: &opts::ContainerAttachOpts) -> Result<tty::Multiplexer<'_>> {
690        let ep = url::construct_ep(
691            format!("/libpod/containers/{}/attach", &self.id),
692            opts.stream().serialize(),
693        );
694        let inspect = self.inspect().await?;
695        let is_tty = inspect.config.and_then(|c| c.tty).unwrap_or_default();
696        self.podman
697            .post_upgrade_stream(ep, Payload::empty())
698            .await
699            .map(|x| {
700                // When the container allocates a TTY the stream doesn't come in the standard
701                // Docker format but rather as a raw stream of bytes.
702                if is_tty {
703                    tty::Multiplexer::new(x, tty::decode_raw)
704                } else {
705                    tty::Multiplexer::new(x, tty::decode_chunk)
706                }
707            })
708    }}
709
710    api_doc! {
711    Container => ChangesLibpod
712    |
713    /// Returns which files in this container's filesystem have been added, deleted, or modified.
714    ///
715    /// Examples:
716    ///
717    /// ```no_run
718    /// async {
719    ///     use podman_api::Podman;
720    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
721    ///
722    ///     match podman
723    ///         .containers()
724    ///         .get("79c93f220e3e")
725    ///         .changes(&Default::default())
726    ///         .await
727    ///     {
728    ///         Ok(changes) => println!("{:?}", changes),
729    ///         Err(e) => eprintln!("{}", e),
730    ///     }
731    /// };
732    /// ```
733    pub async fn changes(
734        &self,
735        opts: &opts::ChangesOpts,
736    ) -> Result<Vec<models::ContainerChangeResponseItem>> {
737        let ep = url::construct_ep(
738            format!("/libpod/containers/{}/changes", &self.id),
739            opts.serialize(),
740        );
741        self.podman.get_json(&ep).await
742    }}
743
744    api_doc! {
745    Container => LogsLibpod
746    |
747    /// Get logs from this container.
748    ///
749    /// Examples:
750    ///
751    /// ```no_run
752    /// use futures_util::StreamExt;
753    /// async {
754    ///     use podman_api::Podman;
755    ///     use podman_api::conn::TtyChunk;
756    ///     use podman_api::opts::ContainerLogsOpts;
757    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
758    ///
759    ///     let container = podman.containers().get("3f278d2d0d79");
760    ///     let mut logs = container.logs(
761    ///         &ContainerLogsOpts::builder()
762    ///             .stdout(true)
763    ///             .stderr(true)
764    ///             .follow(true)
765    ///             .build(),
766    ///     );
767    ///
768    ///     while let Some(chunk) = logs.next().await {
769    ///         match chunk.unwrap() {
770    ///             TtyChunk::StdOut(data) => {
771    ///                 println!("{}", String::from_utf8_lossy(&data));
772    ///             }
773    ///             TtyChunk::StdErr(data) => {
774    ///                 eprintln!("{}", String::from_utf8_lossy(&data));
775    ///             }
776    ///             _ => {}
777    ///         }
778    ///     }
779    /// };
780    /// ```
781    pub fn logs(
782        &self,
783        opts: &opts::ContainerLogsOpts,
784    ) -> impl Stream<Item = Result<tty::TtyChunk>> + '_ {
785        let ep = url::construct_ep(
786            format!("/libpod/containers/{}/logs", &self.id),
787            opts.serialize(),
788        );
789        let stream = Box::pin(
790            self.podman
791                .get_stream(ep)
792                .map_err(|e| containers_api::conn::Error::Any(Box::new(e))),
793        );
794
795        Box::pin(tty::decode(stream).map_err(crate::Error::Error))
796    }}
797
798    api_doc! {
799    Container => StatsAllLibpod
800    |
801    /// Return a single resource usage statistics of this container.
802    ///
803    /// Examples:
804    ///
805    /// ```no_run
806    /// async {
807    ///     use podman_api::Podman;
808    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
809    ///
810    ///     match podman.containers().get("fc93f220e3e").stats().await {
811    ///         Ok(stats) => println!("{:?}", stats),
812    ///         Err(e) => eprintln!("{}", e),
813    ///     }
814    /// };
815    /// ```
816    pub async fn stats(&self) -> Result<models::ContainerStats200Response> {
817        self.podman
818            .containers()
819            .stats(
820                &opts::ContainerStatsOpts::builder()
821                    .containers([self.id.to_string()])
822                    .build(),
823            )
824            .await
825    }}
826
827    api_doc! {
828    Container => StatsAllLibpod
829    |
830    /// Return a stream of resource usage statistics of this container.
831    ///
832    /// Examples:
833    ///
834    /// ```no_run
835    /// use futures_util::StreamExt;
836    /// async {
837    ///     use podman_api::Podman;
838    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
839    ///
840    ///     let container = podman.containers().get("fc93f220e3e");
841    ///     let mut stats = container.stats_stream(None);
842    ///
843    ///     while let Some(chunk) = stats.next().await {
844    ///         match chunk {
845    ///             Ok(chunk) => println!("{:?}", chunk),
846    ///             Err(e) => eprintln!("{}", e),
847    ///         }
848    ///     }
849    /// };
850    /// ```
851    pub fn stats_stream(
852        &self,
853        interval: Option<usize>,
854    ) -> impl Stream<Item = Result<models::ContainerStats200Response>> + '_ {
855        let opts = opts::ContainerStatsOpts::builder()
856            .containers([self.id.to_string()])
857            .interval(interval.unwrap_or(5))
858            .build();
859        let ep = url::construct_ep("/libpod/containers/stats", opts.stream().serialize());
860
861        Box::pin(self.podman.get_json_stream(ep))
862    }}
863
864    api_doc! {
865    Container => TopLibpod
866    |
867    /// List processes running inside this container.
868    ///
869    /// Examples:
870    ///
871    /// ```no_run
872    /// async {
873    ///     use podman_api::Podman;
874    /// };
875    /// async {
876    ///     use podman_api::Podman;
877    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
878    ///
879    ///     match podman.containers().get("fc93f220e3e").top(&Default::default()).await {
880    ///         Ok(stats) => println!("{:?}", stats),
881    ///         Err(e) => eprintln!("{}", e),
882    ///     }
883    /// };
884    /// ```
885    pub async fn top(&self, opts: &opts::ContainerTopOpts) -> Result<models::ContainerTopOkBody> {
886        let ep = url::construct_ep(
887            format!("/libpod/containers/{}/top", &self.id),
888            opts.oneshot().serialize(),
889        );
890
891        self.podman.get_json(&ep).await
892    }}
893
894    api_doc! {
895    Container => TopLibpod
896    |
897    /// List processes running inside this container as a stream. (As of libpod version 4.0)
898    ///
899    /// Examples:
900    ///
901    /// ```no_run
902    /// use futures_util::StreamExt;
903    /// async {
904    ///     use podman_api::Podman;
905    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
906    ///
907    ///     let container = podman.containers().get("fc93f220e3e");
908    ///     let mut top = container
909    ///         .top_stream(&Default::default());
910    ///
911    ///     while let Some(chunk) = top.next().await {
912    ///         match chunk {
913    ///             Ok(chunk) => println!("{:?}", chunk),
914    ///             Err(e) => eprintln!("{}", e),
915    ///         }
916    ///     }
917    /// };
918    /// ```
919    pub fn top_stream(
920        &self,
921        opts: &opts::ContainerTopOpts,
922    ) -> impl Stream<Item = Result<models::ContainerTopOkBody>> + '_ {
923        let ep = url::construct_ep(
924            format!("/libpod/containers/{}/top", &self.id),
925            opts.stream().serialize(),
926        );
927
928        Box::pin(self.podman.get_json_stream(ep))
929    }}
930
931    api_doc! {
932    Generate => SystemdLibpod
933    |
934    /// Generate Systemd Units based on this container.
935    ///
936    /// Examples:
937    ///
938    /// ```no_run
939    /// async {
940    ///     use podman_api::Podman;
941    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
942    ///
943    ///     match podman
944    ///         .containers()
945    ///         .get("fc93f220e3e")
946    ///         .generate_systemd_units(&Default::default())
947    ///         .await
948    ///     {
949    ///         Ok(info) => println!("{:?}", info),
950    ///         Err(e) => eprintln!("{}", e),
951    ///     }
952    /// };
953    /// ```
954    pub async fn generate_systemd_units(
955        &self,
956        opts: &opts::SystemdUnitsOpts,
957    ) -> Result<Value> {
958        self.podman.generate_systemd_units(opts, &self.id).await
959    }}
960
961    api_doc! {
962    Generate => KubeLibpod
963    |
964    /// Generate Kubernetes YAML based on this container
965    ///
966    /// Parameters:
967    /// * service - Generate YAML for a Kubernetes service object.
968    ///
969    /// Examples:
970    ///
971    /// ```no_run
972    /// async {
973    ///     use podman_api::Podman;
974    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
975    ///
976    ///     match podman
977    ///         .containers()
978    ///         .get("fc93f220e3e")
979    ///         .generate_kube_yaml(false)
980    ///         .await
981    ///     {
982    ///         Ok(yaml) => println!("{:?}", yaml),
983    ///         Err(e) => eprintln!("{}", e),
984    ///     }
985    /// };
986    /// ```
987    pub async fn generate_kube_yaml(&self, service: bool) -> Result<String> {
988        self.podman.generate_kube_yaml(service, &self.id).await
989    }}
990
991    api_doc! {
992    Network => ConnectLibpod
993    |
994    /// Connect this container to a network
995    ///
996    /// Examples:
997    ///
998    /// ```no_run
999    /// async {
1000    ///     use podman_api::Podman;
1001    ///     use podman_api::opts::NetworkConnectOpts;
1002    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1003    ///
1004    ///     if let Err(e) = podman
1005    ///             .containers()
1006    ///             .get("fc93f220e3e")
1007    ///             .connect("my-network", &Default::default())
1008    ///             .await
1009    ///     {
1010    ///         eprintln!("{}", e);
1011    ///     }
1012    /// };
1013    /// ```
1014    pub async fn connect(
1015        &self,
1016        network: impl Into<crate::Id>,
1017        opts: &opts::NetworkConnectOpts,
1018    ) -> Result<()> {
1019        let network = self.podman.networks().get(network.into());
1020        let opts = opts.for_container(&self.id);
1021        network.connect_container(&opts).await
1022    }}
1023
1024    api_doc! {
1025    Network => DisconnectLibpod
1026    |
1027    /// Disconnect this container from a network.
1028    ///
1029    /// Examples:
1030    ///
1031    /// ```no_run
1032    /// async {
1033    ///     use podman_api::Podman;
1034    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1035    ///
1036    ///     if let Err(e) = podman.containers().get("fc93f220e3e").disconnect("my-network", true).await {
1037    ///         eprintln!("{}", e);
1038    ///     }
1039    /// };
1040    /// ```
1041    pub async fn disconnect(&self, network: impl Into<crate::Id>, force: bool) -> Result<()> {
1042        let network = self.podman.networks().get(network.into());
1043        network
1044            .disconnect_container(
1045                &opts::NetworkDisconnectOpts::builder()
1046                    .container(self.id.as_ref())
1047                    .force(force)
1048                    .build(),
1049            )
1050            .await
1051    }}
1052
1053    api_doc! {
1054    Container => HealthcheckLibpod
1055    |
1056    /// Execute the defined healthcheck and return information about the result.
1057    ///
1058    /// Examples:
1059    ///
1060    /// ```no_run
1061    /// async {
1062    ///     use podman_api::Podman;
1063    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1064    ///
1065    ///     match podman.containers().get("fc93f220e3e").healthcheck().await {
1066    ///         Ok(healthcheck) => println!("{:?}", healthcheck),
1067    ///         Err(e) => eprintln!("{}", e),
1068    ///     }
1069    /// };
1070    /// ```
1071    pub async fn healthcheck(&self) -> Result<models::HealthCheckResults> {
1072        self.podman
1073            .get_json(&format!("/libpod/containers/{}/healthcheck", &self.id))
1074            .await
1075    }}
1076
1077    api_doc! {
1078    Container => Archive
1079    |
1080    /// Copy a file/folder from the container.  The resulting stream is a tarball of the extracted
1081    /// files.
1082    ///
1083    /// If `path` is not an absolute path, it is relative to the container’s root directory. The
1084    /// resource specified by `path` must exist. To assert that the resource is expected to be a
1085    /// directory, `path` should end in `/` or `/`. (assuming a path separator of `/`). If `path`
1086    /// ends in `/.`  then this indicates that only the contents of the path directory should be
1087    /// copied.  A symlink is always resolved to its target.
1088    ///
1089    /// Examples:
1090    ///
1091    /// ```no_run
1092    /// async {
1093    ///     use podman_api::Podman;
1094    ///     use futures_util::TryStreamExt;
1095    ///     use tar::Archive;
1096    ///
1097    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1098    ///
1099    ///     let bytes = podman
1100    ///          .containers()
1101    ///          .get("fc93f220e3e")
1102    ///          .copy_from("/tmp/dir")
1103    ///          .try_concat()
1104    ///          .await.unwrap();
1105    ///
1106    ///      let mut archive = Archive::new(&bytes[..]);
1107    ///      let local_path = "/tmp";
1108    ///      archive.unpack(&local_path).unwrap();
1109    /// };
1110    /// ```
1111    pub fn copy_from(&self, path: impl AsRef<Path>) -> impl Stream<Item = Result<Vec<u8>>> + '_ {
1112        self.podman
1113            .get_stream(format!(
1114                "/containers/{}/archive?{}",
1115                self.id,
1116                url::encoded_pair("path", path.as_ref().to_string_lossy())
1117            ))
1118            .map_ok(|c| c.to_vec())
1119    }}
1120
1121    api_doc! {
1122    PutContainer => Archive
1123    |
1124    /// Copy a tarball (see `body`) to the container.
1125    ///
1126    /// The tarball will be copied to the container and extracted at the given location (see `path`).
1127    ///
1128    /// Examples:
1129    ///
1130    /// ```no_run
1131    /// async {
1132    ///     use podman_api::Podman;
1133    ///     use futures_util::TryStreamExt;
1134    ///     use tar::Archive;
1135    ///
1136    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1137    ///
1138    ///     let bytes = vec![];
1139    ///     let src_path = std::path::PathBuf::from("/tmp/dir");
1140    ///
1141    ///     let mut ar = tar::Builder::new(Vec::new());
1142    ///     let mut header = tar::Header::new_gnu();
1143    ///     header.set_size(bytes.len() as u64);
1144    ///     header.set_mode(0o0644);
1145    ///     ar.append_data(
1146    ///         &mut header,
1147    ///         src_path
1148    ///             .iter()
1149    ///             .skip(1)
1150    ///             .collect::<std::path::PathBuf>(),
1151    ///         std::io::Cursor::new(bytes),
1152    ///     ).unwrap();
1153    ///     let data = ar.into_inner().unwrap();
1154    ///
1155    ///     if let Err(e) = podman
1156    ///         .containers()
1157    ///         .get("fc93f220e3e")
1158    ///         .copy_to("/", data.into())
1159    ///         .await
1160    ///     {
1161    ///         eprintln!("Error: {}", e);
1162    ///     }
1163    /// };
1164    /// ```
1165    pub async fn copy_to(
1166        &self,
1167        path: impl AsRef<Path>,
1168        body: crate::conn::hyper::Body,
1169    ) -> Result<()> {
1170        self.podman
1171            .put(
1172                &format!(
1173                    "/containers/{}/archive?{}",
1174                    self.id,
1175                    url::encoded_pair("path", path.as_ref().to_string_lossy())
1176                ),
1177                Payload::XTar(body),
1178            )
1179            .await
1180            .map(|_| ())
1181    }}
1182
1183    api_doc! {
1184    PutContainer => Archive
1185    |
1186    /// Copy a byte slice as file into (see `bytes`) the container.
1187    ///
1188    /// The file will be copied at the given location (see `path`) and will be owned by root
1189    /// with access mask 644.
1190    ///
1191    /// Examples:
1192    ///
1193    /// ```no_run
1194    /// async {
1195    ///     use podman_api::Podman;
1196    ///     use futures_util::TryStreamExt;
1197    ///     use tar::Archive;
1198    ///
1199    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1200    ///
1201    ///     use std::{fs::File, io::Read};
1202    ///
1203    ///     let mut file = File::open("/some/important/file").unwrap();
1204    ///     let mut bytes = Vec::new();
1205    ///     file.read_to_end(&mut bytes)
1206    ///         .expect("Cannot read file on the localhost.");
1207    ///
1208    ///     if let Err(e) = podman
1209    ///         .containers()
1210    ///         .get("fc93f220e3e")
1211    ///         .copy_file_into("/tmp/", &bytes)
1212    ///         .await
1213    ///     {
1214    ///         eprintln!("Error: {}", e);
1215    ///     }
1216    /// };
1217    /// ```
1218    pub async fn copy_file_into<P: AsRef<Path>>(&self, path: P, bytes: &[u8]) -> Result<()> {
1219        let path = path.as_ref();
1220
1221        let mut ar = tar::Builder::new(Vec::new());
1222        let mut header = tar::Header::new_gnu();
1223        header.set_size(bytes.len() as u64);
1224        header.set_mode(0o0644);
1225        ar.append_data(
1226            &mut header,
1227            path.to_path_buf()
1228                .iter()
1229                .skip(1)
1230                .collect::<std::path::PathBuf>(),
1231            bytes,
1232        )?;
1233        let data = ar.into_inner()?;
1234
1235        self.copy_to(Path::new("/"), data.into()).await.map(|_| ())
1236    }}
1237
1238    api_doc! {
1239    Container => ResizeLibpod
1240    |
1241    /// Resize the terminal attached to this container (for use with Attach).
1242    ///
1243    /// Examples:
1244    ///
1245    /// ```no_run
1246    /// async {
1247    ///     use podman_api::Podman;
1248    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1249    ///     let container = podman.containers().get("451b27c6b9d3");
1250    ///
1251    ///     if let Err(e) = container.resize(1280, 720).await {
1252    ///         eprintln!("{}", e);
1253    ///     }
1254    /// };
1255    /// ```
1256    pub async fn resize(&self, width: usize, heigth: usize) -> Result<()> {
1257        let ep = url::construct_ep(
1258            format!("/libpod/containers/{}/resize", &self.id),
1259            Some(url::encoded_pairs([
1260                ("h", heigth.to_string()),
1261                ("w", width.to_string()),
1262            ])),
1263        );
1264        self.podman
1265            .post(&ep, Payload::None::<&str>, Headers::none())
1266            .await
1267            .map(|_| ())
1268    }}
1269
1270    api_doc! {
1271    Container => ExportLibpod
1272    |
1273    /// Export the contents of a container as a tarball.
1274    ///
1275    /// Examples:
1276    ///
1277    /// ```no_run
1278    /// use futures_util::StreamExt;
1279    /// async {
1280    ///     use podman_api::Podman;
1281    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1282    ///
1283    ///     let container = podman.containers().get("79c93f220e3e");
1284    ///     let mut tarball_stream = container.export();
1285    ///     let mut content = vec![];
1286    ///
1287    ///     while let Some(tar_chunk) = tarball_stream.next().await {
1288    ///         content.append(&mut tar_chunk.unwrap());
1289    ///     }
1290    ///
1291    ///     std::fs::write("/tmp/79c93f220e3e.tar", &content).unwrap();
1292    /// };
1293    /// ```
1294    pub fn export(&self) -> impl Stream<Item = Result<Vec<u8>>> + Unpin + '_ {
1295        let ep = format!("/libpod/containers/{}/export", &self.id);
1296
1297        Box::pin(self.podman.get_stream(ep).map_ok(|c| c.to_vec()))
1298    }}
1299
1300    api_doc! {
1301    Container => RestoreLibpod
1302    |
1303    /// Restore a container from a checkpoint
1304    ///
1305    /// Examples:
1306    ///
1307    /// ```no_run
1308    /// async {
1309    ///     use podman_api::Podman;
1310    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1311    ///
1312    ///     match podman.containers().get("79c93f220e3e").restore(&Default::default()).await {
1313    ///         Ok(info) => println!("{info:?}"),
1314    ///         Err(e) =>  eprintln!("{e}"),
1315    ///     }
1316    /// };
1317    /// ```
1318    pub async fn restore(&self, opts: &opts::ContainerRestoreOpts) -> Result<Value> {
1319        let ep = url::construct_ep(
1320            format!("/libpod/containers/{}/restore", &self.id),
1321            opts.serialize(),
1322        );
1323        self.podman
1324            .post_json(&ep, Payload::empty(), Headers::none())
1325            .await
1326    }}
1327}
1328
1329impl Containers {
1330    api_doc! {
1331    Container => CreateLibpod
1332    |
1333    /// Create a container with specified options.
1334    ///
1335    /// Examples:
1336    ///
1337    /// ```no_run
1338    /// async {
1339    ///     use podman_api::Podman;
1340    ///     use podman_api::opts::ContainerCreateOpts;
1341    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1342    ///
1343    ///     if let Err(e) = podman
1344    ///         .containers()
1345    ///         .create(
1346    ///             &ContainerCreateOpts::builder()
1347    ///                 .image("debian:11")
1348    ///                 .command(
1349    ///                     ["/usr/bin/httpd"]
1350    ///                 )
1351    ///                 .env([
1352    ///                     ("app", "web"),
1353    ///                 ])
1354    ///                 .build(),
1355    ///         )
1356    ///         .await
1357    ///     {
1358    ///         eprintln!("{}", e);
1359    ///     }
1360    /// };
1361    /// ```
1362    pub async fn create(
1363        &self,
1364        opts: &opts::ContainerCreateOpts,
1365    ) -> Result<models::ContainerCreateCreatedBody> {
1366        self.podman
1367            .post_json(
1368                &"/libpod/containers/create",
1369                Payload::Json(opts.serialize_vec()?),
1370                Headers::none(),
1371            )
1372            .await
1373    }}
1374
1375    api_doc! {
1376    Container => ListLibpod
1377    |
1378    /// Returns a list of containers.
1379    ///
1380    /// Examples:
1381    ///
1382    /// ```no_run
1383    /// async {
1384    ///     use podman_api::Podman;
1385    ///     use podman_api::opts::{ContainerListOpts, ContainerListFilter};
1386    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1387    ///
1388    ///     for container in podman
1389    ///         .containers()
1390    ///         .list(
1391    ///             &ContainerListOpts::builder()
1392    ///                 .all(true)
1393    ///                 .filter([ContainerListFilter::LabelKeyVal("app".into(), "web".into())])
1394    ///                 .build(),
1395    ///         )
1396    ///         .await
1397    ///         .unwrap()
1398    ///     {
1399    ///         println!("{:?}", container);
1400    ///     }
1401    /// };
1402    /// ```
1403    pub async fn list(&self, opts: &opts::ContainerListOpts) -> Result<Vec<models::ListContainer>> {
1404        let ep = url::construct_ep("/libpod/containers/json", opts.serialize());
1405        self.podman.get_json(&ep).await
1406    }}
1407
1408    api_doc! {
1409    Container => StatsAllLibpod
1410    |
1411    /// Return a single resource usage statistics of one or more container. If not container is
1412    /// specified in the options, the statistics of all are returned.
1413    ///
1414    /// Examples:
1415    ///
1416    /// ```no_run
1417    /// async {
1418    ///     use podman_api::Podman;
1419    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1420    ///
1421    ///     match podman.containers().stats(&Default::default()).await {
1422    ///         Ok(stats) => println!("{:?}", stats),
1423    ///         Err(e) => eprintln!("{}", e),
1424    ///     }
1425    /// };
1426    /// ```
1427    pub async fn stats(
1428        &self,
1429        opts: &opts::ContainerStatsOpts,
1430    ) -> Result<models::ContainerStats200Response> {
1431        let ep = url::construct_ep("/libpod/containers/stats", opts.oneshot().serialize());
1432
1433        self.podman.get_json(&ep).await
1434    }}
1435
1436    api_doc! {
1437    Container => StatsAllLibpod
1438    |
1439    /// Return a stream of resource usage statistics of one or more container. If not container is
1440    /// specified in the options, the statistics of all are returned.
1441    ///
1442    /// Examples:
1443    ///
1444    /// ```no_run
1445    /// use futures_util::StreamExt;
1446    /// async {
1447    ///     use podman_api::Podman;
1448    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1449    ///
1450    ///     let containers = podman.containers();
1451    ///     let mut stats = containers
1452    ///         .stats_stream(&Default::default());
1453    ///
1454    ///     while let Some(chunk) = stats.next().await {
1455    ///         match chunk {
1456    ///             Ok(chunk) => println!("{:?}", chunk),
1457    ///             Err(e) => eprintln!("{}", e),
1458    ///         }
1459    ///     }
1460    /// };
1461    /// ```
1462    pub fn stats_stream(
1463        &self,
1464        opts: &opts::ContainerStatsOpts,
1465    ) -> impl Stream<Item = Result<models::ContainerStats200Response>> + '_ {
1466        let ep = url::construct_ep("/libpod/containers/stats", opts.stream().serialize());
1467
1468        Box::pin(self.podman.get_json_stream(ep))
1469    }}
1470
1471    api_doc! {
1472    Container => ShowMountedLibpod
1473    |
1474    /// List all mounted containers mount points.
1475    ///
1476    /// Examples:
1477    ///
1478    /// ```no_run
1479    /// async {
1480    ///     use podman_api::Podman;
1481    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1482    ///
1483    ///     match podman.containers().list_mounted().await {
1484    ///         Ok(mounts) => println!("{:?}", mounts),
1485    ///         Err(e) => eprintln!("{}", e),
1486    ///     }
1487    /// };
1488    /// ```
1489    pub async fn list_mounted(&self) -> Result<Value> {
1490        self.podman.get_json("/libpod/containers/showmounted").await
1491    }}
1492
1493    api_doc! {
1494    Container => PruneLibpod
1495    |
1496    /// Remove containers not in use.
1497    ///
1498    /// Examples:
1499    ///
1500    /// ```no_run
1501    /// async {
1502    ///     use podman_api::Podman;
1503    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1504    ///
1505    ///     match podman.containers().prune(&Default::default()).await {
1506    ///         Ok(report) => println!("{:?}", report),
1507    ///         Err(e) => eprintln!("{}", e),
1508    ///     }
1509    /// };
1510    /// ```
1511    pub async fn prune(
1512        &self,
1513        opts: &opts::ContainerPruneOpts,
1514    ) -> Result<Vec<models::ContainersPruneReportLibpod>> {
1515        let ep = url::construct_ep("/libpod/containers/prune", opts.serialize());
1516        self.podman
1517            .post_json(&ep, Payload::empty(), Headers::none())
1518            .await
1519    }}
1520}