podman_api/api/images.rs
1use crate::{
2 api::ApiResource,
3 conn::{Headers, Payload},
4 models, opts, Error, Result, Stream, TryStreamExt,
5};
6
7use containers_api::{tarball, url};
8
9impl_api_ty!(
10 Image => id
11);
12
13impl Image {
14 api_doc! {
15 Image => InspectLibpod
16 |
17 /// Obtain low-level information about this image.
18 ///
19 /// Examples:
20 ///
21 /// ```no_run
22 /// async {
23 /// use podman_api::Podman;
24 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
25 ///
26 /// match podman.images().get("debian").inspect().await {
27 /// Ok(info) => println!("{:?}", info),
28 /// Err(e) => eprintln!("{}", e),
29 /// }
30 /// };
31 /// ```
32 pub async fn inspect(&self) -> Result<models::InspectImageResponseLibpod> {
33 self.podman
34 .get_json(&format!("/libpod/images/{}/json", &self.id))
35 .await
36 }}
37
38 api_doc! {
39 Image => HistoryLibpod
40 |
41 /// Return parent layers of an image.
42 ///
43 /// Examples:
44 ///
45 /// ```no_run
46 /// async {
47 /// use podman_api::Podman;
48 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
49 ///
50 /// match podman.images().get("debian").history().await {
51 /// Ok(info) => println!("{:?}", info),
52 /// Err(e) => eprintln!("{}", e),
53 /// }
54 /// };
55 /// ```
56 pub async fn history(&self) -> Result<Vec<models::HistoryResponse>> {
57 self.podman
58 .get_json(&format!("/libpod/images/{}/history", &self.id))
59 .await
60 }}
61
62 api_doc! {
63 Image => ExistsLibpod
64 |
65 /// Quick way to determine if a image exists by name or ID.
66 ///
67 /// Examples:
68 ///
69 /// ```no_run
70 /// async {
71 /// use podman_api::Podman;
72 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
73 ///
74 /// match podman.images().get("debian").exists().await {
75 /// Ok(exists) => if exists {
76 /// println!("image exists!");
77 /// } else {
78 /// println!("image doesn't exists!");
79 /// },
80 /// Err(e) => eprintln!("check failed: {}", e),
81 /// }
82 /// };
83 /// ```
84 pub async fn exists(&self) -> Result<bool> {
85 self.podman
86 .resource_exists(ApiResource::Images, &self.id)
87 .await
88 }}
89
90 api_doc! {
91 Image => DeleteLibpod
92 |
93 /// Delete this image from local storage. To forcefully remove an image use
94 /// [`Image::remove`](Image::remove).
95 ///
96 /// Examples:
97 ///
98 /// ```no_run
99 /// async {
100 /// use podman_api::Podman;
101 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
102 ///
103 /// if let Err(e) = podman.images().get("debian").delete().await {
104 /// eprintln!("{}", e);
105 /// }
106 /// };
107 /// ```
108 pub async fn delete(&self) -> Result<()> {
109 self.podman
110 .delete(&format!("/libpod/images/{}", &self.id))
111 .await
112 .map(|_| ())
113 }}
114
115 api_doc! {
116 Image => DeleteLibpod
117 |
118 /// Remove this image forcefully from local storage. To remove the image normally use
119 /// [`Image::delete`](Image::delete).
120 ///
121 /// Examples:
122 ///
123 /// ```no_run
124 /// async {
125 /// use podman_api::Podman;
126 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
127 ///
128 /// if let Err(e) = podman.images().get("debian").remove().await {
129 /// eprintln!("{}", e);
130 /// }
131 /// };
132 /// ```
133 pub async fn remove(&self) -> Result<()> {
134 let ep = url::construct_ep(
135 format!("/libpod/images/{}", &self.id),
136 Some(url::encoded_pair("force", true)),
137 );
138 self.podman.delete(&ep).await.map(|_| ())
139 }}
140
141 api_doc! {
142 Image => TagLibpod
143 |
144 /// Tag an image so that it becomes part of a repository.
145 ///
146 /// Examples:
147 ///
148 /// ```no_run
149 /// async {
150 /// use podman_api::Podman;
151 /// use podman_api::opts::ImageTagOpts;
152 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
153 ///
154 /// if let Err(e) = podman
155 /// .images()
156 /// .get("debian")
157 /// .tag(
158 /// &ImageTagOpts::builder()
159 /// .repo("my.custom.repo/debian")
160 /// .tag("1.0.0")
161 /// .build(),
162 /// )
163 /// .await
164 /// {
165 /// eprintln!("{}", e);
166 /// }
167 /// };
168 /// ```
169 pub async fn tag(&self, opts: &opts::ImageTagOpts) -> Result<()> {
170 let ep = url::construct_ep(format!("/libpod/images/{}/tag", &self.id), opts.serialize());
171 self.podman
172 .post(&ep, Payload::empty(), Headers::none())
173 .await
174 .map(|_| ())
175 }}
176
177 api_doc! {
178 Image => UntagLibpod
179 |
180 /// Untag an image. If repo and tag are not specified, all tags are removed
181 /// from the image.
182 ///
183 /// Examples:
184 ///
185 /// ```no_run
186 /// async {
187 /// use podman_api::Podman;
188 /// use podman_api::opts::ImageTagOpts;
189 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
190 ///
191 /// if let Err(e) = podman
192 /// .images()
193 /// .get("debian")
194 /// .untag(
195 /// &ImageTagOpts::builder()
196 /// .repo("my.custom.repo/debian")
197 /// .tag("1.0.0")
198 /// .build(),
199 /// )
200 /// .await
201 /// {
202 /// eprintln!("{}", e);
203 /// }
204 /// };
205 /// ```
206 pub async fn untag(&self, opts: &opts::ImageTagOpts) -> Result<()> {
207 let ep = url::construct_ep(format!("/libpod/images/{}/untag", &self.id), opts.serialize());
208 self.podman
209 .post(&ep, Payload::empty(), Headers::none())
210 .await
211 .map(|_| ())
212 }}
213
214 api_doc! {
215 Image => GetLibpod
216 |
217 /// Export this image.
218 ///
219 /// Examples:
220 ///
221 /// ```no_run
222 /// async {
223 /// use podman_api::Podman;
224 /// use futures_util::stream::TryStreamExt;
225 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
226 /// let image = podman.images().get("myimage");
227 ///
228 /// let export_stream = image.export(&Default::default());
229 /// let export_data = export_stream.try_concat().await.expect("image archive");
230 /// assert!(!export_data.is_empty());
231 /// };
232 /// ```
233 pub fn export(
234 &self,
235 opts: &opts::ImageExportOpts,
236 ) -> impl Stream<Item = Result<Vec<u8>>> + Unpin + '_ {
237 let ep = url::construct_ep(format!("/libpod/images/{}/get", &self.id), opts.serialize());
238 Box::pin(self.podman.get_stream(ep).map_ok(|c| c.to_vec()))
239 }}
240
241 api_doc! {
242 Image => ChangesLibpod
243 |
244 /// Returns which files in this image's filesystem have been added, deleted, or modified.
245 ///
246 /// Examples:
247 ///
248 /// ```no_run
249 /// async {
250 /// use podman_api::Podman;
251 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
252 ///
253 /// match podman
254 /// .images()
255 /// .get("79c93f220e3e")
256 /// .changes(&Default::default())
257 /// .await
258 /// {
259 /// Ok(changes) => println!("{:?}", changes),
260 /// Err(e) => eprintln!("{}", e),
261 /// }
262 /// };
263 /// ```
264 pub async fn changes(
265 &self,
266 opts: &opts::ChangesOpts,
267 ) -> Result<Vec<models::ContainerChangeResponseItem>> {
268 let ep = url::construct_ep(
269 format!("/libpod/images/{}/changes", &self.id),
270 opts.serialize(),
271 );
272 self.podman.get_json(&ep).await
273 }}
274
275 api_doc! {
276 Image => TreeLibpod
277 |
278 /// Retrieve the image tree for this image.
279 ///
280 /// Examples:
281 ///
282 /// ```no_run
283 /// async {
284 /// use podman_api::Podman;
285 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
286 ///
287 /// match podman
288 /// .images()
289 /// .get("79c93f220e3e")
290 /// .tree(&Default::default())
291 /// .await
292 /// {
293 /// Ok(tree) => println!("{:?}", tree),
294 /// Err(e) => eprintln!("{}", e),
295 /// }
296 /// };
297 /// ```
298 pub async fn tree(&self, opts: &opts::ImageTreeOpts) -> Result<models::TreeResponse> {
299 let ep = url::construct_ep(
300 format!("/libpod/images/{}/tree", &self.id),
301 opts.serialize(),
302 );
303 self.podman.get_json(&ep).await
304 }}
305
306 api_doc! {
307 Image => PushLibpod
308 |
309 /// Push this image to a container registry.
310 ///
311 /// Examples:
312 ///
313 /// ```no_run
314 /// async {
315 /// use podman_api::opts::{ImagePushOpts, RegistryAuth};
316 /// use podman_api::Podman;
317 /// use futures_util::StreamExt;
318 ///
319 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
320 ///
321 /// let image = podman.images().get("alpine");
322 /// let mut events = image.push(
323 /// &ImagePushOpts::builder()
324 /// .destination("my-destination")
325 /// .tls_verify(true)
326 /// .auth(
327 /// RegistryAuth::builder()
328 /// .username("test")
329 /// .password("test")
330 /// .server_address("https://my-registry")
331 /// .build(),
332 /// )
333 /// .build(),
334 /// );
335 ///
336 /// for event in events.next().await {
337 /// match event {
338 /// Ok(line) => println!("{line}"),
339 /// Err(e) => eprintln!("{e}"),
340 /// }
341 /// }
342 /// };
343 /// ```
344 pub fn push(&self, opts: &opts::ImagePushOpts) -> impl Stream<Item = Result<String>> + Unpin + '_ {
345 let headers = opts
346 .auth_header()
347 .map(|a| Headers::single(crate::conn::AUTH_HEADER, a));
348
349 let ep = url::construct_ep(
350 format!("/libpod/images/{}/push", &self.id),
351 opts.serialize(),
352 );
353
354 let reader = Box::pin(
355 self.podman
356 .post_stream(
357 ep,
358 Payload::empty(),
359 headers,
360 )
361 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e)),
362 )
363 .into_async_read();
364
365 Box::pin(
366 futures_codec::FramedRead::new(reader, futures_codec::LinesCodec)
367 .map_err(Error::IO)
368 )
369 }}
370}
371
372impl Images {
373 api_doc! {
374 Image => BuildLibpod
375 |
376 /// Build an image from the given Dockerfile(s)
377 ///
378 /// Examples:
379 ///
380 /// ```no_run
381 /// async {
382 /// use podman_api::Podman;
383 /// use futures_util::StreamExt;
384 /// use podman_api::opts::ImageBuildOpts;
385 ///
386 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
387 ///
388 /// let opts = ImageBuildOpts::builder("http://some.url.to/Dockerfile")
389 /// .tag("myimage:1.0.0")
390 /// .build();
391 ///
392 /// let images = podman.images();
393 /// match images.build(&opts) {
394 /// Ok(mut build_stream) => while let Some(chunk) = build_stream.next().await {
395 /// match chunk {
396 /// Ok(chunk) => println!("{:?}", chunk),
397 /// Err(e) => eprintln!("{}", e),
398 /// }
399 /// },
400 /// Err(e) => eprintln!("{}", e),
401 /// };
402 /// };
403 /// ```
404 pub fn build(
405 &self,
406 opts: &opts::ImageBuildOpts,
407 ) -> Result<impl Stream<Item = Result<models::ImageBuildLibpod200Response>> + Unpin + '_> {
408 let mut bytes = Vec::default();
409 let path = opts
410 .get_param("path")
411 .ok_or_else(|| Error::OptsSerialization("expected a path to build context".into()))?;
412 tarball::dir(&mut bytes, path)?;
413
414 let ep = url::construct_ep("/libpod/build", opts.serialize());
415 let reader = Box::pin(
416 self.podman
417 .post_stream(ep, Payload::Tar(bytes), Headers::none())
418 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e)),
419 )
420 .into_async_read();
421
422 Ok(Box::pin(
423 futures_codec::FramedRead::new(reader, futures_codec::LinesCodec)
424 .map_err(Error::IO)
425 .and_then(|s: String| async move {
426 match serde_json::from_str(&s) {
427 Ok(s) => Ok(s),
428 Err(e) => match serde_json::from_str::<models::JsonError>(&s) {
429 Ok(e) => Err(Error::ServerError(e)),
430 Err(_) => Err(e.into())
431 }
432 }
433 }),
434 ))
435 }}
436
437 api_doc! {
438 Image => ListLibpod
439 |
440 /// Returns a list of images.
441 ///
442 /// Examples:
443 ///
444 /// ```no_run
445 /// async {
446 /// use podman_api::Podman;
447 /// use podman_api::opts::{ImageListOpts, ImageListFilter};
448 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
449 ///
450 /// for image in podman
451 /// .images()
452 /// .list(
453 /// &ImageListOpts::builder()
454 /// .all(true)
455 /// .filter([ImageListFilter::Dangling(true)])
456 /// .build(),
457 /// )
458 /// .await
459 /// .unwrap()
460 /// {
461 /// println!("{:?}", image);
462 /// }
463 /// };
464 /// ```
465 pub async fn list(
466 &self,
467 opts: &opts::ImageListOpts,
468 ) -> Result<Vec<models::LibpodImageSummary>> {
469 let ep = url::construct_ep("/libpod/images/json", opts.serialize());
470 self.podman.get_json(&ep).await
471 }}
472
473 api_doc! {
474 Image => PullLibpod
475 |
476 /// Pull one or more images from a container registry.
477 ///
478 /// Examples:
479 ///
480 /// ```no_run
481 /// async {
482 /// use futures_util::{StreamExt, TryStreamExt};
483 /// use podman_api::{Error, Podman};
484 /// use podman_api::opts::PullOpts;
485 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
486 ///
487 /// let events = podman
488 /// .images()
489 /// .pull(
490 /// &PullOpts::builder()
491 /// .reference("docker.io/library/alpine")
492 /// .build(),
493 /// )
494 /// .map(|report| {
495 /// report.and_then(|report| match report.error {
496 /// Some(error) => Err(Error::InvalidResponse(error)),
497 /// None => Ok(report),
498 /// })
499 /// })
500 /// .try_collect::<Vec<_>>()
501 /// .await;
502 ///
503 /// if let Err(e) = events {
504 /// eprintln!("{}", e);
505 /// }
506 /// };
507 /// ```
508 pub fn pull(
509 &self,
510 opts: &opts::PullOpts,
511 ) -> impl Stream<Item = Result<models::LibpodImagesPullReport>> + Unpin + '_ {
512 let ep = url::construct_ep("/libpod/images/pull", opts.serialize());
513 let reader = Box::pin(
514 self.podman
515 .post_stream(
516 ep,
517 Payload::empty(),
518 opts.auth_header()
519 .map(|a| Headers::single(crate::conn::AUTH_HEADER, a)),
520 )
521 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e)),
522 )
523 .into_async_read();
524
525 Box::pin(
526 futures_codec::FramedRead::new(reader, futures_codec::LinesCodec)
527 .map_err(Error::IO)
528 .and_then(|s: String| async move {
529 serde_json::from_str(&s).map_err(Error::SerdeJsonError)
530 }),
531 )
532 }}
533
534 api_doc! {
535 Image => LoadLibpod
536 |
537 /// Load an image (oci-archive or docker-archive) stream.
538 ///
539 /// Examples:
540 ///
541 /// ```no_run
542 /// async {
543 /// use podman_api::Podman;
544 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
545 ///
546 /// let image = std::fs::read("image_archive").unwrap();
547 ///
548 /// match podman.images().load(&image).await {
549 /// Ok(info) => println!("{:?}", info),
550 /// Err(e) => eprintln!("{}", e),
551 /// }
552 /// };
553 /// ```
554 pub async fn load(&self, image: impl AsRef<[u8]>) -> Result<models::ImageLoadReport> {
555 let archive = image.as_ref().to_vec();
556 self.podman
557 .post_json(
558 "/libpod/images/load",
559 Payload::XTar(archive),
560 Headers::none(),
561 )
562 .await
563 }}
564
565 api_doc! {
566 Image => ImportLibpod
567 |
568 /// Import a previously exported tarball as an image.
569 ///
570 /// Examples:
571 ///
572 /// ```no_run
573 /// async {
574 /// use podman_api::Podman;
575 /// use podman_api::opts::ImageImportOpts;
576 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
577 ///
578 /// let image = vec![0, 1];
579 ///
580 /// if let Err(e) = podman
581 /// .images()
582 /// .import(
583 /// &ImageImportOpts::builder()
584 /// .reference("rockylinux/rockylinux:8")
585 /// .build(),
586 /// image
587 /// )
588 /// .await
589 /// {
590 /// eprintln!("{}", e);
591 /// }
592 /// };
593 /// ```
594 pub async fn import(
595 &self,
596 opts: &opts::ImageImportOpts,
597 image: impl AsRef<[u8]>,
598 ) -> Result<models::LibpodImagesPullReport> {
599 let archive = image.as_ref().to_vec();
600 self.podman
601 .post_json(
602 url::construct_ep("/libpod/images/import", opts.serialize()),
603 Payload::XTar(archive),
604 Headers::none(),
605 )
606 .await
607 }}
608
609 api_doc! {
610 Image => DeleteAllLibpod
611 |
612 /// Remove multiple images. To remove a single image use
613 /// [`Image::delete`](Image::delete) or [`Image::remove`](Image::remove).
614 ///
615 /// Examples:
616 ///
617 /// ```no_run
618 /// async {
619 /// use podman_api::Podman;
620 /// use podman_api::opts::ImagesRemoveOpts;
621 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
622 ///
623 /// match podman
624 /// .images()
625 /// .remove(&ImagesRemoveOpts::builder().all(true).force(true).build())
626 /// .await
627 /// {
628 /// Ok(info) => println!("{:?}", info),
629 /// Err(e) => eprintln!("{}", e),
630 /// }
631 /// };
632 /// ```
633 pub async fn remove(
634 &self,
635 opts: &opts::ImagesRemoveOpts,
636 ) -> Result<models::LibpodImagesRemoveReport> {
637 let ep = url::construct_ep("/libpod/images/remove", opts.serialize());
638 self.podman.delete_json(&ep).await
639 }}
640
641 api_doc! {
642 Image => PruneLibpod
643 |
644 /// Remove images that are not being used by a container.
645 ///
646 /// Examples:
647 ///
648 /// ```no_run
649 /// async {
650 /// use podman_api::Podman;
651 /// use podman_api::opts::ImagePruneOpts;
652 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
653 ///
654 /// match podman
655 /// .images()
656 /// .prune(
657 /// &ImagePruneOpts::builder()
658 /// .all(true)
659 /// .build()
660 /// ).await {
661 /// Ok(report) => println!("{:?}", report),
662 /// Err(e) => eprintln!("{}", e),
663 /// }
664 /// };
665 /// ```
666 pub async fn prune(
667 &self,
668 opts: &opts::ImagePruneOpts,
669 ) -> Result<Option<Vec<models::PruneReport>>> {
670 let ep = url::construct_ep("/libpod/images/prune", opts.serialize());
671 self.podman
672 .post_json(&ep, Payload::empty(), Headers::none())
673 .await
674 }}
675
676 api_doc! {
677 Image => SearchLibpod
678 |
679 /// Search registries for images.
680 ///
681 /// Examples:
682 ///
683 /// ```no_run
684 /// async {
685 /// use podman_api::Podman;
686 /// use podman_api::opts::ImageSearchOpts;
687 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
688 ///
689 /// match podman
690 /// .images()
691 /// .search(
692 /// &ImageSearchOpts::builder()
693 /// .list_tags(true)
694 /// .build()
695 /// ).await {
696 /// Ok(images) => println!("{:?}", images),
697 /// Err(e) => eprintln!("{}", e),
698 /// }
699 /// };
700 /// ```
701 pub async fn search(
702 &self,
703 opts: &opts::ImageSearchOpts,
704 ) -> Result<Vec<models::RegistrySearchResponse>> {
705 let ep = url::construct_ep("/libpod/images/search", opts.serialize());
706 self.podman.get_json(&ep).await
707 }}
708
709 api_doc! {
710 Image => ExportLibpod
711 |
712 /// Export multiple images into a single object.
713 ///
714 /// Examples:
715 ///
716 /// ```no_run
717 /// async {
718 /// use podman_api::Podman;
719 /// use podman_api::opts::ImagesExportOpts;
720 /// use futures_util::stream::TryStreamExt;
721 /// let podman = Podman::unix("/run/user/1000/podman/podman.sock");
722 /// let images = podman.images();
723 ///
724 /// let full_id_a = "3290fj209...".to_string();
725 /// let full_id_b = "ioajfoi32...".to_string();
726 ///
727 /// let export_opts = ImagesExportOpts::builder()
728 /// .references([full_id_a, full_id_b])
729 /// .build();
730 ///
731 /// let export_stream = images.export(&export_opts);
732 /// let export_data = export_stream.try_concat().await.expect("images archive");
733 /// assert!(!export_data.is_empty());
734 /// };
735 /// ```
736 pub fn export(&self, opts: &opts::ImagesExportOpts) -> impl Stream<Item = Result<Vec<u8>>> + Unpin + '_ {
737 let ep = url::construct_ep("/libpod/images/export", opts.serialize());
738 Box::pin(self.podman.get_stream(ep).map_ok(|c| c.to_vec()))
739 }}
740}