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}