Skip to main content

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            .clone()
698            .post_upgrade_stream(ep, Payload::empty())
699            .await
700            .map(|x| {
701                // When the container allocates a TTY the stream doesn't come in the standard
702                // Docker format but rather as a raw stream of bytes.
703                if is_tty {
704                    tty::Multiplexer::new(x, tty::decode_raw)
705                } else {
706                    tty::Multiplexer::new(x, tty::decode_chunk)
707                }
708            })
709    }}
710
711    api_doc! {
712    Container => ChangesLibpod
713    |
714    /// Returns which files in this container's filesystem have been added, deleted, or modified.
715    ///
716    /// Examples:
717    ///
718    /// ```no_run
719    /// async {
720    ///     use podman_api::Podman;
721    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
722    ///
723    ///     match podman
724    ///         .containers()
725    ///         .get("79c93f220e3e")
726    ///         .changes(&Default::default())
727    ///         .await
728    ///     {
729    ///         Ok(changes) => println!("{:?}", changes),
730    ///         Err(e) => eprintln!("{}", e),
731    ///     }
732    /// };
733    /// ```
734    pub async fn changes(
735        &self,
736        opts: &opts::ChangesOpts,
737    ) -> Result<Vec<models::ContainerChangeResponseItem>> {
738        let ep = url::construct_ep(
739            format!("/libpod/containers/{}/changes", &self.id),
740            opts.serialize(),
741        );
742        self.podman.get_json(&ep).await
743    }}
744
745    api_doc! {
746    Container => LogsLibpod
747    |
748    /// Get logs from this container.
749    ///
750    /// Examples:
751    ///
752    /// ```no_run
753    /// use futures_util::StreamExt;
754    /// async {
755    ///     use podman_api::Podman;
756    ///     use podman_api::conn::TtyChunk;
757    ///     use podman_api::opts::ContainerLogsOpts;
758    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
759    ///
760    ///     let container = podman.containers().get("3f278d2d0d79");
761    ///     let mut logs = container.logs(
762    ///         &ContainerLogsOpts::builder()
763    ///             .stdout(true)
764    ///             .stderr(true)
765    ///             .follow(true)
766    ///             .build(),
767    ///     );
768    ///
769    ///     while let Some(chunk) = logs.next().await {
770    ///         match chunk.unwrap() {
771    ///             TtyChunk::StdOut(data) => {
772    ///                 println!("{}", String::from_utf8_lossy(&data));
773    ///             }
774    ///             TtyChunk::StdErr(data) => {
775    ///                 eprintln!("{}", String::from_utf8_lossy(&data));
776    ///             }
777    ///             _ => {}
778    ///         }
779    ///     }
780    /// };
781    /// ```
782    pub fn logs(
783        &self,
784        opts: &opts::ContainerLogsOpts,
785    ) -> impl Stream<Item = Result<tty::TtyChunk>> + '_ {
786        let ep = url::construct_ep(
787            format!("/libpod/containers/{}/logs", &self.id),
788            opts.serialize(),
789        );
790        let stream = Box::pin(
791            self.podman
792                .get_stream(ep)
793                .map_err(|e| containers_api::conn::Error::Any(Box::new(e))),
794        );
795
796        Box::pin(tty::decode(stream).map_err(crate::Error::Error))
797    }}
798
799    api_doc! {
800    Container => StatsAllLibpod
801    |
802    /// Return a single resource usage statistics of this container.
803    ///
804    /// Examples:
805    ///
806    /// ```no_run
807    /// async {
808    ///     use podman_api::Podman;
809    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
810    ///
811    ///     match podman.containers().get("fc93f220e3e").stats().await {
812    ///         Ok(stats) => println!("{:?}", stats),
813    ///         Err(e) => eprintln!("{}", e),
814    ///     }
815    /// };
816    /// ```
817    pub async fn stats(&self) -> Result<models::ContainerStats200Response> {
818        self.podman
819            .containers()
820            .stats(
821                &opts::ContainerStatsOpts::builder()
822                    .containers([self.id.to_string()])
823                    .build(),
824            )
825            .await
826    }}
827
828    api_doc! {
829    Container => StatsAllLibpod
830    |
831    /// Return a stream of resource usage statistics of this container.
832    ///
833    /// Examples:
834    ///
835    /// ```no_run
836    /// use futures_util::StreamExt;
837    /// async {
838    ///     use podman_api::Podman;
839    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
840    ///
841    ///     let container = podman.containers().get("fc93f220e3e");
842    ///     let mut stats = container.stats_stream(None);
843    ///
844    ///     while let Some(chunk) = stats.next().await {
845    ///         match chunk {
846    ///             Ok(chunk) => println!("{:?}", chunk),
847    ///             Err(e) => eprintln!("{}", e),
848    ///         }
849    ///     }
850    /// };
851    /// ```
852    pub fn stats_stream(
853        &self,
854        interval: Option<usize>,
855    ) -> impl Stream<Item = Result<models::ContainerStats200Response>> + '_ {
856        let opts = opts::ContainerStatsOpts::builder()
857            .containers([self.id.to_string()])
858            .interval(interval.unwrap_or(5))
859            .build();
860        let ep = url::construct_ep("/libpod/containers/stats", opts.stream().serialize());
861
862        Box::pin(self.podman.get_json_stream(ep))
863    }}
864
865    api_doc! {
866    Container => TopLibpod
867    |
868    /// List processes running inside this container.
869    ///
870    /// Examples:
871    ///
872    /// ```no_run
873    /// async {
874    ///     use podman_api::Podman;
875    /// };
876    /// async {
877    ///     use podman_api::Podman;
878    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
879    ///
880    ///     match podman.containers().get("fc93f220e3e").top(&Default::default()).await {
881    ///         Ok(stats) => println!("{:?}", stats),
882    ///         Err(e) => eprintln!("{}", e),
883    ///     }
884    /// };
885    /// ```
886    pub async fn top(&self, opts: &opts::ContainerTopOpts) -> Result<models::ContainerTopOkBody> {
887        let ep = url::construct_ep(
888            format!("/libpod/containers/{}/top", &self.id),
889            opts.oneshot().serialize(),
890        );
891
892        self.podman.get_json(&ep).await
893    }}
894
895    api_doc! {
896    Container => TopLibpod
897    |
898    /// List processes running inside this container as a stream. (As of libpod version 4.0)
899    ///
900    /// Examples:
901    ///
902    /// ```no_run
903    /// use futures_util::StreamExt;
904    /// async {
905    ///     use podman_api::Podman;
906    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
907    ///
908    ///     let container = podman.containers().get("fc93f220e3e");
909    ///     let mut top = container
910    ///         .top_stream(&Default::default());
911    ///
912    ///     while let Some(chunk) = top.next().await {
913    ///         match chunk {
914    ///             Ok(chunk) => println!("{:?}", chunk),
915    ///             Err(e) => eprintln!("{}", e),
916    ///         }
917    ///     }
918    /// };
919    /// ```
920    pub fn top_stream(
921        &self,
922        opts: &opts::ContainerTopOpts,
923    ) -> impl Stream<Item = Result<models::ContainerTopOkBody>> + '_ {
924        let ep = url::construct_ep(
925            format!("/libpod/containers/{}/top", &self.id),
926            opts.stream().serialize(),
927        );
928
929        Box::pin(self.podman.get_json_stream(ep))
930    }}
931
932    api_doc! {
933    Generate => SystemdLibpod
934    |
935    /// Generate Systemd Units based on this container.
936    ///
937    /// Examples:
938    ///
939    /// ```no_run
940    /// async {
941    ///     use podman_api::Podman;
942    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
943    ///
944    ///     match podman
945    ///         .containers()
946    ///         .get("fc93f220e3e")
947    ///         .generate_systemd_units(&Default::default())
948    ///         .await
949    ///     {
950    ///         Ok(info) => println!("{:?}", info),
951    ///         Err(e) => eprintln!("{}", e),
952    ///     }
953    /// };
954    /// ```
955    pub async fn generate_systemd_units(
956        &self,
957        opts: &opts::SystemdUnitsOpts,
958    ) -> Result<Value> {
959        self.podman.generate_systemd_units(opts, &self.id).await
960    }}
961
962    api_doc! {
963    Generate => KubeLibpod
964    |
965    /// Generate Kubernetes YAML based on this container
966    ///
967    /// Parameters:
968    /// * service - Generate YAML for a Kubernetes service object.
969    ///
970    /// Examples:
971    ///
972    /// ```no_run
973    /// async {
974    ///     use podman_api::Podman;
975    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
976    ///
977    ///     match podman
978    ///         .containers()
979    ///         .get("fc93f220e3e")
980    ///         .generate_kube_yaml(false)
981    ///         .await
982    ///     {
983    ///         Ok(yaml) => println!("{:?}", yaml),
984    ///         Err(e) => eprintln!("{}", e),
985    ///     }
986    /// };
987    /// ```
988    pub async fn generate_kube_yaml(&self, service: bool) -> Result<String> {
989        self.podman.generate_kube_yaml(service, &self.id).await
990    }}
991
992    api_doc! {
993    Network => ConnectLibpod
994    |
995    /// Connect this container to a network
996    ///
997    /// Examples:
998    ///
999    /// ```no_run
1000    /// async {
1001    ///     use podman_api::Podman;
1002    ///     use podman_api::opts::NetworkConnectOpts;
1003    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1004    ///
1005    ///     if let Err(e) = podman
1006    ///             .containers()
1007    ///             .get("fc93f220e3e")
1008    ///             .connect("my-network", &Default::default())
1009    ///             .await
1010    ///     {
1011    ///         eprintln!("{}", e);
1012    ///     }
1013    /// };
1014    /// ```
1015    pub async fn connect(
1016        &self,
1017        network: impl Into<crate::Id>,
1018        opts: &opts::NetworkConnectOpts,
1019    ) -> Result<()> {
1020        let network = self.podman.networks().get(network.into());
1021        let opts = opts.for_container(&self.id);
1022        network.connect_container(&opts).await
1023    }}
1024
1025    api_doc! {
1026    Network => DisconnectLibpod
1027    |
1028    /// Disconnect this container from a network.
1029    ///
1030    /// Examples:
1031    ///
1032    /// ```no_run
1033    /// async {
1034    ///     use podman_api::Podman;
1035    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1036    ///
1037    ///     if let Err(e) = podman.containers().get("fc93f220e3e").disconnect("my-network", true).await {
1038    ///         eprintln!("{}", e);
1039    ///     }
1040    /// };
1041    /// ```
1042    pub async fn disconnect(&self, network: impl Into<crate::Id>, force: bool) -> Result<()> {
1043        let network = self.podman.networks().get(network.into());
1044        network
1045            .disconnect_container(
1046                &opts::NetworkDisconnectOpts::builder()
1047                    .container(self.id.as_ref())
1048                    .force(force)
1049                    .build(),
1050            )
1051            .await
1052    }}
1053
1054    api_doc! {
1055    Container => HealthcheckLibpod
1056    |
1057    /// Execute the defined healthcheck and return information about the result.
1058    ///
1059    /// Examples:
1060    ///
1061    /// ```no_run
1062    /// async {
1063    ///     use podman_api::Podman;
1064    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1065    ///
1066    ///     match podman.containers().get("fc93f220e3e").healthcheck().await {
1067    ///         Ok(healthcheck) => println!("{:?}", healthcheck),
1068    ///         Err(e) => eprintln!("{}", e),
1069    ///     }
1070    /// };
1071    /// ```
1072    pub async fn healthcheck(&self) -> Result<models::HealthCheckResults> {
1073        self.podman
1074            .get_json(&format!("/libpod/containers/{}/healthcheck", &self.id))
1075            .await
1076    }}
1077
1078    api_doc! {
1079    Container => Archive
1080    |
1081    /// Copy a file/folder from the container.  The resulting stream is a tarball of the extracted
1082    /// files.
1083    ///
1084    /// If `path` is not an absolute path, it is relative to the container’s root directory. The
1085    /// resource specified by `path` must exist. To assert that the resource is expected to be a
1086    /// directory, `path` should end in `/` or `/`. (assuming a path separator of `/`). If `path`
1087    /// ends in `/.`  then this indicates that only the contents of the path directory should be
1088    /// copied.  A symlink is always resolved to its target.
1089    ///
1090    /// Examples:
1091    ///
1092    /// ```no_run
1093    /// async {
1094    ///     use podman_api::Podman;
1095    ///     use futures_util::TryStreamExt;
1096    ///     use tar::Archive;
1097    ///
1098    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1099    ///
1100    ///     let bytes = podman
1101    ///          .containers()
1102    ///          .get("fc93f220e3e")
1103    ///          .copy_from("/tmp/dir")
1104    ///          .try_concat()
1105    ///          .await.unwrap();
1106    ///
1107    ///      let mut archive = Archive::new(&bytes[..]);
1108    ///      let local_path = "/tmp";
1109    ///      archive.unpack(&local_path).unwrap();
1110    /// };
1111    /// ```
1112    pub fn copy_from(&self, path: impl AsRef<Path>) -> impl Stream<Item = Result<Vec<u8>>> + '_ {
1113        self.podman
1114            .get_stream(format!(
1115                "/containers/{}/archive?{}",
1116                self.id,
1117                url::encoded_pair("path", path.as_ref().to_string_lossy())
1118            ))
1119            .map_ok(|c| c.to_vec())
1120    }}
1121
1122    api_doc! {
1123    PutContainer => Archive
1124    |
1125    /// Copy a tarball (see `body`) to the container.
1126    ///
1127    /// The tarball will be copied to the container and extracted at the given location (see `path`).
1128    ///
1129    /// Examples:
1130    ///
1131    /// ```no_run
1132    /// async {
1133    ///     use podman_api::Podman;
1134    ///     use futures_util::TryStreamExt;
1135    ///     use tar::Archive;
1136    ///
1137    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1138    ///
1139    ///     let bytes = vec![];
1140    ///     let src_path = std::path::PathBuf::from("/tmp/dir");
1141    ///
1142    ///     let mut ar = tar::Builder::new(Vec::new());
1143    ///     let mut header = tar::Header::new_gnu();
1144    ///     header.set_size(bytes.len() as u64);
1145    ///     header.set_mode(0o0644);
1146    ///     ar.append_data(
1147    ///         &mut header,
1148    ///         src_path
1149    ///             .iter()
1150    ///             .skip(1)
1151    ///             .collect::<std::path::PathBuf>(),
1152    ///         std::io::Cursor::new(bytes),
1153    ///     ).unwrap();
1154    ///     let data = ar.into_inner().unwrap();
1155    ///
1156    ///     if let Err(e) = podman
1157    ///         .containers()
1158    ///         .get("fc93f220e3e")
1159    ///         .copy_to("/", data.into())
1160    ///         .await
1161    ///     {
1162    ///         eprintln!("Error: {}", e);
1163    ///     }
1164    /// };
1165    /// ```
1166    pub async fn copy_to(
1167        &self,
1168        path: impl AsRef<Path>,
1169        body: crate::conn::hyper::Body,
1170    ) -> Result<()> {
1171        self.podman
1172            .put(
1173                &format!(
1174                    "/containers/{}/archive?{}",
1175                    self.id,
1176                    url::encoded_pair("path", path.as_ref().to_string_lossy())
1177                ),
1178                Payload::XTar(body),
1179            )
1180            .await
1181            .map(|_| ())
1182    }}
1183
1184    api_doc! {
1185    PutContainer => Archive
1186    |
1187    /// Copy a byte slice as file into (see `bytes`) the container.
1188    ///
1189    /// The file will be copied at the given location (see `path`) and will be owned by root
1190    /// with access mask 644.
1191    ///
1192    /// Examples:
1193    ///
1194    /// ```no_run
1195    /// async {
1196    ///     use podman_api::Podman;
1197    ///     use futures_util::TryStreamExt;
1198    ///     use tar::Archive;
1199    ///
1200    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1201    ///
1202    ///     use std::{fs::File, io::Read};
1203    ///
1204    ///     let mut file = File::open("/some/important/file").unwrap();
1205    ///     let mut bytes = Vec::new();
1206    ///     file.read_to_end(&mut bytes)
1207    ///         .expect("Cannot read file on the localhost.");
1208    ///
1209    ///     if let Err(e) = podman
1210    ///         .containers()
1211    ///         .get("fc93f220e3e")
1212    ///         .copy_file_into("/tmp/", &bytes)
1213    ///         .await
1214    ///     {
1215    ///         eprintln!("Error: {}", e);
1216    ///     }
1217    /// };
1218    /// ```
1219    pub async fn copy_file_into<P: AsRef<Path>>(&self, path: P, bytes: &[u8]) -> Result<()> {
1220        let path = path.as_ref();
1221
1222        let mut ar = tar::Builder::new(Vec::new());
1223        let mut header = tar::Header::new_gnu();
1224        header.set_size(bytes.len() as u64);
1225        header.set_mode(0o0644);
1226        ar.append_data(
1227            &mut header,
1228            path.to_path_buf()
1229                .iter()
1230                .skip(1)
1231                .collect::<std::path::PathBuf>(),
1232            bytes,
1233        )?;
1234        let data = ar.into_inner()?;
1235
1236        self.copy_to(Path::new("/"), data.into()).await.map(|_| ())
1237    }}
1238
1239    api_doc! {
1240    Container => ResizeLibpod
1241    |
1242    /// Resize the terminal attached to this container (for use with Attach).
1243    ///
1244    /// Examples:
1245    ///
1246    /// ```no_run
1247    /// async {
1248    ///     use podman_api::Podman;
1249    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1250    ///     let container = podman.containers().get("451b27c6b9d3");
1251    ///
1252    ///     if let Err(e) = container.resize(1280, 720).await {
1253    ///         eprintln!("{}", e);
1254    ///     }
1255    /// };
1256    /// ```
1257    pub async fn resize(&self, width: usize, heigth: usize) -> Result<()> {
1258        let ep = url::construct_ep(
1259            format!("/libpod/containers/{}/resize", &self.id),
1260            Some(url::encoded_pairs([
1261                ("h", heigth.to_string()),
1262                ("w", width.to_string()),
1263            ])),
1264        );
1265        self.podman
1266            .post(&ep, Payload::None::<&str>, Headers::none())
1267            .await
1268            .map(|_| ())
1269    }}
1270
1271    api_doc! {
1272    Container => ExportLibpod
1273    |
1274    /// Export the contents of a container as a tarball.
1275    ///
1276    /// Examples:
1277    ///
1278    /// ```no_run
1279    /// use futures_util::StreamExt;
1280    /// async {
1281    ///     use podman_api::Podman;
1282    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1283    ///
1284    ///     let container = podman.containers().get("79c93f220e3e");
1285    ///     let mut tarball_stream = container.export();
1286    ///     let mut content = vec![];
1287    ///
1288    ///     while let Some(tar_chunk) = tarball_stream.next().await {
1289    ///         content.append(&mut tar_chunk.unwrap());
1290    ///     }
1291    ///
1292    ///     std::fs::write("/tmp/79c93f220e3e.tar", &content).unwrap();
1293    /// };
1294    /// ```
1295    pub fn export(&self) -> impl Stream<Item = Result<Vec<u8>>> + Unpin + '_ {
1296        let ep = format!("/libpod/containers/{}/export", &self.id);
1297
1298        Box::pin(self.podman.get_stream(ep).map_ok(|c| c.to_vec()))
1299    }}
1300
1301    api_doc! {
1302    Container => RestoreLibpod
1303    |
1304    /// Restore a container from a checkpoint
1305    ///
1306    /// Examples:
1307    ///
1308    /// ```no_run
1309    /// async {
1310    ///     use podman_api::Podman;
1311    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1312    ///
1313    ///     match podman.containers().get("79c93f220e3e").restore(&Default::default()).await {
1314    ///         Ok(info) => println!("{info:?}"),
1315    ///         Err(e) =>  eprintln!("{e}"),
1316    ///     }
1317    /// };
1318    /// ```
1319    pub async fn restore(&self, opts: &opts::ContainerRestoreOpts) -> Result<Value> {
1320        let ep = url::construct_ep(
1321            format!("/libpod/containers/{}/restore", &self.id),
1322            opts.serialize(),
1323        );
1324        self.podman
1325            .post_json(&ep, Payload::empty(), Headers::none())
1326            .await
1327    }}
1328}
1329
1330impl Containers {
1331    api_doc! {
1332    Container => CreateLibpod
1333    |
1334    /// Create a container with specified options.
1335    ///
1336    /// Examples:
1337    ///
1338    /// ```no_run
1339    /// async {
1340    ///     use podman_api::Podman;
1341    ///     use podman_api::opts::ContainerCreateOpts;
1342    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1343    ///
1344    ///     if let Err(e) = podman
1345    ///         .containers()
1346    ///         .create(
1347    ///             &ContainerCreateOpts::builder()
1348    ///                 .image("debian:11")
1349    ///                 .command(
1350    ///                     ["/usr/bin/httpd"]
1351    ///                 )
1352    ///                 .env([
1353    ///                     ("app", "web"),
1354    ///                 ])
1355    ///                 .build(),
1356    ///         )
1357    ///         .await
1358    ///     {
1359    ///         eprintln!("{}", e);
1360    ///     }
1361    /// };
1362    /// ```
1363    pub async fn create(
1364        &self,
1365        opts: &opts::ContainerCreateOpts,
1366    ) -> Result<models::ContainerCreateCreatedBody> {
1367        self.podman
1368            .post_json(
1369                &"/libpod/containers/create",
1370                Payload::Json(opts.serialize_vec()?),
1371                Headers::none(),
1372            )
1373            .await
1374    }}
1375
1376    api_doc! {
1377    Container => ListLibpod
1378    |
1379    /// Returns a list of containers.
1380    ///
1381    /// Examples:
1382    ///
1383    /// ```no_run
1384    /// async {
1385    ///     use podman_api::Podman;
1386    ///     use podman_api::opts::{ContainerListOpts, ContainerListFilter};
1387    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1388    ///
1389    ///     for container in podman
1390    ///         .containers()
1391    ///         .list(
1392    ///             &ContainerListOpts::builder()
1393    ///                 .all(true)
1394    ///                 .filter([ContainerListFilter::LabelKeyVal("app".into(), "web".into())])
1395    ///                 .build(),
1396    ///         )
1397    ///         .await
1398    ///         .unwrap()
1399    ///     {
1400    ///         println!("{:?}", container);
1401    ///     }
1402    /// };
1403    /// ```
1404    pub async fn list(&self, opts: &opts::ContainerListOpts) -> Result<Vec<models::ListContainer>> {
1405        let ep = url::construct_ep("/libpod/containers/json", opts.serialize());
1406        self.podman.get_json(&ep).await
1407    }}
1408
1409    api_doc! {
1410    Container => StatsAllLibpod
1411    |
1412    /// Return a single resource usage statistics of one or more container. If not container is
1413    /// specified in the options, the statistics of all are returned.
1414    ///
1415    /// Examples:
1416    ///
1417    /// ```no_run
1418    /// async {
1419    ///     use podman_api::Podman;
1420    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1421    ///
1422    ///     match podman.containers().stats(&Default::default()).await {
1423    ///         Ok(stats) => println!("{:?}", stats),
1424    ///         Err(e) => eprintln!("{}", e),
1425    ///     }
1426    /// };
1427    /// ```
1428    pub async fn stats(
1429        &self,
1430        opts: &opts::ContainerStatsOpts,
1431    ) -> Result<models::ContainerStats200Response> {
1432        let ep = url::construct_ep("/libpod/containers/stats", opts.oneshot().serialize());
1433
1434        self.podman.get_json(&ep).await
1435    }}
1436
1437    api_doc! {
1438    Container => StatsAllLibpod
1439    |
1440    /// Return a stream of resource usage statistics of one or more container. If not container is
1441    /// specified in the options, the statistics of all are returned.
1442    ///
1443    /// Examples:
1444    ///
1445    /// ```no_run
1446    /// use futures_util::StreamExt;
1447    /// async {
1448    ///     use podman_api::Podman;
1449    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1450    ///
1451    ///     let containers = podman.containers();
1452    ///     let mut stats = containers
1453    ///         .stats_stream(&Default::default());
1454    ///
1455    ///     while let Some(chunk) = stats.next().await {
1456    ///         match chunk {
1457    ///             Ok(chunk) => println!("{:?}", chunk),
1458    ///             Err(e) => eprintln!("{}", e),
1459    ///         }
1460    ///     }
1461    /// };
1462    /// ```
1463    pub fn stats_stream(
1464        &self,
1465        opts: &opts::ContainerStatsOpts,
1466    ) -> impl Stream<Item = Result<models::ContainerStats200Response>> + '_ {
1467        let ep = url::construct_ep("/libpod/containers/stats", opts.stream().serialize());
1468
1469        Box::pin(self.podman.get_json_stream(ep))
1470    }}
1471
1472    api_doc! {
1473    Container => ShowMountedLibpod
1474    |
1475    /// List all mounted containers mount points.
1476    ///
1477    /// Examples:
1478    ///
1479    /// ```no_run
1480    /// async {
1481    ///     use podman_api::Podman;
1482    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1483    ///
1484    ///     match podman.containers().list_mounted().await {
1485    ///         Ok(mounts) => println!("{:?}", mounts),
1486    ///         Err(e) => eprintln!("{}", e),
1487    ///     }
1488    /// };
1489    /// ```
1490    pub async fn list_mounted(&self) -> Result<Value> {
1491        self.podman.get_json("/libpod/containers/showmounted").await
1492    }}
1493
1494    api_doc! {
1495    Container => PruneLibpod
1496    |
1497    /// Remove containers not in use.
1498    ///
1499    /// Examples:
1500    ///
1501    /// ```no_run
1502    /// async {
1503    ///     use podman_api::Podman;
1504    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
1505    ///
1506    ///     match podman.containers().prune(&Default::default()).await {
1507    ///         Ok(report) => println!("{:?}", report),
1508    ///         Err(e) => eprintln!("{}", e),
1509    ///     }
1510    /// };
1511    /// ```
1512    pub async fn prune(
1513        &self,
1514        opts: &opts::ContainerPruneOpts,
1515    ) -> Result<Vec<models::ContainersPruneReportLibpod>> {
1516        let ep = url::construct_ep("/libpod/containers/prune", opts.serialize());
1517        self.podman
1518            .post_json(&ep, Payload::empty(), Headers::none())
1519            .await
1520    }}
1521}