From 2d971ef50084a5d6d048491c56be121a3c8ef40b7f083b7f26a62a6a3094fef5 Mon Sep 17 00:00:00 2001 From: Till Wegmueller Date: Tue, 7 Apr 2026 15:52:02 +0200 Subject: [PATCH] Replace image download with vm-manager ImageManager Use vm-manager's ImageManager::download() for streaming image downloads with automatic zstd decompression, replacing the hand-rolled reqwest + zstd code. Supports http(s), file://, and OCI artifact URLs. --- crates/orchestrator/src/config.rs | 96 ++++++------------------------- 1 file changed, 16 insertions(+), 80 deletions(-) diff --git a/crates/orchestrator/src/config.rs b/crates/orchestrator/src/config.rs index a2f1d1f..65f1c04 100644 --- a/crates/orchestrator/src/config.rs +++ b/crates/orchestrator/src/config.rs @@ -103,9 +103,17 @@ fn default_example_path() -> PathBuf { } /// Ensure images referenced in config exist at local_path. If missing, fetch -/// from `source` (supports http(s):// and file://) and optionally decompress -/// according to `decompress`. +/// from `source` using vm-manager's ImageManager (supports http(s), file://, +/// oci://, streaming downloads, and zstd decompression). pub async fn ensure_images(cfg: &OrchestratorConfig) -> Result<()> { + let cache_dir = cfg + .images + .values() + .next() + .and_then(|img| img.local_path.parent()) + .unwrap_or(Path::new("/var/lib/solstice/images")); + let img_mgr = vm_manager::image::ImageManager::with_cache_dir(cache_dir.to_path_buf()); + for (label, image) in cfg.images.iter() { if image.local_path.exists() { continue; @@ -115,85 +123,13 @@ pub async fn ensure_images(cfg: &OrchestratorConfig) -> Result<()> { tokio::fs::create_dir_all(parent).await.into_diagnostic()?; } - let source = image.source.as_str(); - let is_file = source.starts_with("file://"); - let tmp_path = image.local_path.with_extension("part"); + tracing::info!(label = %label, source = %image.source, local = ?image.local_path, "downloading base image via vm-manager"); - if is_file { - // Local file source: copy or decompress from local path - let src_path = PathBuf::from(&source[7..]); // naive parse; paths should be absolute - tracing::info!(label = %label, src = ?src_path, local = ?image.local_path, "preparing base image from file:// source"); - - match image.decompress.unwrap_or(Decompress::None) { - Decompress::None => { - // Copy to temporary then atomically move into place - tokio::fs::copy(&src_path, &tmp_path) - .await - .into_diagnostic()?; - tokio::fs::rename(&tmp_path, &image.local_path) - .await - .into_diagnostic()?; - } - Decompress::Zstd => { - let src = src_path.clone(); - let tmp_out = tmp_path.clone(); - task::spawn_blocking(move || -> miette::Result<()> { - let infile = fs::File::open(&src).into_diagnostic()?; - let mut decoder = - zstd::stream::read::Decoder::new(infile).into_diagnostic()?; - let mut outfile = fs::File::create(&tmp_out).into_diagnostic()?; - std::io::copy(&mut decoder, &mut outfile).into_diagnostic()?; - Ok(()) - }) - .await - .into_diagnostic()??; - tokio::fs::rename(&tmp_path, &image.local_path) - .await - .into_diagnostic()?; - } - } - } else { - // Remote URL (HTTP/HTTPS): download to temporary file first - tracing::info!(label = %label, url = %image.source, local = ?image.local_path, "downloading base image"); - let resp = reqwest::get(&image.source).await.into_diagnostic()?; - let status = resp.status(); - if !status.is_success() { - miette::bail!( - "failed to download {url}: {status}", - url = image.source, - status = status - ); - } - let bytes = resp.bytes().await.into_diagnostic()?; - tokio::fs::write(&tmp_path, &bytes) - .await - .into_diagnostic()?; - - // Decompress or move into place - match image.decompress.unwrap_or(Decompress::None) { - Decompress::None => { - tokio::fs::rename(&tmp_path, &image.local_path) - .await - .into_diagnostic()?; - } - Decompress::Zstd => { - let src = tmp_path.clone(); - let dst = image.local_path.clone(); - task::spawn_blocking(move || -> miette::Result<()> { - let infile = fs::File::open(&src).into_diagnostic()?; - let mut decoder = - zstd::stream::read::Decoder::new(infile).into_diagnostic()?; - let mut outfile = fs::File::create(&dst).into_diagnostic()?; - std::io::copy(&mut decoder, &mut outfile).into_diagnostic()?; - // remove compressed temp - std::fs::remove_file(&src).ok(); - Ok(()) - }) - .await - .into_diagnostic()??; - } - } - } + // Use vm-manager's ImageManager for streaming download + decompression + img_mgr + .download(&image.source, &image.local_path) + .await + .into_diagnostic()?; tracing::info!(label = %label, local = ?image.local_path, "image ready"); }