| 638 | } |
| 639 | |
| 640 | pub async fn download_image(&self, hex_os_image_hash: &str, dst_dir: &Path) -> Result<()> { |
| 641 | let url = self |
| 642 | .download_url |
| 643 | .replace("{OS_IMAGE_HASH}", hex_os_image_hash); |
| 644 | |
| 645 | // Create a temporary directory for extraction within the cache directory |
| 646 | let cache_dir = Path::new(&self.image_cache_dir).join("images").join("tmp"); |
| 647 | fs_err::create_dir_all(&cache_dir).context("Failed to create cache directory")?; |
| 648 | let auto_delete_temp_dir = tempfile::Builder::new() |
| 649 | .prefix("tmp-download-") |
| 650 | .tempdir_in(&cache_dir) |
| 651 | .context("Failed to create temporary directory")?; |
| 652 | let tmp_dir = auto_delete_temp_dir.path(); |
| 653 | |
| 654 | info!("Downloading image from {}", url); |
| 655 | let client = reqwest::Client::new(); |
| 656 | let response = client |
| 657 | .get(&url) |
| 658 | .send() |
| 659 | .await |
| 660 | .context("Failed to download image")?; |
| 661 | |
| 662 | if !response.status().is_success() { |
| 663 | bail!( |
| 664 | "Failed to download image: HTTP status {}, url: {url}", |
| 665 | response.status(), |
| 666 | ); |
| 667 | } |
| 668 | |
| 669 | // Save the tarball to a temporary file using streaming |
| 670 | let tarball_path = tmp_dir.join("image.tar.gz"); |
| 671 | let mut file = tokio::fs::File::create(&tarball_path) |
| 672 | .await |
| 673 | .context("Failed to create tarball file")?; |
| 674 | let mut response = response; |
| 675 | while let Some(chunk) = response.chunk().await? { |
| 676 | file.write_all(&chunk) |
| 677 | .await |
| 678 | .context("Failed to write chunk to file")?; |
| 679 | } |
| 680 | |
| 681 | let extracted_dir = tmp_dir.join("extracted"); |
| 682 | fs_err::create_dir_all(&extracted_dir).context("Failed to create extraction directory")?; |
| 683 | |
| 684 | // Extract the tarball |
| 685 | let output = Command::new("tar") |
| 686 | .arg("xzf") |
| 687 | .arg(&tarball_path) |
| 688 | .current_dir(&extracted_dir) |
| 689 | .output() |
| 690 | .await |
| 691 | .context("Failed to extract tarball")?; |
| 692 | |
| 693 | if !output.status.success() { |
| 694 | bail!( |
| 695 | "Failed to extract tarball: {}", |
| 696 | String::from_utf8_lossy(&output.stderr) |
| 697 | ); |