harmont_cli/commands/cache/
restore.rs1use std::path::Path;
2
3use anyhow::{Context, Result};
4use tracing::{info, warn};
5
6use super::manifest;
7use crate::orchestrator::docker_client::DockerClient;
8
9#[allow(clippy::print_stderr)]
20pub async fn handle_restore(dir: &Path) -> Result<i32> {
21 let docker = DockerClient::connect()?;
22 docker.ping().await?;
23
24 if !dir.exists() {
25 info!("cache dir does not exist, nothing to restore");
26 eprintln!("restored 0/0 images (cache dir missing)");
27 return Ok(0);
28 }
29
30 let mut tars = Vec::new();
31 let mut entries = tokio::fs::read_dir(dir)
32 .await
33 .with_context(|| format!("read cache dir {}", dir.display()))?;
34 while let Some(entry) = entries.next_entry().await? {
35 let name = entry.file_name();
36 let name_str = name.to_string_lossy().to_string();
37 if std::path::Path::new(&name_str)
38 .extension()
39 .is_some_and(|ext| ext.eq_ignore_ascii_case("tar"))
40 {
41 tars.push((name_str, entry.path()));
42 }
43 }
44
45 let total = tars.len();
46 let mut restored = 0u32;
47 let mut skipped = 0u32;
48
49 for (filename, tar_path) in &tars {
50 let Some(tag) = manifest::tag_from_tar_name(filename) else {
51 warn!("skip unrecognized tar: {filename}");
52 continue;
53 };
54
55 if docker.image_exists(&tag).await? {
56 info!("skip (present): {tag}");
57 skipped += 1;
58 continue;
59 }
60
61 info!("restore: {filename} → {tag}");
62 match docker.import_image(tar_path).await {
63 Ok(()) => restored += 1,
64 Err(e) => {
65 warn!("failed to load {filename}: {e}");
66 }
67 }
68 }
69
70 eprintln!("restored {restored}/{total} images ({skipped} already present)");
71 Ok(0)
72}