docker_api/api/
image.rs

1//! Create and manage images.
2
3use crate::{
4    models,
5    opts::{
6        ClearCacheOpts, ImageBuildOpts, ImageListOpts, ImagePruneOpts, ImagePushOpts,
7        ImageRemoveOpts, PullOpts, TagOpts,
8    },
9};
10
11use std::io::Read;
12
13use futures_util::{stream::Stream, TryFutureExt, TryStreamExt};
14
15use containers_api::{
16    conn::{Headers, Payload, AUTH_HEADER},
17    tarball,
18    url::{construct_ep, encoded_pair, encoded_pairs},
19};
20
21use crate::Result;
22
23impl_api_ty!(Image => name);
24
25impl Image {
26    impl_api_ep! {img: Image, resp
27        Inspect -> &format!("/images/{}/json", img.name), models::ImageInspect
28    }
29
30    api_doc! { Image => Delete
31    |
32    /// Remove this image with options.
33    ///
34    /// Use [`delete`](Image::delete) to delete without options.
35    pub async fn remove(&self, opts: &ImageRemoveOpts) -> Result<Vec<models::ImageDeleteResponseItem>> {
36        let ep =
37            containers_api::url::construct_ep(format!("/images/{}", self.name), opts.serialize());
38        self.docker.delete_json(ep.as_ref()).await
39    }}
40
41    api_doc! { Image => Delete
42    |
43    /// Delete this image with force.
44    ///
45    /// Use [`remove`](Image::remove) to delete with options.
46    pub async fn delete(&self) -> Result<Vec<models::ImageDeleteResponseItem>> {
47        self.docker
48            .delete_json(&format!("/images/{}", self.name))
49            .await
50    }}
51
52    api_doc! { Image => History
53    |
54    /// Lists the history of the images set of changes.
55    pub async fn history(&self) -> Result<models::ImageHistory200Response> {
56        self.docker
57            .get_json(&format!("/images/{}/history", self.name))
58            .await
59    }}
60
61    api_doc! { Image => Get
62    |
63    /// Export this image to a tarball.
64    pub fn export(&self) -> impl Stream<Item = Result<Vec<u8>>> + Unpin + '_ {
65        Box::pin(
66            self.docker
67                .get_stream(format!("/images/{}/get", self.name))
68                .map_ok(|c| c.to_vec()),
69        )
70    }}
71
72    api_doc! { Image => Tag
73    |
74    /// Adds a tag to an image.
75    pub async fn tag(&self, opts: &TagOpts) -> Result<()> {
76        let ep = construct_ep(format!("/images/{}/tag", self.name), opts.serialize());
77        self.docker
78            .post_string(&ep, Payload::empty(), Headers::none())
79            .await
80            .map(|_| ())
81    }}
82
83    api_doc! { Image => Push
84    |
85    /// Push an image to registry.
86    pub async fn push(&self, opts: &ImagePushOpts) -> Result<()> {
87        let ep = construct_ep(format!("/images/{}/push", self.name), opts.serialize());
88
89        let headers = opts
90            .auth_header()
91            .map(|auth| Headers::single(AUTH_HEADER, auth))
92            .unwrap_or_else(Headers::default);
93
94        self.docker
95            .post_string(&ep, Payload::empty(), Some(headers))
96            .await
97            .map(|_| ())
98    }}
99
100    api_doc! { Distribution => Inspect
101    |
102    /// Return image digest and platform information by contacting the registry.
103    pub async fn distribution_inspect(&self) -> Result<models::DistributionInspect> {
104        self.docker
105            .post_json(
106                &format!("/distribution/{}/json", self.name),
107                Payload::empty(),
108                Headers::none(),
109            )
110            .await
111    }}
112}
113
114impl Images {
115    impl_api_ep! {img: Image, resp
116        List -> "/images/json", models::ImageSummary
117        Prune ->  "/images/prune", models::ImagePrune200Response
118    }
119
120    api_doc! { Image => Build
121    |
122    /// Builds a new image by reading a Dockerfile in a target directory. If speed is
123    /// important consider using [`Image::build_par`](Image::build_par) that utilizes
124    /// parallel compression on big directories, to use it enable `par-compression` feature.
125    pub fn build<'docker>(
126        &'docker self,
127        opts: &ImageBuildOpts,
128    ) -> impl Stream<Item = Result<models::ImageBuildChunk>> + Unpin + 'docker {
129        let ep = construct_ep("/build", opts.serialize());
130        let mut bytes = vec![];
131        let tar_result = tarball::dir(&mut bytes, &opts.path);
132
133        let docker = &self.docker;
134        Box::pin(
135            async move {
136                tar_result?;
137
138                let value_stream =
139                    docker.post_into_stream(ep, Payload::Tar(bytes), Headers::none());
140
141                Ok(value_stream)
142            }
143            .try_flatten_stream(),
144        )
145    }}
146
147    api_doc! { Image => Build
148    |
149    #[cfg(feature = "par-compress")]
150    /// Builds a new image by reading a Dockerfile in a target directory. Uses parallel
151    /// compression algorithm to speed up the execution. For a single-threaded version check
152    /// [`Image::build`](Image::build).
153    pub fn build_par<'docker>(
154        &'docker self,
155        opts: &ImageBuildOpts,
156    ) -> impl Stream<Item = Result<models::ImageBuildChunk>> + Unpin + 'docker {
157        let ep = construct_ep("/build", opts.serialize());
158
159        let tar_result = tarball::dir_par(&opts.path);
160
161        let docker = &self.docker;
162        Box::pin(
163            async move {
164                let bytes = tar_result?;
165
166                let value_stream =
167                    docker.post_into_stream(ep, Payload::Tar(bytes), Headers::none());
168
169                Ok(value_stream)
170            }
171            .try_flatten_stream(),
172        )
173    }}
174
175    api_doc! { Image => Search
176    |
177    /// Search for docker images by term.
178    pub async fn search<T>(&self, term: T) -> Result<models::ImageSearch200Response>
179    where
180        T: AsRef<str>,
181    {
182        self.docker
183            .get_json(&construct_ep(
184                "/images/search",
185                Some(encoded_pair("term", term.as_ref())),
186            ))
187            .await
188    }}
189
190    api_doc! { Image => Pull
191    |
192    /// Pull and create a new docker images from an existing image.
193    pub fn pull<'docker>(
194        &'docker self,
195        opts: &PullOpts,
196    ) -> impl Stream<Item = Result<models::ImageBuildChunk>> + Unpin + 'docker {
197        let headers = opts.auth_header().map(|a| Headers::single(AUTH_HEADER, a));
198
199        Box::pin(self.docker.post_into_stream(
200            construct_ep("/images/create", opts.serialize()),
201            Payload::empty(),
202            headers,
203        ))
204    }}
205
206    api_doc! { Image => GetAll
207    |
208    /// Exports a collection of named images,
209    /// either by name, name:tag, or image id, into a tarball.
210    pub fn export<'docker>(
211        &'docker self,
212        names: Vec<&str>,
213    ) -> impl Stream<Item = Result<Vec<u8>>> + 'docker {
214        self.docker
215            .get_stream(format!(
216                "/images/get?{}",
217                encoded_pairs(names.iter().map(|n| ("names", *n)))
218            ))
219            .map_ok(|c| c.to_vec())
220    }}
221
222    api_doc! { Image => Load
223    |
224    /// Imports an image or set of images from a given tarball source.
225    /// Source can be uncompressed on compressed via gzip, bzip2 or xz.
226    pub fn import<'docker, R>(
227        &'docker self,
228        mut tarball: R,
229    ) -> impl Stream<Item = Result<models::ImageBuildChunk>> + Unpin + 'docker
230    where
231        R: Read + Send + 'docker,
232    {
233        Box::pin(
234            async move {
235                let mut bytes = Vec::default();
236
237                tarball.read_to_end(&mut bytes)?;
238
239                let value_stream = self.docker.post_into_stream(
240                    "/images/load",
241                    Payload::Tar(bytes),
242                    Headers::none(),
243                );
244                Ok(value_stream)
245            }
246            .try_flatten_stream(),
247        )
248    }}
249
250    api_doc! { Image => Push
251    |
252    /// Push an image to registry.
253    pub async fn push(&self, name: impl Into<crate::Id>, opts: &ImagePushOpts) -> Result<()> {
254        let image = Image::new(self.docker.clone(), name);
255        image.push(opts).await
256    }}
257
258    api_doc! { Build => Prune
259    |
260    /// Clear image build cache.
261    pub async fn clear_cache(
262        &self,
263        opts: &ClearCacheOpts,
264    ) -> Result<models::BuildPrune200Response> {
265        self.docker
266            .post_json(
267                construct_ep("/build/prune", opts.serialize()),
268                Payload::empty(),
269                Headers::none(),
270            )
271            .await
272    }}
273}