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}