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:
2026-10-03 00:13:34 +02:00
parent 031c6ba113
commit fe0b99ee6b
16 changed files with 129 additions and 106 deletions
Generated
+2
View File
@@ -307,6 +307,7 @@ dependencies = [
"flate2",
"futures-util",
"libc",
"log",
"nix",
"reqwest",
"serde",
@@ -329,6 +330,7 @@ dependencies = [
"clap",
"ecr",
"indicatif",
"log",
]
[[package]]
+3
View File
@@ -22,5 +22,8 @@ clap = { version = "4", features = ["derive", "env"] }
# Error handling
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
indicatif = "0.18"
+47
View File
@@ -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);
}
+9 -7
View File
@@ -1,4 +1,5 @@
mod cli;
mod logger;
mod progress;
use std::path::{Path, PathBuf};
@@ -11,14 +12,14 @@ use ecr::chroot;
use ecr::config::Config;
use ecr::exec::ExecOptions;
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;
fn main() -> Result<()> {
let args = Args::parse();
// Initialise verbosity before anything else so all downstream code can use veprintln!.
verbose::set(args.verbose);
// Install the logger before anything else so all downstream code can log.
logger::init(args.verbose);
// Render library progress (bars/spinners) on the terminal
let progress = TermProgress::new();
@@ -63,11 +64,11 @@ fn main() -> Result<()> {
// QEMU system mode
let kernel_path = match kernel_opt {
Some(path) => {
veprintln!("QEMU mode: using provided kernel {}", path.display());
log::debug!("QEMU mode: using provided kernel {}", path.display());
path.clone()
}
None => {
veprintln!("QEMU mode: downloading default kernel...");
log::debug!("QEMU mode: downloading default kernel...");
kernel::get_default_kernel(cache.dir(), &arch, &progress)?
}
};
@@ -83,6 +84,7 @@ fn main() -> Result<()> {
kernel_path,
rootfs_path: rootfs.dir().to_path_buf(),
memory: args.memory.clone(),
quiet: !args.verbose,
arch: arch.clone(),
command,
},
@@ -91,7 +93,7 @@ fn main() -> Result<()> {
// Cleanup happens automatically via tempfile
if result.is_ok() {
veprintln!("Cleanup complete.");
log::debug!("Cleanup complete.");
}
result
@@ -123,7 +125,7 @@ fn namespace_mode(args: &Args, rootfs: &Path, config: &Config, arch: &str) -> Re
// Cleanup happens automatically via tempfile
if exit_code.is_ok() {
veprintln!("Cleanup complete.");
log::debug!("Cleanup complete.");
}
exit_code
+3
View File
@@ -21,6 +21,9 @@ flate2 = "1"
xz2 = "0.1"
zstd = "0.13"
# Logging facade (the CLI installs the sink)
log = "0.4"
# Unix syscall bindings
nix = { version = "0.31", features = ["fs", "mount", "sched", "signal", "user", "process", "hostname"] }
+3 -4
View File
@@ -1,4 +1,3 @@
use crate::veprintln;
use anyhow::{anyhow, Context, Result};
use nix::unistd::{chroot, execve};
use std::os::unix::ffi::OsStrExt;
@@ -39,7 +38,7 @@ pub fn run_chroot(
) -> Result<()> {
// Set hostname in UTS namespace
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)
@@ -90,8 +89,8 @@ pub fn run_chroot(
})?;
// Print welcome message
veprintln!("Entering chroot at {}", rootfs.display());
veprintln!("Working directory: {}", working_dir.display());
log::debug!("Entering chroot at {}", rootfs.display());
log::debug!("Working directory: {}", working_dir.display());
// execve(2) does not search PATH: resolve the program through the
// caller-composed environment like execvp(3) would.
+13 -14
View File
@@ -1,5 +1,4 @@
use crate::progress::{Progress, Unit};
use crate::veprintln;
use anyhow::{anyhow, Context, Result};
use futures_util::StreamExt;
use reqwest::Client;
@@ -88,7 +87,7 @@ pub fn download_image(
match source {
ImageSource::DirectTarball { distro, version } => {
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://") {
return Err(anyhow!("Unsupported URL scheme: {}", url));
}
@@ -100,7 +99,7 @@ pub fn download_image(
tag,
architecture,
} => {
veprintln!("Pulling OCI image: {}/{}:{}", registry, repository, tag);
log::debug!("Pulling OCI image: {}/{}:{}", registry, repository, tag);
rt.block_on(download_oci_image_async(
registry,
repository,
@@ -114,7 +113,7 @@ pub fn download_image(
}
async fn download_file_async(url: &str, dest: &Path, progress: &dyn Progress) -> Result<()> {
veprintln!("Downloading: {}", url);
log::debug!("Downloading: {}", url);
let client = Client::builder()
.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)
.await
.context("Failed to move partial download to final destination")?;
veprintln!("Download complete: {}", dest.display());
log::debug!("Download complete: {}", dest.display());
Ok(())
}
@@ -230,7 +229,7 @@ async fn try_download_oci_image(
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)
let response = client
@@ -265,7 +264,7 @@ async fn try_download_oci_image(
// Check if this is a manifest list (multi-arch)
let layers = if let Some(manifests) = manifest["manifests"].as_array() {
// 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
.iter()
@@ -291,7 +290,7 @@ async fn try_download_oci_image(
.as_str()
.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
let arch_manifest_url = if registry == "docker.io" {
@@ -338,7 +337,7 @@ async fn try_download_oci_image(
.clone()
};
veprintln!("Found {} layers to download", layers.len());
log::debug!("Found {} layers to download", layers.len());
// Download all layer blobs into a temp directory
let temp_dir = tempfile::tempdir()?;
@@ -358,7 +357,7 @@ async fn try_download_oci_image(
let ext = media_type_to_extension(media_type);
let layer_name = format!("layer_{}.{}", i, ext);
veprintln!(
log::debug!(
"Fetching layer {}/{}: {}",
i + 1,
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",
repository
);
veprintln!("Getting auth token from: {}", url);
log::debug!("Getting auth token from: {}", url);
return fetch_bearer_token(client, &url).await;
}
@@ -464,8 +463,8 @@ async fn get_auth_token(client: &Client, registry: &str, repository: &str) -> Re
other => {
// Unexpected status; proceed without a token and let the manifest
// request fail with a more descriptive error.
eprintln!(
"Warning: registry probe returned HTTP {}; trying without auth",
log::warn!(
"registry probe returned HTTP {}; trying without auth",
other
);
return Ok(String::new());
@@ -492,7 +491,7 @@ async fn get_auth_token(client: &Client, registry: &str, repository: &str) -> Re
"{}?service={}&scope=repository:{}:pull",
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
}
+1 -2
View File
@@ -17,7 +17,6 @@ use crate::mount::{self, BindTarget};
use crate::namespace;
use crate::qemu;
use crate::utils::{self, Arch};
use crate::veprintln;
/// Options for [`exec`].
#[derive(Debug, Clone)]
@@ -99,7 +98,7 @@ pub fn exec(rootfs: &Path, opts: &ExecOptions) -> Result<i32> {
for bind in &opts.binds {
if !bind.read_only {
veprintln!("Read-write mount: {}", bind.target.display());
log::debug!("Read-write mount: {}", bind.target.display());
}
}
+5 -6
View File
@@ -1,5 +1,4 @@
use crate::progress::{Progress, Unit};
use crate::veprintln;
use anyhow::{Context, Result};
use std::fs::File;
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
// reading the entire compressed archive twice for large OCI images).
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);
}
@@ -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
// an OCI cache entry (the dispatch above keys on the "oci-" filename
// 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);
}
@@ -134,7 +133,7 @@ fn extract_multi_layer_oci(tarball: &Path, dest: &Path, progress: &dyn Progress)
if !layer_path.exists() {
continue;
}
veprintln!("Extracting layer: {}", layer_name);
log::debug!("Extracting layer: {}", layer_name);
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)
.with_context(|| format!("Failed to link {} -> {}", dst.display(), target.display()))?;
veprintln!(
log::debug!(
"Warning: hard link target {} absent; extracted {} as a symlink",
target.display(),
dst.display()
@@ -421,7 +420,7 @@ fn extract_archive<R: std::io::Read>(
}
progress.finish();
veprintln!("Extracted {} files", count);
log::debug!("Extracted {} files", count);
Ok(())
}
+15 -16
View File
@@ -18,7 +18,6 @@
//! only ELF, uImage and raw images.
use crate::progress::{Progress, Unit};
use crate::veprintln;
use anyhow::{anyhow, Context, Result};
use std::io::{Read, Seek};
use std::path::{Path, PathBuf};
@@ -112,7 +111,7 @@ fn find_kernel_package(branch: &str, arch: &str) -> Result<(&'static str, String
branch, arch
);
veprintln!("Fetching package index: {}", url);
log::debug!("Fetching package index: {}", url);
let client = reqwest::blocking::Client::builder()
.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
// We need to own the bytes to avoid lifetime issues
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
// 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)
.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 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 {
Ok(e) => e,
Err(e) => {
veprintln!(
log::debug!(
" Warning: failed to read tar entry #{}: {}",
entry_count,
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_str = path.to_string_lossy();
veprintln!(" Entry #{}: {}", entry_count, path_str);
log::debug!(" Entry #{}: {}", entry_count, path_str);
if path_str == "APKINDEX" {
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)
.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
// 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!(
"APKINDEX file not found in tar.gz archive (processed {} entries)",
@@ -244,7 +243,7 @@ fn download_alpine_kernel(
progress: &dyn Progress,
) -> Result<()> {
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
// 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
);
veprintln!("Downloading kernel: {}", url);
log::debug!("Downloading kernel: {}", url);
// Use async download via the existing download module pattern
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))?;
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")?;
// 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_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) {
// Extract to destination
entry.unpack(dest).context("Failed to extract kernel")?;
veprintln!(" Extracted kernel to: {}", dest.display());
log::debug!(" Extracted kernel to: {}", dest.display());
return Ok(());
}
}
@@ -379,7 +378,7 @@ fn decompress_kernel_if_gzipped(path: &Path) -> Result<()> {
return Ok(());
}
veprintln!("Kernel is gzip-compressed, decompressing...");
log::debug!("Kernel is gzip-compressed, decompressing...");
file.rewind().context("Failed to rewind kernel file")?;
let mut decompressed = Vec::new();
flate2::read::MultiGzDecoder::new(file)
@@ -407,7 +406,7 @@ pub fn get_default_kernel(
// Check if already cached
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);
}
@@ -416,7 +415,7 @@ pub fn get_default_kernel(
// Determine Alpine branch
let branch = get_alpine_branch()?;
veprintln!("Using Alpine branch: {}", branch);
log::debug!("Using Alpine branch: {}", branch);
// Download and extract the kernel
download_alpine_kernel(&branch, alpine_arch, &kernel_path, progress)?;
-11
View File
@@ -25,17 +25,6 @@ pub mod qemu;
pub mod qemu_vm;
pub mod rootfs;
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 mount::BindTarget;
+2 -2
View File
@@ -67,7 +67,7 @@ pub fn setup_mounts(rootfs: &Path, binds: &[BindTarget]) -> Result<Vec<TempDir>>
MsFlags::MS_PRIVATE | MsFlags::MS_REC,
None::<&str>,
) {
eprintln!("Warning: Failed to make mounts private: {}", e);
log::warn!("Failed to make mounts private: {}", e);
}
// 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)
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 {
+1 -2
View File
@@ -1,4 +1,3 @@
use crate::veprintln;
use anyhow::{anyhow, Result};
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(())
}
+20 -22
View File
@@ -1,5 +1,4 @@
use crate::progress::{Progress, Unit};
use crate::veprintln;
use anyhow::{anyhow, Context, Result};
use cpio::{newc, NewcBuilder};
use std::io::Write;
@@ -15,6 +14,9 @@ pub struct QemuConfig {
pub rootfs_path: PathBuf,
/// Memory size for VM (e.g., "2G", "512M")
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
pub arch: String,
/// 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
let use_kvm = can_use_kvm(&config.arch);
if use_kvm {
veprintln!(" KVM: enabled (native acceleration)");
log::debug!(" KVM: enabled (native acceleration)");
} else {
veprintln!(" KVM: disabled (using software emulation)");
log::debug!(" KVM: disabled (using software emulation)");
}
// 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
// For initramfs boot, use rdinit= instead of init=
// 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
let quiet_flag = if crate::verbose::is_verbose() {
""
} else {
" quiet"
};
let quiet_flag = if config.quiet { " quiet" } else { "" };
let kernel_append = if let Some(ref cmd) = config.command {
// 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);
veprintln!(" Kernel: {}", config.kernel_path.display());
veprintln!(" Initramfs: {}", initramfs.display());
veprintln!(" Memory: {}", config.memory);
veprintln!(" Kernel append: {}", kernel_append);
log::debug!("Launching QEMU: {}", qemu_bin);
log::debug!(" Kernel: {}", config.kernel_path.display());
log::debug!(" Initramfs: {}", initramfs.display());
log::debug!(" Memory: {}", config.memory);
log::debug!(" Kernel append: {}", kernel_append);
// Build QEMU arguments
// -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
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() {
@@ -308,7 +306,7 @@ fn create_initramfs(rootfs: &Path, progress: &dyn Progress) -> Result<PathBuf> {
let total_bytes = std::fs::metadata(&initramfs_path)
.map(|m| m.len())
.unwrap_or(0);
veprintln!(
log::debug!(
"Initramfs created: {} bytes, {} files",
total_bytes,
file_count
@@ -345,7 +343,7 @@ fn write_cpio_archive<W: Write>(
&mut total_data,
)?;
veprintln!(
log::debug!(
"Collected {} entries, {} bytes total data",
written_names.len(),
total_data
@@ -508,7 +506,7 @@ fn write_dir_entries(
let dir_entries: Vec<_> = match std::fs::read_dir(current) {
Ok(entries) => entries.collect::<std::result::Result<_, _>>()?,
Err(e) => {
veprintln!(
log::debug!(
"Warning: cannot read directory {}: {}",
current.display(),
e
@@ -526,7 +524,7 @@ fn write_dir_entries(
let metadata = match std::fs::symlink_metadata(&path) {
Ok(m) => m,
Err(e) => {
veprintln!(
log::debug!(
"Warning: skipping {} due to metadata error: {}",
path.display(),
e
@@ -577,7 +575,7 @@ fn write_dir_entries(
let data = match std::fs::read(&path) {
Ok(data) => data,
Err(e) => {
veprintln!("Warning: cannot read file {}: {}", path.display(), e);
log::debug!("Warning: cannot read file {}: {}", path.display(), e);
continue;
}
};
@@ -589,7 +587,7 @@ fn write_dir_entries(
let data = match std::fs::read(&path) {
Ok(data) => data,
Err(e) => {
veprintln!("Warning: cannot read file {}: {}", path.display(), e);
log::debug!("Warning: cannot read file {}: {}", path.display(), e);
continue;
}
};
@@ -598,7 +596,7 @@ fn write_dir_entries(
match std::fs::read_link(&path) {
Ok(target) => (target.to_string_lossy().into_owned().into_bytes(), 1, 0),
Err(e) => {
veprintln!("Warning: cannot read symlink {}: {}", path.display(), e);
log::debug!("Warning: cannot read symlink {}: {}", path.display(), e);
continue;
}
}
+5 -9
View File
@@ -48,7 +48,6 @@ use crate::download::{digest_sidecar, download_image, fetch_oci_digest};
use crate::exec::ExecOptions;
use crate::extract::extract_tarball;
use crate::progress::Progress;
use crate::veprintln;
/// Sidecar suffix marking a cache entry that holds a provisioned rootfs.
const PROVISIONED_SUFFIX: &str = "provisioned";
@@ -117,10 +116,7 @@ impl RootfsCache {
stored.as_deref() != Some(current.trim())
}
Err(e) => {
eprintln!(
"Warning: could not check image freshness ({}); using cache",
e
);
log::warn!("could not check image freshness ({}); using cache", e);
false
}
}
@@ -142,7 +138,7 @@ impl RootfsCache {
})?;
download_image(&source, &cache_path, &arch, progress)?;
} else {
veprintln!("Using cached tarball: {}", cache_path.display());
log::debug!("Using cached tarball: {}", cache_path.display());
}
extract_prepared(cache_path, arch, progress)
@@ -175,7 +171,7 @@ impl RootfsCache {
let marker = sidecar(&cache_path, PROVISIONED_SUFFIX);
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);
}
@@ -278,7 +274,7 @@ impl PreparedRootfs {
self.cache_path.display()
)
})?;
veprintln!("Persisted rootfs to cache: {}", self.cache_path.display());
log::debug!("Persisted rootfs to cache: {}", self.cache_path.display());
Ok(())
}
}
@@ -309,7 +305,7 @@ fn extract_prepared(
let temp = tempfile::tempdir().context("Failed to create temporary directory")?;
let rootfs = temp.path().to_path_buf();
veprintln!("Extracting to: {}", rootfs.display());
log::debug!("Extracting to: {}", rootfs.display());
extract_tarball(&cache_path, &rootfs, progress)?;
Ok(PreparedRootfs {
-11
View File
@@ -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)
}