Pull using the libpod HTTP API (docker transport only).
(&self, image: &str)
| 181 | |
| 182 | /// Pull using the libpod HTTP API (docker transport only). |
| 183 | async fn pull_via_api(&self, image: &str) -> Result<()> { |
| 184 | let stream = tokio::net::UnixStream::connect(&self.socket_path) |
| 185 | .await |
| 186 | .context("Connecting to podman API socket")?; |
| 187 | let io = hyper_util::rt::TokioIo::new(stream); |
| 188 | |
| 189 | let (mut sender, conn) = hyper::client::conn::http1::handshake(io) |
| 190 | .await |
| 191 | .context("HTTP/1.1 handshake with podman")?; |
| 192 | |
| 193 | tokio::spawn(async move { |
| 194 | if let Err(e) = conn.await { |
| 195 | tracing::warn!("Podman HTTP connection error: {e}"); |
| 196 | } |
| 197 | }); |
| 198 | |
| 199 | let encoded_ref = |
| 200 | percent_encoding::utf8_percent_encode(image, percent_encoding::NON_ALPHANUMERIC); |
| 201 | let uri = format!( |
| 202 | "/{LIBPOD_API_VERSION}/libpod/images/pull?reference={encoded_ref}&pullProgress=true&policy=always" |
| 203 | ); |
| 204 | |
| 205 | tracing::debug!("POST {uri}"); |
| 206 | let response = sender |
| 207 | .send_request( |
| 208 | hyper::Request::builder() |
| 209 | .method(hyper::Method::POST) |
| 210 | .uri(&uri) |
| 211 | .header(hyper::header::HOST, "d") |
| 212 | .body(http_body_util::Empty::<hyper::body::Bytes>::new()) |
| 213 | .context("Building pull request")?, |
| 214 | ) |
| 215 | .await |
| 216 | .context("Sending pull request to podman")?; |
| 217 | |
| 218 | let status = response.status(); |
| 219 | if !status.is_success() { |
| 220 | let body = response |
| 221 | .into_body() |
| 222 | .collect() |
| 223 | .await |
| 224 | .context("Reading error response body")? |
| 225 | .to_bytes(); |
| 226 | anyhow::bail!( |
| 227 | "Podman libpod pull failed with HTTP {status}: {}", |
| 228 | String::from_utf8_lossy(&body) |
| 229 | ); |
| 230 | } |
| 231 | |
| 232 | // Turn the HTTP body into an AsyncBufRead so we can use read_line(). |
| 233 | let body_stream = |
| 234 | http_body_util::BodyStream::new(response.into_body()).filter_map(|r| async { |
| 235 | match r { |
| 236 | Ok(frame) => frame.into_data().ok().map(|b| Ok::<_, std::io::Error>(b)), |
| 237 | Err(e) => Some(Err(std::io::Error::other(e))), |
| 238 | } |
| 239 | }); |
| 240 | let reader = tokio_util::io::StreamReader::new(body_stream); |
no test coverage detected