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}