refactor(verbose): replace the global flag and veprintln! with log
Diagnostics now go through the log facade: the library no longer decides where output goes, and consumers plug in their own sink (the CLI installs a stderr logger printing warnings always and ecr debug diagnostics under -v; dependency records stay filtered out). The kernel 'quiet' flag moves from the global verbosity flag to an explicit QemuConfig field.
This commit is contained in:
Generated
+2
@@ -307,6 +307,7 @@ dependencies = [
|
|||||||
"flate2",
|
"flate2",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"libc",
|
"libc",
|
||||||
|
"log",
|
||||||
"nix",
|
"nix",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -329,6 +330,7 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"ecr",
|
"ecr",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -22,5 +22,8 @@ clap = { version = "4", features = ["derive", "env"] }
|
|||||||
# Error handling
|
# Error handling
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
|
|
||||||
|
# Logging facade; the stderr sink lives in src/logger.rs
|
||||||
|
log = { version = "0.4", features = ["std"] }
|
||||||
|
|
||||||
# Progress bar rendering of library progress events
|
# Progress bar rendering of library progress events
|
||||||
indicatif = "0.18"
|
indicatif = "0.18"
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
//! stderr logging sink: `Warn`/`Error` records always, `Debug`
|
||||||
|
//! diagnostics only with -v.
|
||||||
|
|
||||||
|
use log::{Level, LevelFilter, Log, Metadata, Record};
|
||||||
|
|
||||||
|
struct CliLogger {
|
||||||
|
max: LevelFilter,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Log for CliLogger {
|
||||||
|
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||||
|
if metadata.level() > self.max {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
match metadata.level() {
|
||||||
|
Level::Warn | Level::Error => true,
|
||||||
|
// Dependencies (hyper, rustls, …) share the log facade and are
|
||||||
|
// far noisier than we are; -v follows only our own diagnostics.
|
||||||
|
_ => metadata.target().starts_with("ecr"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&self, record: &Record) {
|
||||||
|
if !self.enabled(record.metadata()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
match record.level() {
|
||||||
|
Level::Error => eprintln!("Error: {}", record.args()),
|
||||||
|
Level::Warn => eprintln!("Warning: {}", record.args()),
|
||||||
|
_ => eprintln!("{}", record.args()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Install the CLI logger: warnings and errors always, `ecr` debug
|
||||||
|
/// diagnostics only when `verbose` (-v).
|
||||||
|
pub fn init(verbose: bool) {
|
||||||
|
let max = if verbose {
|
||||||
|
LevelFilter::Debug
|
||||||
|
} else {
|
||||||
|
LevelFilter::Warn
|
||||||
|
};
|
||||||
|
log::set_boxed_logger(Box::new(CliLogger { max })).ok();
|
||||||
|
log::set_max_level(max);
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
mod cli;
|
mod cli;
|
||||||
|
mod logger;
|
||||||
mod progress;
|
mod progress;
|
||||||
|
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@@ -11,14 +12,14 @@ use ecr::chroot;
|
|||||||
use ecr::config::Config;
|
use ecr::config::Config;
|
||||||
use ecr::exec::ExecOptions;
|
use ecr::exec::ExecOptions;
|
||||||
use ecr::mount::BindTarget;
|
use ecr::mount::BindTarget;
|
||||||
use ecr::{kernel, qemu, qemu_vm, utils, veprintln, verbose, PrepareRequest, RootfsCache};
|
use ecr::{kernel, qemu, qemu_vm, utils, PrepareRequest, RootfsCache};
|
||||||
use progress::TermProgress;
|
use progress::TermProgress;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let args = Args::parse();
|
let args = Args::parse();
|
||||||
|
|
||||||
// Initialise verbosity before anything else so all downstream code can use veprintln!.
|
// Install the logger before anything else so all downstream code can log.
|
||||||
verbose::set(args.verbose);
|
logger::init(args.verbose);
|
||||||
|
|
||||||
// Render library progress (bars/spinners) on the terminal
|
// Render library progress (bars/spinners) on the terminal
|
||||||
let progress = TermProgress::new();
|
let progress = TermProgress::new();
|
||||||
@@ -63,11 +64,11 @@ fn main() -> Result<()> {
|
|||||||
// QEMU system mode
|
// QEMU system mode
|
||||||
let kernel_path = match kernel_opt {
|
let kernel_path = match kernel_opt {
|
||||||
Some(path) => {
|
Some(path) => {
|
||||||
veprintln!("QEMU mode: using provided kernel {}", path.display());
|
log::debug!("QEMU mode: using provided kernel {}", path.display());
|
||||||
path.clone()
|
path.clone()
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
veprintln!("QEMU mode: downloading default kernel...");
|
log::debug!("QEMU mode: downloading default kernel...");
|
||||||
kernel::get_default_kernel(cache.dir(), &arch, &progress)?
|
kernel::get_default_kernel(cache.dir(), &arch, &progress)?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -83,6 +84,7 @@ fn main() -> Result<()> {
|
|||||||
kernel_path,
|
kernel_path,
|
||||||
rootfs_path: rootfs.dir().to_path_buf(),
|
rootfs_path: rootfs.dir().to_path_buf(),
|
||||||
memory: args.memory.clone(),
|
memory: args.memory.clone(),
|
||||||
|
quiet: !args.verbose,
|
||||||
arch: arch.clone(),
|
arch: arch.clone(),
|
||||||
command,
|
command,
|
||||||
},
|
},
|
||||||
@@ -91,7 +93,7 @@ fn main() -> Result<()> {
|
|||||||
|
|
||||||
// Cleanup happens automatically via tempfile
|
// Cleanup happens automatically via tempfile
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
veprintln!("Cleanup complete.");
|
log::debug!("Cleanup complete.");
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
result
|
||||||
@@ -123,7 +125,7 @@ fn namespace_mode(args: &Args, rootfs: &Path, config: &Config, arch: &str) -> Re
|
|||||||
|
|
||||||
// Cleanup happens automatically via tempfile
|
// Cleanup happens automatically via tempfile
|
||||||
if exit_code.is_ok() {
|
if exit_code.is_ok() {
|
||||||
veprintln!("Cleanup complete.");
|
log::debug!("Cleanup complete.");
|
||||||
}
|
}
|
||||||
|
|
||||||
exit_code
|
exit_code
|
||||||
|
|||||||
@@ -21,6 +21,9 @@ flate2 = "1"
|
|||||||
xz2 = "0.1"
|
xz2 = "0.1"
|
||||||
zstd = "0.13"
|
zstd = "0.13"
|
||||||
|
|
||||||
|
# Logging facade (the CLI installs the sink)
|
||||||
|
log = "0.4"
|
||||||
|
|
||||||
# Unix syscall bindings
|
# Unix syscall bindings
|
||||||
nix = { version = "0.31", features = ["fs", "mount", "sched", "signal", "user", "process", "hostname"] }
|
nix = { version = "0.31", features = ["fs", "mount", "sched", "signal", "user", "process", "hostname"] }
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::veprintln;
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use nix::unistd::{chroot, execve};
|
use nix::unistd::{chroot, execve};
|
||||||
use std::os::unix::ffi::OsStrExt;
|
use std::os::unix::ffi::OsStrExt;
|
||||||
@@ -39,7 +38,7 @@ pub fn run_chroot(
|
|||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Set hostname in UTS namespace
|
// Set hostname in UTS namespace
|
||||||
if let Err(e) = crate::namespace::set_hostname("chroot") {
|
if let Err(e) = crate::namespace::set_hostname("chroot") {
|
||||||
eprintln!("Warning: Failed to set hostname: {}", e);
|
log::warn!("Failed to set hostname: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect shell before chroot (we're still outside)
|
// Detect shell before chroot (we're still outside)
|
||||||
@@ -90,8 +89,8 @@ pub fn run_chroot(
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Print welcome message
|
// Print welcome message
|
||||||
veprintln!("Entering chroot at {}", rootfs.display());
|
log::debug!("Entering chroot at {}", rootfs.display());
|
||||||
veprintln!("Working directory: {}", working_dir.display());
|
log::debug!("Working directory: {}", working_dir.display());
|
||||||
|
|
||||||
// execve(2) does not search PATH: resolve the program through the
|
// execve(2) does not search PATH: resolve the program through the
|
||||||
// caller-composed environment like execvp(3) would.
|
// caller-composed environment like execvp(3) would.
|
||||||
|
|||||||
+13
-14
@@ -1,5 +1,4 @@
|
|||||||
use crate::progress::{Progress, Unit};
|
use crate::progress::{Progress, Unit};
|
||||||
use crate::veprintln;
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use futures_util::StreamExt;
|
use futures_util::StreamExt;
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
@@ -88,7 +87,7 @@ pub fn download_image(
|
|||||||
match source {
|
match source {
|
||||||
ImageSource::DirectTarball { distro, version } => {
|
ImageSource::DirectTarball { distro, version } => {
|
||||||
let url = crate::distro::resolve_distro_url(distro, version.as_deref(), arch)?;
|
let url = crate::distro::resolve_distro_url(distro, version.as_deref(), arch)?;
|
||||||
veprintln!("Resolved URL: {}", url);
|
log::debug!("Resolved URL: {}", url);
|
||||||
if !url.starts_with("http://") && !url.starts_with("https://") {
|
if !url.starts_with("http://") && !url.starts_with("https://") {
|
||||||
return Err(anyhow!("Unsupported URL scheme: {}", url));
|
return Err(anyhow!("Unsupported URL scheme: {}", url));
|
||||||
}
|
}
|
||||||
@@ -100,7 +99,7 @@ pub fn download_image(
|
|||||||
tag,
|
tag,
|
||||||
architecture,
|
architecture,
|
||||||
} => {
|
} => {
|
||||||
veprintln!("Pulling OCI image: {}/{}:{}", registry, repository, tag);
|
log::debug!("Pulling OCI image: {}/{}:{}", registry, repository, tag);
|
||||||
rt.block_on(download_oci_image_async(
|
rt.block_on(download_oci_image_async(
|
||||||
registry,
|
registry,
|
||||||
repository,
|
repository,
|
||||||
@@ -114,7 +113,7 @@ pub fn download_image(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn download_file_async(url: &str, dest: &Path, progress: &dyn Progress) -> Result<()> {
|
async fn download_file_async(url: &str, dest: &Path, progress: &dyn Progress) -> Result<()> {
|
||||||
veprintln!("Downloading: {}", url);
|
log::debug!("Downloading: {}", url);
|
||||||
|
|
||||||
let client = Client::builder()
|
let client = Client::builder()
|
||||||
.timeout(Duration::from_secs(300))
|
.timeout(Duration::from_secs(300))
|
||||||
@@ -168,7 +167,7 @@ async fn download_file_async(url: &str, dest: &Path, progress: &dyn Progress) ->
|
|||||||
tokio::fs::rename(&temp_dest, dest)
|
tokio::fs::rename(&temp_dest, dest)
|
||||||
.await
|
.await
|
||||||
.context("Failed to move partial download to final destination")?;
|
.context("Failed to move partial download to final destination")?;
|
||||||
veprintln!("Download complete: {}", dest.display());
|
log::debug!("Download complete: {}", dest.display());
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -230,7 +229,7 @@ async fn try_download_oci_image(
|
|||||||
format!("https://{}/v2/{}/manifests/{}", registry, repository, tag)
|
format!("https://{}/v2/{}/manifests/{}", registry, repository, tag)
|
||||||
};
|
};
|
||||||
|
|
||||||
veprintln!("Fetching manifest from: {}", manifest_url);
|
log::debug!("Fetching manifest from: {}", manifest_url);
|
||||||
|
|
||||||
// Request both manifest list and single manifest types (including OCI formats)
|
// Request both manifest list and single manifest types (including OCI formats)
|
||||||
let response = client
|
let response = client
|
||||||
@@ -265,7 +264,7 @@ async fn try_download_oci_image(
|
|||||||
// Check if this is a manifest list (multi-arch)
|
// Check if this is a manifest list (multi-arch)
|
||||||
let layers = if let Some(manifests) = manifest["manifests"].as_array() {
|
let layers = if let Some(manifests) = manifest["manifests"].as_array() {
|
||||||
// This is a manifest list - find the right architecture
|
// This is a manifest list - find the right architecture
|
||||||
veprintln!("Got manifest list with {} manifests", manifests.len());
|
log::debug!("Got manifest list with {} manifests", manifests.len());
|
||||||
|
|
||||||
let manifest_entry = manifests
|
let manifest_entry = manifests
|
||||||
.iter()
|
.iter()
|
||||||
@@ -291,7 +290,7 @@ async fn try_download_oci_image(
|
|||||||
.as_str()
|
.as_str()
|
||||||
.ok_or_else(|| anyhow!("No digest in manifest entry"))?;
|
.ok_or_else(|| anyhow!("No digest in manifest entry"))?;
|
||||||
|
|
||||||
veprintln!("Found manifest digest: {}", manifest_digest);
|
log::debug!("Found manifest digest: {}", manifest_digest);
|
||||||
|
|
||||||
// Now get the actual manifest for this architecture
|
// Now get the actual manifest for this architecture
|
||||||
let arch_manifest_url = if registry == "docker.io" {
|
let arch_manifest_url = if registry == "docker.io" {
|
||||||
@@ -338,7 +337,7 @@ async fn try_download_oci_image(
|
|||||||
.clone()
|
.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
veprintln!("Found {} layers to download", layers.len());
|
log::debug!("Found {} layers to download", layers.len());
|
||||||
|
|
||||||
// Download all layer blobs into a temp directory
|
// Download all layer blobs into a temp directory
|
||||||
let temp_dir = tempfile::tempdir()?;
|
let temp_dir = tempfile::tempdir()?;
|
||||||
@@ -358,7 +357,7 @@ async fn try_download_oci_image(
|
|||||||
let ext = media_type_to_extension(media_type);
|
let ext = media_type_to_extension(media_type);
|
||||||
let layer_name = format!("layer_{}.{}", i, ext);
|
let layer_name = format!("layer_{}.{}", i, ext);
|
||||||
|
|
||||||
veprintln!(
|
log::debug!(
|
||||||
"Fetching layer {}/{}: {}",
|
"Fetching layer {}/{}: {}",
|
||||||
i + 1,
|
i + 1,
|
||||||
layers.len(),
|
layers.len(),
|
||||||
@@ -440,7 +439,7 @@ async fn get_auth_token(client: &Client, registry: &str, repository: &str) -> Re
|
|||||||
"https://auth.docker.io/token?service=registry.docker.io&scope=repository:{}:pull",
|
"https://auth.docker.io/token?service=registry.docker.io&scope=repository:{}:pull",
|
||||||
repository
|
repository
|
||||||
);
|
);
|
||||||
veprintln!("Getting auth token from: {}", url);
|
log::debug!("Getting auth token from: {}", url);
|
||||||
return fetch_bearer_token(client, &url).await;
|
return fetch_bearer_token(client, &url).await;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -464,8 +463,8 @@ async fn get_auth_token(client: &Client, registry: &str, repository: &str) -> Re
|
|||||||
other => {
|
other => {
|
||||||
// Unexpected status; proceed without a token and let the manifest
|
// Unexpected status; proceed without a token and let the manifest
|
||||||
// request fail with a more descriptive error.
|
// request fail with a more descriptive error.
|
||||||
eprintln!(
|
log::warn!(
|
||||||
"Warning: registry probe returned HTTP {}; trying without auth",
|
"registry probe returned HTTP {}; trying without auth",
|
||||||
other
|
other
|
||||||
);
|
);
|
||||||
return Ok(String::new());
|
return Ok(String::new());
|
||||||
@@ -492,7 +491,7 @@ async fn get_auth_token(client: &Client, registry: &str, repository: &str) -> Re
|
|||||||
"{}?service={}&scope=repository:{}:pull",
|
"{}?service={}&scope=repository:{}:pull",
|
||||||
realm, service, repository
|
realm, service, repository
|
||||||
);
|
);
|
||||||
veprintln!("Getting auth token from: {}", token_url);
|
log::debug!("Getting auth token from: {}", token_url);
|
||||||
fetch_bearer_token(client, &token_url).await
|
fetch_bearer_token(client, &token_url).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ use crate::mount::{self, BindTarget};
|
|||||||
use crate::namespace;
|
use crate::namespace;
|
||||||
use crate::qemu;
|
use crate::qemu;
|
||||||
use crate::utils::{self, Arch};
|
use crate::utils::{self, Arch};
|
||||||
use crate::veprintln;
|
|
||||||
|
|
||||||
/// Options for [`exec`].
|
/// Options for [`exec`].
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -99,7 +98,7 @@ pub fn exec(rootfs: &Path, opts: &ExecOptions) -> Result<i32> {
|
|||||||
|
|
||||||
for bind in &opts.binds {
|
for bind in &opts.binds {
|
||||||
if !bind.read_only {
|
if !bind.read_only {
|
||||||
veprintln!("Read-write mount: {}", bind.target.display());
|
log::debug!("Read-write mount: {}", bind.target.display());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use crate::progress::{Progress, Unit};
|
use crate::progress::{Progress, Unit};
|
||||||
use crate::veprintln;
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::{BufReader, Read};
|
use std::io::{BufReader, Read};
|
||||||
@@ -15,7 +14,7 @@ pub fn extract_tarball(tarball: &Path, dest: &Path, progress: &dyn Progress) ->
|
|||||||
// scanning the archive for a layers.manifest entry (which required
|
// scanning the archive for a layers.manifest entry (which required
|
||||||
// reading the entire compressed archive twice for large OCI images).
|
// reading the entire compressed archive twice for large OCI images).
|
||||||
if filename.starts_with("oci-") {
|
if filename.starts_with("oci-") {
|
||||||
veprintln!("Detected multi-layer OCI image, extracting layers...");
|
log::debug!("Detected multi-layer OCI image, extracting layers...");
|
||||||
return extract_multi_layer_oci(tarball, dest, progress);
|
return extract_multi_layer_oci(tarball, dest, progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +114,7 @@ fn extract_multi_layer_oci(tarball: &Path, dest: &Path, progress: &dyn Progress)
|
|||||||
// Not our layer-bundle layout: a provisioned rootfs persisted over
|
// Not our layer-bundle layout: a provisioned rootfs persisted over
|
||||||
// an OCI cache entry (the dispatch above keys on the "oci-" filename
|
// an OCI cache entry (the dispatch above keys on the "oci-" filename
|
||||||
// prefix). The outer archive is a plain rootfs tarball.
|
// prefix). The outer archive is a plain rootfs tarball.
|
||||||
veprintln!("No layers.manifest in bundle; extracting as plain rootfs tarball");
|
log::debug!("No layers.manifest in bundle; extracting as plain rootfs tarball");
|
||||||
return extract_single_archive(tarball, dest, progress);
|
return extract_single_archive(tarball, dest, progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +133,7 @@ fn extract_multi_layer_oci(tarball: &Path, dest: &Path, progress: &dyn Progress)
|
|||||||
if !layer_path.exists() {
|
if !layer_path.exists() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
veprintln!("Extracting layer: {}", layer_name);
|
log::debug!("Extracting layer: {}", layer_name);
|
||||||
extract_oci_layer(&layer_path, dest, &mut fixups, progress)?;
|
extract_oci_layer(&layer_path, dest, &mut fixups, progress)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,7 +323,7 @@ fn apply_hard_link(dest: &Path, dst: &Path, target: &Path) -> Result<()> {
|
|||||||
}
|
}
|
||||||
std::os::unix::fs::symlink(target, dst)
|
std::os::unix::fs::symlink(target, dst)
|
||||||
.with_context(|| format!("Failed to link {} -> {}", dst.display(), target.display()))?;
|
.with_context(|| format!("Failed to link {} -> {}", dst.display(), target.display()))?;
|
||||||
veprintln!(
|
log::debug!(
|
||||||
"Warning: hard link target {} absent; extracted {} as a symlink",
|
"Warning: hard link target {} absent; extracted {} as a symlink",
|
||||||
target.display(),
|
target.display(),
|
||||||
dst.display()
|
dst.display()
|
||||||
@@ -421,7 +420,7 @@ fn extract_archive<R: std::io::Read>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
progress.finish();
|
progress.finish();
|
||||||
veprintln!("Extracted {} files", count);
|
log::debug!("Extracted {} files", count);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-16
@@ -18,7 +18,6 @@
|
|||||||
//! only ELF, uImage and raw images.
|
//! only ELF, uImage and raw images.
|
||||||
|
|
||||||
use crate::progress::{Progress, Unit};
|
use crate::progress::{Progress, Unit};
|
||||||
use crate::veprintln;
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use std::io::{Read, Seek};
|
use std::io::{Read, Seek};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@@ -112,7 +111,7 @@ fn find_kernel_package(branch: &str, arch: &str) -> Result<(&'static str, String
|
|||||||
branch, arch
|
branch, arch
|
||||||
);
|
);
|
||||||
|
|
||||||
veprintln!("Fetching package index: {}", url);
|
log::debug!("Fetching package index: {}", url);
|
||||||
|
|
||||||
let client = reqwest::blocking::Client::builder()
|
let client = reqwest::blocking::Client::builder()
|
||||||
.timeout(Duration::from_secs(30))
|
.timeout(Duration::from_secs(30))
|
||||||
@@ -136,7 +135,7 @@ fn find_kernel_package(branch: &str, arch: &str) -> Result<(&'static str, String
|
|||||||
// Extract APKINDEX from the tar.gz
|
// Extract APKINDEX from the tar.gz
|
||||||
// We need to own the bytes to avoid lifetime issues
|
// We need to own the bytes to avoid lifetime issues
|
||||||
let bytes_owned = bytes.to_vec();
|
let bytes_owned = bytes.to_vec();
|
||||||
veprintln!(" Downloaded {} bytes", bytes_owned.len());
|
log::debug!(" Downloaded {} bytes", bytes_owned.len());
|
||||||
|
|
||||||
// First decompress gzip to memory, then parse tar
|
// First decompress gzip to memory, then parse tar
|
||||||
// Note: Alpine's APKINDEX.tar.gz uses concatenated gzip members (multi-member gzip)
|
// Note: Alpine's APKINDEX.tar.gz uses concatenated gzip members (multi-member gzip)
|
||||||
@@ -148,7 +147,7 @@ fn find_kernel_package(branch: &str, arch: &str) -> Result<(&'static str, String
|
|||||||
.read_to_end(&mut decompressed)
|
.read_to_end(&mut decompressed)
|
||||||
.context("Failed to decompress gzip")?;
|
.context("Failed to decompress gzip")?;
|
||||||
|
|
||||||
veprintln!(" Decompressed {} bytes", decompressed.len());
|
log::debug!(" Decompressed {} bytes", decompressed.len());
|
||||||
|
|
||||||
let tar_cursor = std::io::Cursor::new(decompressed);
|
let tar_cursor = std::io::Cursor::new(decompressed);
|
||||||
let mut archive = tar::Archive::new(tar_cursor);
|
let mut archive = tar::Archive::new(tar_cursor);
|
||||||
@@ -164,7 +163,7 @@ fn find_kernel_package(branch: &str, arch: &str) -> Result<(&'static str, String
|
|||||||
let mut entry = match entry_result {
|
let mut entry = match entry_result {
|
||||||
Ok(e) => e,
|
Ok(e) => e,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
veprintln!(
|
log::debug!(
|
||||||
" Warning: failed to read tar entry #{}: {}",
|
" Warning: failed to read tar entry #{}: {}",
|
||||||
entry_count,
|
entry_count,
|
||||||
e
|
e
|
||||||
@@ -176,7 +175,7 @@ fn find_kernel_package(branch: &str, arch: &str) -> Result<(&'static str, String
|
|||||||
let path = entry.path().context("Failed to get entry path")?;
|
let path = entry.path().context("Failed to get entry path")?;
|
||||||
let path_str = path.to_string_lossy();
|
let path_str = path.to_string_lossy();
|
||||||
|
|
||||||
veprintln!(" Entry #{}: {}", entry_count, path_str);
|
log::debug!(" Entry #{}: {}", entry_count, path_str);
|
||||||
|
|
||||||
if path_str == "APKINDEX" {
|
if path_str == "APKINDEX" {
|
||||||
let mut contents = String::new();
|
let mut contents = String::new();
|
||||||
@@ -184,7 +183,7 @@ fn find_kernel_package(branch: &str, arch: &str) -> Result<(&'static str, String
|
|||||||
.read_to_string(&mut contents)
|
.read_to_string(&mut contents)
|
||||||
.context("Failed to read APKINDEX contents")?;
|
.context("Failed to read APKINDEX contents")?;
|
||||||
|
|
||||||
veprintln!(" APKINDEX size: {} bytes", contents.len());
|
log::debug!(" APKINDEX size: {} bytes", contents.len());
|
||||||
|
|
||||||
// Parse the APKINDEX to find the best kernel package
|
// Parse the APKINDEX to find the best kernel package
|
||||||
// Format:
|
// Format:
|
||||||
@@ -228,7 +227,7 @@ fn find_kernel_package(branch: &str, arch: &str) -> Result<(&'static str, String
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
veprintln!(" Total entries processed: {}", entry_count);
|
log::debug!(" Total entries processed: {}", entry_count);
|
||||||
|
|
||||||
Err(anyhow!(
|
Err(anyhow!(
|
||||||
"APKINDEX file not found in tar.gz archive (processed {} entries)",
|
"APKINDEX file not found in tar.gz archive (processed {} entries)",
|
||||||
@@ -244,7 +243,7 @@ fn download_alpine_kernel(
|
|||||||
progress: &dyn Progress,
|
progress: &dyn Progress,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let (package, version) = find_kernel_package(branch, arch)?;
|
let (package, version) = find_kernel_package(branch, arch)?;
|
||||||
veprintln!("Found {} version: {}", package, version);
|
log::debug!("Found {} version: {}", package, version);
|
||||||
|
|
||||||
// Construct the download URL for the kernel .apk
|
// Construct the download URL for the kernel .apk
|
||||||
// Format: https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/linux-virt-6.12.8-r0.apk
|
// Format: https://dl-cdn.alpinelinux.org/alpine/v3.23/main/x86_64/linux-virt-6.12.8-r0.apk
|
||||||
@@ -253,7 +252,7 @@ fn download_alpine_kernel(
|
|||||||
branch, arch, package, version
|
branch, arch, package, version
|
||||||
);
|
);
|
||||||
|
|
||||||
veprintln!("Downloading kernel: {}", url);
|
log::debug!("Downloading kernel: {}", url);
|
||||||
|
|
||||||
// Use async download via the existing download module pattern
|
// Use async download via the existing download module pattern
|
||||||
let rt = tokio::runtime::Runtime::new().context("Failed to create Tokio runtime")?;
|
let rt = tokio::runtime::Runtime::new().context("Failed to create Tokio runtime")?;
|
||||||
@@ -336,7 +335,7 @@ fn extract_kernel_from_apk(apk_path: &Path, dest: &Path, package: &str) -> Resul
|
|||||||
.ok_or_else(|| anyhow!("unexpected kernel package name: {}", package))?;
|
.ok_or_else(|| anyhow!("unexpected kernel package name: {}", package))?;
|
||||||
let kernel_name = format!("boot/vmlinuz-{}", flavor);
|
let kernel_name = format!("boot/vmlinuz-{}", flavor);
|
||||||
|
|
||||||
veprintln!("Extracting kernel from APK...");
|
log::debug!("Extracting kernel from APK...");
|
||||||
|
|
||||||
let file = std::fs::File::open(apk_path).context("Failed to open APK file")?;
|
let file = std::fs::File::open(apk_path).context("Failed to open APK file")?;
|
||||||
// Use MultiGzDecoder because Alpine APKs have concatenated gzip members
|
// Use MultiGzDecoder because Alpine APKs have concatenated gzip members
|
||||||
@@ -348,12 +347,12 @@ fn extract_kernel_from_apk(apk_path: &Path, dest: &Path, package: &str) -> Resul
|
|||||||
let path = entry.path().context("Failed to get entry path")?;
|
let path = entry.path().context("Failed to get entry path")?;
|
||||||
let path_str = path.to_string_lossy();
|
let path_str = path.to_string_lossy();
|
||||||
|
|
||||||
veprintln!(" APK entry: {}", path_str);
|
log::debug!(" APK entry: {}", path_str);
|
||||||
|
|
||||||
if path_str == kernel_name || path_str == format!("./{}", kernel_name) {
|
if path_str == kernel_name || path_str == format!("./{}", kernel_name) {
|
||||||
// Extract to destination
|
// Extract to destination
|
||||||
entry.unpack(dest).context("Failed to extract kernel")?;
|
entry.unpack(dest).context("Failed to extract kernel")?;
|
||||||
veprintln!(" Extracted kernel to: {}", dest.display());
|
log::debug!(" Extracted kernel to: {}", dest.display());
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -379,7 +378,7 @@ fn decompress_kernel_if_gzipped(path: &Path) -> Result<()> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
veprintln!("Kernel is gzip-compressed, decompressing...");
|
log::debug!("Kernel is gzip-compressed, decompressing...");
|
||||||
file.rewind().context("Failed to rewind kernel file")?;
|
file.rewind().context("Failed to rewind kernel file")?;
|
||||||
let mut decompressed = Vec::new();
|
let mut decompressed = Vec::new();
|
||||||
flate2::read::MultiGzDecoder::new(file)
|
flate2::read::MultiGzDecoder::new(file)
|
||||||
@@ -407,7 +406,7 @@ pub fn get_default_kernel(
|
|||||||
|
|
||||||
// Check if already cached
|
// Check if already cached
|
||||||
if kernel_path.exists() {
|
if kernel_path.exists() {
|
||||||
veprintln!("Using cached default kernel: {}", kernel_path.display());
|
log::debug!("Using cached default kernel: {}", kernel_path.display());
|
||||||
return Ok(kernel_path);
|
return Ok(kernel_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -416,7 +415,7 @@ pub fn get_default_kernel(
|
|||||||
|
|
||||||
// Determine Alpine branch
|
// Determine Alpine branch
|
||||||
let branch = get_alpine_branch()?;
|
let branch = get_alpine_branch()?;
|
||||||
veprintln!("Using Alpine branch: {}", branch);
|
log::debug!("Using Alpine branch: {}", branch);
|
||||||
|
|
||||||
// Download and extract the kernel
|
// Download and extract the kernel
|
||||||
download_alpine_kernel(&branch, alpine_arch, &kernel_path, progress)?;
|
download_alpine_kernel(&branch, alpine_arch, &kernel_path, progress)?;
|
||||||
|
|||||||
@@ -25,17 +25,6 @@ pub mod qemu;
|
|||||||
pub mod qemu_vm;
|
pub mod qemu_vm;
|
||||||
pub mod rootfs;
|
pub mod rootfs;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub mod verbose;
|
|
||||||
|
|
||||||
/// Print to stderr only when verbose mode is active (see [`verbose::set`]).
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! veprintln {
|
|
||||||
($($arg:tt)*) => {
|
|
||||||
if $crate::verbose::is_verbose() {
|
|
||||||
eprintln!($($arg)*);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use exec::{exec, ExecOptions};
|
pub use exec::{exec, ExecOptions};
|
||||||
pub use mount::BindTarget;
|
pub use mount::BindTarget;
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ pub fn setup_mounts(rootfs: &Path, binds: &[BindTarget]) -> Result<Vec<TempDir>>
|
|||||||
MsFlags::MS_PRIVATE | MsFlags::MS_REC,
|
MsFlags::MS_PRIVATE | MsFlags::MS_REC,
|
||||||
None::<&str>,
|
None::<&str>,
|
||||||
) {
|
) {
|
||||||
eprintln!("Warning: Failed to make mounts private: {}", e);
|
log::warn!("Failed to make mounts private: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mount /proc
|
// Mount /proc
|
||||||
@@ -81,7 +81,7 @@ pub fn setup_mounts(rootfs: &Path, binds: &[BindTarget]) -> Result<Vec<TempDir>>
|
|||||||
|
|
||||||
// Try to mount /sys (may fail in some environments)
|
// Try to mount /sys (may fail in some environments)
|
||||||
if let Err(e) = mount_sys(rootfs) {
|
if let Err(e) = mount_sys(rootfs) {
|
||||||
eprintln!("Warning: Could not mount /sys: {}", e);
|
log::warn!("Could not mount /sys: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
for bind in binds {
|
for bind in binds {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
use crate::veprintln;
|
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
@@ -19,6 +18,6 @@ pub fn check_binfmt(arch: &str) -> Result<()> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
veprintln!("QEMU binfmt_misc registered for {}", arch);
|
log::debug!("QEMU binfmt_misc registered for {}", arch);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-22
@@ -1,5 +1,4 @@
|
|||||||
use crate::progress::{Progress, Unit};
|
use crate::progress::{Progress, Unit};
|
||||||
use crate::veprintln;
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use cpio::{newc, NewcBuilder};
|
use cpio::{newc, NewcBuilder};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
@@ -15,6 +14,9 @@ pub struct QemuConfig {
|
|||||||
pub rootfs_path: PathBuf,
|
pub rootfs_path: PathBuf,
|
||||||
/// Memory size for VM (e.g., "2G", "512M")
|
/// Memory size for VM (e.g., "2G", "512M")
|
||||||
pub memory: String,
|
pub memory: String,
|
||||||
|
/// Append `quiet` to the kernel command line to suppress kernel log
|
||||||
|
/// messages (the CLI leaves this off under -v).
|
||||||
|
pub quiet: bool,
|
||||||
/// Target architecture
|
/// Target architecture
|
||||||
pub arch: String,
|
pub arch: String,
|
||||||
/// Optional command to run instead of default init
|
/// Optional command to run instead of default init
|
||||||
@@ -65,9 +67,9 @@ pub fn launch_qemu(config: QemuConfig, progress: &dyn Progress) -> Result<()> {
|
|||||||
// Check if we can use KVM acceleration
|
// Check if we can use KVM acceleration
|
||||||
let use_kvm = can_use_kvm(&config.arch);
|
let use_kvm = can_use_kvm(&config.arch);
|
||||||
if use_kvm {
|
if use_kvm {
|
||||||
veprintln!(" KVM: enabled (native acceleration)");
|
log::debug!(" KVM: enabled (native acceleration)");
|
||||||
} else {
|
} else {
|
||||||
veprintln!(" KVM: disabled (using software emulation)");
|
log::debug!(" KVM: disabled (using software emulation)");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect the best available shell in the rootfs
|
// Detect the best available shell in the rootfs
|
||||||
@@ -89,13 +91,9 @@ pub fn launch_qemu(config: QemuConfig, progress: &dyn Progress) -> Result<()> {
|
|||||||
// Build kernel command line
|
// Build kernel command line
|
||||||
// For initramfs boot, use rdinit= instead of init=
|
// For initramfs boot, use rdinit= instead of init=
|
||||||
// No root= needed as initramfs becomes the rootfs
|
// No root= needed as initramfs becomes the rootfs
|
||||||
// 'quiet' suppresses kernel log messages for a cleaner console (removed with -v)
|
// 'quiet' suppresses kernel log messages for a cleaner console (see QemuConfig::quiet)
|
||||||
// The init script (added to initramfs) handles hostname, shell, and poweroff
|
// The init script (added to initramfs) handles hostname, shell, and poweroff
|
||||||
let quiet_flag = if crate::verbose::is_verbose() {
|
let quiet_flag = if config.quiet { " quiet" } else { "" };
|
||||||
""
|
|
||||||
} else {
|
|
||||||
" quiet"
|
|
||||||
};
|
|
||||||
|
|
||||||
let kernel_append = if let Some(ref cmd) = config.command {
|
let kernel_append = if let Some(ref cmd) = config.command {
|
||||||
// The argv travels through the kernel cmdline, where quotes and
|
// The argv travels through the kernel cmdline, where quotes and
|
||||||
@@ -119,11 +117,11 @@ pub fn launch_qemu(config: QemuConfig, progress: &dyn Progress) -> Result<()> {
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
veprintln!("Launching QEMU: {}", qemu_bin);
|
log::debug!("Launching QEMU: {}", qemu_bin);
|
||||||
veprintln!(" Kernel: {}", config.kernel_path.display());
|
log::debug!(" Kernel: {}", config.kernel_path.display());
|
||||||
veprintln!(" Initramfs: {}", initramfs.display());
|
log::debug!(" Initramfs: {}", initramfs.display());
|
||||||
veprintln!(" Memory: {}", config.memory);
|
log::debug!(" Memory: {}", config.memory);
|
||||||
veprintln!(" Kernel append: {}", kernel_append);
|
log::debug!(" Kernel append: {}", kernel_append);
|
||||||
|
|
||||||
// Build QEMU arguments
|
// Build QEMU arguments
|
||||||
// -machine virt is selected explicitly on riscv64 (see machine_for_arch)
|
// -machine virt is selected explicitly on riscv64 (see machine_for_arch)
|
||||||
@@ -173,7 +171,7 @@ pub fn launch_qemu(config: QemuConfig, progress: &dyn Progress) -> Result<()> {
|
|||||||
|
|
||||||
// Cleanup initramfs
|
// Cleanup initramfs
|
||||||
if let Err(e) = std::fs::remove_file(&initramfs) {
|
if let Err(e) = std::fs::remove_file(&initramfs) {
|
||||||
veprintln!("Warning: failed to cleanup initramfs: {}", e);
|
log::debug!("Warning: failed to cleanup initramfs: {}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if !status.success() {
|
if !status.success() {
|
||||||
@@ -308,7 +306,7 @@ fn create_initramfs(rootfs: &Path, progress: &dyn Progress) -> Result<PathBuf> {
|
|||||||
let total_bytes = std::fs::metadata(&initramfs_path)
|
let total_bytes = std::fs::metadata(&initramfs_path)
|
||||||
.map(|m| m.len())
|
.map(|m| m.len())
|
||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
veprintln!(
|
log::debug!(
|
||||||
"Initramfs created: {} bytes, {} files",
|
"Initramfs created: {} bytes, {} files",
|
||||||
total_bytes,
|
total_bytes,
|
||||||
file_count
|
file_count
|
||||||
@@ -345,7 +343,7 @@ fn write_cpio_archive<W: Write>(
|
|||||||
&mut total_data,
|
&mut total_data,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
veprintln!(
|
log::debug!(
|
||||||
"Collected {} entries, {} bytes total data",
|
"Collected {} entries, {} bytes total data",
|
||||||
written_names.len(),
|
written_names.len(),
|
||||||
total_data
|
total_data
|
||||||
@@ -508,7 +506,7 @@ fn write_dir_entries(
|
|||||||
let dir_entries: Vec<_> = match std::fs::read_dir(current) {
|
let dir_entries: Vec<_> = match std::fs::read_dir(current) {
|
||||||
Ok(entries) => entries.collect::<std::result::Result<_, _>>()?,
|
Ok(entries) => entries.collect::<std::result::Result<_, _>>()?,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
veprintln!(
|
log::debug!(
|
||||||
"Warning: cannot read directory {}: {}",
|
"Warning: cannot read directory {}: {}",
|
||||||
current.display(),
|
current.display(),
|
||||||
e
|
e
|
||||||
@@ -526,7 +524,7 @@ fn write_dir_entries(
|
|||||||
let metadata = match std::fs::symlink_metadata(&path) {
|
let metadata = match std::fs::symlink_metadata(&path) {
|
||||||
Ok(m) => m,
|
Ok(m) => m,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
veprintln!(
|
log::debug!(
|
||||||
"Warning: skipping {} due to metadata error: {}",
|
"Warning: skipping {} due to metadata error: {}",
|
||||||
path.display(),
|
path.display(),
|
||||||
e
|
e
|
||||||
@@ -577,7 +575,7 @@ fn write_dir_entries(
|
|||||||
let data = match std::fs::read(&path) {
|
let data = match std::fs::read(&path) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
veprintln!("Warning: cannot read file {}: {}", path.display(), e);
|
log::debug!("Warning: cannot read file {}: {}", path.display(), e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -589,7 +587,7 @@ fn write_dir_entries(
|
|||||||
let data = match std::fs::read(&path) {
|
let data = match std::fs::read(&path) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
veprintln!("Warning: cannot read file {}: {}", path.display(), e);
|
log::debug!("Warning: cannot read file {}: {}", path.display(), e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -598,7 +596,7 @@ fn write_dir_entries(
|
|||||||
match std::fs::read_link(&path) {
|
match std::fs::read_link(&path) {
|
||||||
Ok(target) => (target.to_string_lossy().into_owned().into_bytes(), 1, 0),
|
Ok(target) => (target.to_string_lossy().into_owned().into_bytes(), 1, 0),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
veprintln!("Warning: cannot read symlink {}: {}", path.display(), e);
|
log::debug!("Warning: cannot read symlink {}: {}", path.display(), e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ use crate::download::{digest_sidecar, download_image, fetch_oci_digest};
|
|||||||
use crate::exec::ExecOptions;
|
use crate::exec::ExecOptions;
|
||||||
use crate::extract::extract_tarball;
|
use crate::extract::extract_tarball;
|
||||||
use crate::progress::Progress;
|
use crate::progress::Progress;
|
||||||
use crate::veprintln;
|
|
||||||
|
|
||||||
/// Sidecar suffix marking a cache entry that holds a provisioned rootfs.
|
/// Sidecar suffix marking a cache entry that holds a provisioned rootfs.
|
||||||
const PROVISIONED_SUFFIX: &str = "provisioned";
|
const PROVISIONED_SUFFIX: &str = "provisioned";
|
||||||
@@ -117,10 +116,7 @@ impl RootfsCache {
|
|||||||
stored.as_deref() != Some(current.trim())
|
stored.as_deref() != Some(current.trim())
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!(
|
log::warn!("could not check image freshness ({}); using cache", e);
|
||||||
"Warning: could not check image freshness ({}); using cache",
|
|
||||||
e
|
|
||||||
);
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,7 +138,7 @@ impl RootfsCache {
|
|||||||
})?;
|
})?;
|
||||||
download_image(&source, &cache_path, &arch, progress)?;
|
download_image(&source, &cache_path, &arch, progress)?;
|
||||||
} else {
|
} else {
|
||||||
veprintln!("Using cached tarball: {}", cache_path.display());
|
log::debug!("Using cached tarball: {}", cache_path.display());
|
||||||
}
|
}
|
||||||
|
|
||||||
extract_prepared(cache_path, arch, progress)
|
extract_prepared(cache_path, arch, progress)
|
||||||
@@ -175,7 +171,7 @@ impl RootfsCache {
|
|||||||
let marker = sidecar(&cache_path, PROVISIONED_SUFFIX);
|
let marker = sidecar(&cache_path, PROVISIONED_SUFFIX);
|
||||||
|
|
||||||
if !req.no_cache && cache_path.exists() && marker.exists() {
|
if !req.no_cache && cache_path.exists() && marker.exists() {
|
||||||
veprintln!("Using provisioned cache entry: {}", cache_path.display());
|
log::debug!("Using provisioned cache entry: {}", cache_path.display());
|
||||||
return extract_prepared(cache_path, arch, progress);
|
return extract_prepared(cache_path, arch, progress);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,7 +274,7 @@ impl PreparedRootfs {
|
|||||||
self.cache_path.display()
|
self.cache_path.display()
|
||||||
)
|
)
|
||||||
})?;
|
})?;
|
||||||
veprintln!("Persisted rootfs to cache: {}", self.cache_path.display());
|
log::debug!("Persisted rootfs to cache: {}", self.cache_path.display());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -309,7 +305,7 @@ fn extract_prepared(
|
|||||||
let temp = tempfile::tempdir().context("Failed to create temporary directory")?;
|
let temp = tempfile::tempdir().context("Failed to create temporary directory")?;
|
||||||
let rootfs = temp.path().to_path_buf();
|
let rootfs = temp.path().to_path_buf();
|
||||||
|
|
||||||
veprintln!("Extracting to: {}", rootfs.display());
|
log::debug!("Extracting to: {}", rootfs.display());
|
||||||
extract_tarball(&cache_path, &rootfs, progress)?;
|
extract_tarball(&cache_path, &rootfs, progress)?;
|
||||||
|
|
||||||
Ok(PreparedRootfs {
|
Ok(PreparedRootfs {
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
|
|
||||||
static VERBOSE: AtomicBool = AtomicBool::new(false);
|
|
||||||
|
|
||||||
pub fn set(v: bool) {
|
|
||||||
VERBOSE.store(v, Ordering::Relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_verbose() -> bool {
|
|
||||||
VERBOSE.load(Ordering::Relaxed)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user