1use std::env::{self};
2
3mod chunk_processor;
4mod connection;
5mod docker_host;
6mod util;
7
8use connection::DockerApiConnection;
9use docker_host::{ContextMetadata, DEFAULT_DOCKER_HOST, DOCKER_HOST_ENV_VAR, DockerConfig, DockerHost};
10use http::StatusCode;
11use util::encode_request;
12
13use crate::{DockerError, Result};
14
15pub struct DockerApi {
16 connection: DockerApiConnection,
17 buffer: Vec<u8>,
18}
19
20impl DockerApi {
21 pub fn new_with_host_resolution() -> Result<Self> {
23 let host = Self::get_docker_host()?;
24 let connection = DockerApiConnection::connect(host)?;
25
26 Ok(DockerApi {
27 connection,
28 buffer: Vec::new(),
29 })
30 }
31
32 pub fn image_is_present(&mut self, image: &str) -> Result<bool> {
34 let request = http::Request::builder()
35 .uri(format!("/images/{image}/json"))
36 .header("host", "docker")
37 .header("accept", "*/*")
38 .body(Vec::new())
39 .map_err(|_| DockerError::Other("failed to construct the image inspect request".into()))?;
40
41 self.buffer.clear();
43 encode_request(&request, &mut self.buffer)?;
44 let status_code = self.connection.make_request(&mut self.buffer)?;
45
46 Ok(status_code == StatusCode::OK)
47 }
48
49 pub fn pull_image(&mut self, image: &str) -> Result<()> {
51 let tag = image.split_once(":").map(|(_, tag)| tag).unwrap_or("latest");
52 let request = http::Request::builder()
53 .uri(format!("/images/create?fromImage={image}&tag={tag}"))
54 .method("POST")
55 .header("host", "docker")
56 .header("accept", "*/*")
57 .body(Vec::new())
58 .map_err(|_| {
59 DockerError::Other(format!("failed to construct the request to pull the '{image}' image").into())
60 })?;
61
62 self.buffer.clear();
64 encode_request(&request, &mut self.buffer)?;
65 let status_code = self.connection.make_request(&mut self.buffer)?;
66
67 if status_code != http::StatusCode::OK {
68 match status_code {
69 StatusCode::NOT_FOUND => Err(DockerError::Other("failed to pull the image: no such image".into())),
70 _ => Err(DockerError::Other("failed to pull the image".into())),
71 }
72 } else {
73 Ok(())
74 }
75 }
76
77 pub fn export_image(&mut self, image: &str) -> Result<&[u8]> {
79 let request = http::Request::builder()
80 .uri(format!("/images/{image}/get"))
81 .header("host", "docker")
82 .header("accept", "*/*")
83 .body(Vec::new())
84 .map_err(|_| {
85 DockerError::Other(format!("failed to construct the request to export the '{image}' image").into())
86 })?;
87
88 self.buffer.clear();
90 encode_request(&request, &mut self.buffer)?;
91 let status_code = self.connection.make_request(&mut self.buffer)?;
92
93 if status_code != http::StatusCode::OK {
94 match status_code {
95 StatusCode::NOT_FOUND => Err(DockerError::Other("failed to export the image: no such image".into())),
96 _ => Err(DockerError::Other("failed to export the image".into())),
97 }
98 } else {
99 Ok(&self.buffer)
100 }
101 }
102
103 pub fn into_buffer(self) -> Vec<u8> {
105 self.buffer
106 }
107
108 pub fn get_docker_host() -> Result<DockerHost> {
110 if let Ok(host) = env::var(DOCKER_HOST_ENV_VAR) {
111 return Ok(host.into());
113 }
114
115 let Some(docker_config) = DockerConfig::new()? else {
117 return Ok(DEFAULT_DOCKER_HOST.into());
119 };
120
121 let context_meta = ContextMetadata::new_from_docker_config(&docker_config)?;
122 if let Some(context_meta) = context_meta {
123 Ok(context_meta.into_docker_host().into())
124 } else {
125 Ok(DEFAULT_DOCKER_HOST.into())
127 }
128 }
129}