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::Podman;
316    ///     use podman_api::opts::{RegistryAuth, ImagePushOpts};
317    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
318    ///
319    ///     match podman.images().get("alpine").push(
320    ///         &ImagePushOpts::builder()
321    ///             .destinations("my-destination")
322    ///             .tls_verify(true)
323    ///             .auth(
324    ///                 RegistryAuth::builder()
325    ///                     .username("test")
326    ///                     .password("test")
327    ///                     .server_address("https://my-registry")
328    ///                     .build(),
329    ///             )
330    ///             .build(),
331    ///     ).await {
332    ///         Ok(s) => println!("{}", s),
333    ///         Err(e) => eprintln!("{}", e),
334    ///     };
335    /// };
336    /// ```
337    pub async fn push(&self, opts: &opts::ImagePushOpts) -> Result<String> {
338        let headers = opts
339            .auth_header()
340            .map(|a| Headers::single(crate::conn::AUTH_HEADER, a));
341
342        let ep = url::construct_ep(
343            format!("/libpod/images/{}/push", &self.id),
344            opts.serialize(),
345        );
346
347        self.podman
348            .post_string(&ep, Payload::empty(), headers)
349            .await
350    }}
351}
352
353impl Images {
354    api_doc! {
355    Image => BuildLibpod
356    |
357    /// Build an image from the given Dockerfile(s)
358    ///
359    /// Examples:
360    ///
361    /// ```no_run
362    /// async {
363    ///     use podman_api::Podman;
364    ///     use futures_util::StreamExt;
365    ///     use podman_api::opts::ImageBuildOpts;
366    ///
367    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
368    ///
369    ///     let opts = ImageBuildOpts::builder("http://some.url.to/Dockerfile")
370    ///             .tag("myimage:1.0.0")
371    ///             .build();
372    ///
373    ///     let images = podman.images();
374    ///     match images.build(&opts) {
375    ///         Ok(mut build_stream) => while let Some(chunk) = build_stream.next().await {
376    ///             match chunk {
377    ///                 Ok(chunk) => println!("{:?}", chunk),
378    ///                 Err(e) => eprintln!("{}", e),
379    ///             }
380    ///         },
381    ///         Err(e) => eprintln!("{}", e),
382    ///     };
383    /// };
384    /// ```
385    pub fn build(
386        &self,
387        opts: &opts::ImageBuildOpts,
388    ) -> Result<impl Stream<Item = Result<models::ImageBuildLibpod200Response>> + Unpin + '_> {
389        let mut bytes = Vec::default();
390        let path = opts
391            .get_param("path")
392            .ok_or_else(|| Error::OptsSerialization("expected a path to build context".into()))?;
393        tarball::dir(&mut bytes, path)?;
394
395        let ep = url::construct_ep("/libpod/build", opts.serialize());
396        let reader = Box::pin(
397            self.podman
398                .post_stream(ep, Payload::Tar(bytes), Headers::none())
399                .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e)),
400        )
401        .into_async_read();
402
403        Ok(Box::pin(
404            futures_codec::FramedRead::new(reader, futures_codec::LinesCodec)
405                .map_err(Error::IO)
406                .and_then(|s: String| async move {
407                    match serde_json::from_str(&s) {
408                        Ok(s) => Ok(s),
409                        Err(e) => match serde_json::from_str::<models::JsonError>(&s) {
410                            Ok(e) => Err(Error::ServerError(e)),
411                            Err(_) => Err(e.into())
412                        }
413                    }
414                }),
415        ))
416    }}
417
418    api_doc! {
419    Image => ListLibpod
420    |
421    /// Returns a list of images.
422    ///
423    /// Examples:
424    ///
425    /// ```no_run
426    /// async {
427    ///     use podman_api::Podman;
428    ///     use podman_api::opts::{ImageListOpts, ImageListFilter};
429    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
430    ///
431    ///     for image in podman
432    ///         .images()
433    ///         .list(
434    ///             &ImageListOpts::builder()
435    ///                 .all(true)
436    ///                 .filter([ImageListFilter::Dangling(true)])
437    ///                 .build(),
438    ///         )
439    ///         .await
440    ///         .unwrap()
441    ///     {
442    ///         println!("{:?}", image);
443    ///     }
444    /// };
445    /// ```
446    pub async fn list(
447        &self,
448        opts: &opts::ImageListOpts,
449    ) -> Result<Vec<models::LibpodImageSummary>> {
450        let ep = url::construct_ep("/libpod/images/json", opts.serialize());
451        self.podman.get_json(&ep).await
452    }}
453
454    api_doc! {
455    Image => PullLibpod
456    |
457    /// Pull one or more images from a container registry.
458    ///
459    /// Examples:
460    ///
461    /// ```no_run
462    /// async {
463    ///     use futures_util::{StreamExt, TryStreamExt};
464    ///     use podman_api::{Error, Podman};
465    ///     use podman_api::opts::PullOpts;
466    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
467    ///
468    ///     let events = podman
469    ///         .images()
470    ///         .pull(
471    ///             &PullOpts::builder()
472    ///                 .reference("docker.io/library/alpine")
473    ///                 .build(),
474    ///             )
475    ///             .map(|report| {
476    ///                 report.and_then(|report| match report.error {
477    ///                     Some(error) => Err(Error::InvalidResponse(error)),
478    ///                     None => Ok(report),
479    ///                 })
480    ///             })
481    ///             .try_collect::<Vec<_>>()
482    ///             .await;
483    ///
484    ///     if let Err(e) = events {
485    ///         eprintln!("{}", e);
486    ///     }
487    /// };
488    /// ```
489    pub fn pull(
490        &self,
491        opts: &opts::PullOpts,
492    ) -> impl Stream<Item = Result<models::LibpodImagesPullReport>> + Unpin + '_ {
493        let ep = url::construct_ep("/libpod/images/pull", opts.serialize());
494        let reader = Box::pin(
495            self.podman
496                .post_stream(
497                    ep,
498                    Payload::empty(),
499                    opts.auth_header()
500                        .map(|a| Headers::single(crate::conn::AUTH_HEADER, a)),
501                )
502                .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e)),
503        )
504        .into_async_read();
505
506        Box::pin(
507            futures_codec::FramedRead::new(reader, futures_codec::LinesCodec)
508                .map_err(Error::IO)
509                .and_then(|s: String| async move {
510                    serde_json::from_str(&s).map_err(Error::SerdeJsonError)
511                }),
512        )
513    }}
514
515    api_doc! {
516    Image => LoadLibpod
517    |
518    /// Load an image (oci-archive or docker-archive) stream.
519    ///
520    /// Examples:
521    ///
522    /// ```no_run
523    /// async {
524    ///     use podman_api::Podman;
525    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
526    ///
527    ///     let image = std::fs::read("image_archive").unwrap();
528    ///
529    ///     match podman.images().load(&image).await {
530    ///         Ok(info) => println!("{:?}", info),
531    ///         Err(e) => eprintln!("{}", e),
532    ///     }
533    /// };
534    /// ```
535    pub async fn load(&self, image: impl AsRef<[u8]>) -> Result<models::ImageLoadReport> {
536        let archive = image.as_ref().to_vec();
537        self.podman
538            .post_json(
539                "/libpod/images/load",
540                Payload::XTar(archive),
541                Headers::none(),
542            )
543            .await
544    }}
545
546    api_doc! {
547    Image => ImportLibpod
548    |
549    /// Import a previously exported tarball as an image.
550    ///
551    /// Examples:
552    ///
553    /// ```no_run
554    /// async {
555    ///     use podman_api::Podman;
556    ///     use podman_api::opts::ImageImportOpts;
557    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
558    ///
559    ///     let image = vec![0, 1];
560    ///
561    ///     if let Err(e) = podman
562    ///         .images()
563    ///         .import(
564    ///             &ImageImportOpts::builder()
565    ///                 .reference("rockylinux/rockylinux:8")
566    ///                 .build(),
567    ///             image
568    ///         )
569    ///         .await
570    ///     {
571    ///         eprintln!("{}", e);
572    ///     }
573    /// };
574    /// ```
575    pub async fn import(
576        &self,
577        opts: &opts::ImageImportOpts,
578        image: impl AsRef<[u8]>,
579    ) -> Result<models::LibpodImagesPullReport> {
580        let archive = image.as_ref().to_vec();
581        self.podman
582            .post_json(
583                url::construct_ep("/libpod/images/import", opts.serialize()),
584                Payload::XTar(archive),
585                Headers::none(),
586            )
587            .await
588    }}
589
590    api_doc! {
591    Image => DeleteAllLibpod
592    |
593    /// Remove multiple images. To remove a single image use
594    /// [`Image::delete`](Image::delete) or [`Image::remove`](Image::remove).
595    ///
596    /// Examples:
597    ///
598    /// ```no_run
599    /// async {
600    ///     use podman_api::Podman;
601    ///     use podman_api::opts::ImagesRemoveOpts;
602    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
603    ///
604    ///     match podman
605    ///         .images()
606    ///         .remove(&ImagesRemoveOpts::builder().all(true).force(true).build())
607    ///         .await
608    ///     {
609    ///         Ok(info) => println!("{:?}", info),
610    ///         Err(e) => eprintln!("{}", e),
611    ///     }
612    /// };
613    /// ```
614    pub async fn remove(
615        &self,
616        opts: &opts::ImagesRemoveOpts,
617    ) -> Result<models::LibpodImagesRemoveReport> {
618        let ep = url::construct_ep("/libpod/images/remove", opts.serialize());
619        self.podman.delete_json(&ep).await
620    }}
621
622    api_doc! {
623    Image => PruneLibpod
624    |
625    /// Remove images that are not being used by a container.
626    ///
627    /// Examples:
628    ///
629    /// ```no_run
630    /// async {
631    ///     use podman_api::Podman;
632    ///     use podman_api::opts::ImagePruneOpts;
633    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
634    ///
635    ///     match podman
636    ///         .images()
637    ///         .prune(
638    ///             &ImagePruneOpts::builder()
639    ///                 .all(true)
640    ///                 .build()
641    ///         ).await {
642    ///             Ok(report) => println!("{:?}", report),
643    ///             Err(e) => eprintln!("{}", e),
644    ///     }
645    /// };
646    /// ```
647    pub async fn prune(
648        &self,
649        opts: &opts::ImagePruneOpts,
650    ) -> Result<Option<Vec<models::PruneReport>>> {
651        let ep = url::construct_ep("/libpod/images/prune", opts.serialize());
652        self.podman
653            .post_json(&ep, Payload::empty(), Headers::none())
654            .await
655    }}
656
657    api_doc! {
658    Image => SearchLibpod
659    |
660    /// Search registries for images.
661    ///
662    /// Examples:
663    ///
664    /// ```no_run
665    /// async {
666    ///     use podman_api::Podman;
667    ///     use podman_api::opts::ImageSearchOpts;
668    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
669    ///
670    ///     match podman
671    ///         .images()
672    ///         .search(
673    ///             &ImageSearchOpts::builder()
674    ///                 .list_tags(true)
675    ///                 .build()
676    ///         ).await {
677    ///             Ok(images) => println!("{:?}", images),
678    ///             Err(e) => eprintln!("{}", e),
679    ///     }
680    /// };
681    /// ```
682    pub async fn search(
683        &self,
684        opts: &opts::ImageSearchOpts,
685    ) -> Result<Vec<models::RegistrySearchResponse>> {
686        let ep = url::construct_ep("/libpod/images/search", opts.serialize());
687        self.podman.get_json(&ep).await
688    }}
689
690    api_doc! {
691    Image => ExportLibpod
692    |
693    /// Export multiple images into a single object.
694    ///
695    /// Examples:
696    ///
697    /// ```no_run
698    /// async {
699    ///     use podman_api::Podman;
700    ///     use podman_api::opts::ImagesExportOpts;
701    ///     use futures_util::stream::TryStreamExt;
702    ///     let podman = Podman::unix("/run/user/1000/podman/podman.sock");
703    ///     let images = podman.images();
704    ///
705    ///     let full_id_a = "3290fj209...".to_string();
706    ///     let full_id_b = "ioajfoi32...".to_string();
707    ///
708    ///     let export_opts = ImagesExportOpts::builder()
709    ///         .references([full_id_a, full_id_b])
710    ///         .build();
711    ///
712    ///     let export_stream = images.export(&export_opts);
713    ///     let export_data = export_stream.try_concat().await.expect("images archive");
714    ///     assert!(!export_data.is_empty());
715    /// };
716    /// ```
717    pub fn export(&self, opts: &opts::ImagesExportOpts) -> impl Stream<Item = Result<Vec<u8>>> + Unpin + '_ {
718        let ep = url::construct_ep("/libpod/images/export", opts.serialize());
719        Box::pin(self.podman.get_stream(ep).map_ok(|c| c.to_vec()))
720    }}
721}