Skip to main content

initramfs_builder/image/
rootfs.rs

1use anyhow::Result;
2use std::path::{Path, PathBuf};
3use tempfile::TempDir;
4use tracing::info;
5
6use super::LayerExtractor;
7use crate::registry::{PullOptions, RegistryClient};
8
9pub struct RootfsBuilder {
10    client: RegistryClient,
11    options: PullOptions,
12    exclude_patterns: Vec<String>,
13    temp_dir: Option<TempDir>,
14}
15
16impl RootfsBuilder {
17    pub fn new(client: RegistryClient) -> Self {
18        Self {
19            client,
20            options: PullOptions::default(),
21            exclude_patterns: Vec::new(),
22            temp_dir: None,
23        }
24    }
25
26    pub fn platform(mut self, os: &str, arch: &str) -> Self {
27        self.options.platform_os = os.to_string();
28        self.options.platform_arch = arch.to_string();
29        self
30    }
31
32    pub fn exclude(mut self, patterns: &[&str]) -> Self {
33        self.exclude_patterns
34            .extend(patterns.iter().map(|s| s.to_string()));
35        self
36    }
37
38    pub async fn build(&mut self, image: &str) -> Result<PathBuf> {
39        let reference = RegistryClient::parse_reference(image)?;
40
41        info!("Fetching manifest for {}", image);
42        let manifest = self
43            .client
44            .fetch_manifest(&reference, &self.options)
45            .await?;
46
47        info!(
48            "Image has {} layers, total size: {} bytes",
49            manifest.layers.len(),
50            manifest.total_size
51        );
52
53        info!("Pulling layers...");
54        let layers = self
55            .client
56            .pull_all_layers(&reference, &manifest, None)
57            .await?;
58
59        let temp_dir = TempDir::new()?;
60        let rootfs_path = temp_dir.path().to_path_buf();
61
62        info!("Extracting layers to {:?}", rootfs_path);
63        let exclude_refs: Vec<&str> = self.exclude_patterns.iter().map(|s| s.as_str()).collect();
64        let mut extractor = LayerExtractor::new().with_excludes(&exclude_refs)?;
65        extractor.extract_all_layers(&layers, &rootfs_path)?;
66
67        self.temp_dir = Some(temp_dir);
68
69        Ok(rootfs_path)
70    }
71
72    pub fn rootfs_path(&self) -> Option<&Path> {
73        self.temp_dir.as_ref().map(|t| t.path())
74    }
75}