Skip to main content

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}