refactor: split ecr into library and cli crates
The root package becomes a virtual workspace: `crates/ecr` holds the library (package name `ecr`) and `crates/ecr-cli` the command line front-end, which keeps installing the `ecr` binary. No behavior change. The library must not depend on CLI types, so mount::setup_mounts now takes the `no_bind` flag instead of a `&Args`.
This commit is contained in:
Generated
+11
-1
@@ -302,7 +302,6 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"base64",
|
||||
"clap",
|
||||
"cpio",
|
||||
"dirs",
|
||||
"flate2",
|
||||
@@ -323,6 +322,17 @@ dependencies = [
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ecr-cli"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
"dirs",
|
||||
"ecr",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.16.0"
|
||||
|
||||
+5
-41
@@ -1,50 +1,14 @@
|
||||
[package]
|
||||
name = "ecr"
|
||||
[workspace]
|
||||
resolver = "2"
|
||||
members = ["crates/ecr", "crates/ecr-cli"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.77"
|
||||
description = "Enter chroot environments with Linux namespaces"
|
||||
license = "MIT"
|
||||
authors = ["Valentin Haudiquet"]
|
||||
|
||||
[dependencies]
|
||||
# CLI parsing
|
||||
clap = { version = "4", features = ["derive", "env"] }
|
||||
|
||||
# Config parsing
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_yaml = "0.9"
|
||||
|
||||
# HTTP downloads
|
||||
reqwest = { version = "0.13", features = ["blocking", "stream"] }
|
||||
|
||||
# Tarball extraction
|
||||
tar = "0.4"
|
||||
flate2 = "1"
|
||||
xz2 = "0.1"
|
||||
zstd = "0.13"
|
||||
|
||||
# Unix syscall bindings
|
||||
nix = { version = "0.31", features = ["fs", "mount", "sched", "signal", "user", "process", "hostname"] }
|
||||
|
||||
# Temp directories
|
||||
tempfile = "3"
|
||||
|
||||
# Error handling
|
||||
anyhow = "1"
|
||||
|
||||
# Utilities
|
||||
dirs = "6"
|
||||
which = "7"
|
||||
cpio = "0.4"
|
||||
base64 = "0.22"
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "macros", "io-util"] }
|
||||
futures-util = "0.3"
|
||||
indicatif = "0.18"
|
||||
serde_json = "1"
|
||||
libc = "0.2"
|
||||
users = "0.11"
|
||||
|
||||
[profile.release]
|
||||
strip = true
|
||||
opt-level = "z"
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
[package]
|
||||
name = "ecr-cli"
|
||||
description = "Enter chroot environments with Linux namespaces"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
# The installed binary keeps the `ecr` name even though the CLI package is
|
||||
# `ecr-cli`; the library package owns the `ecr` name for consumers.
|
||||
[[bin]]
|
||||
name = "ecr"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
ecr = { path = "../ecr" }
|
||||
|
||||
# CLI parsing
|
||||
clap = { version = "4", features = ["derive", "env"] }
|
||||
|
||||
# Error handling
|
||||
anyhow = "1"
|
||||
|
||||
# Interim: used by the CLI's inline cache/extraction orchestration until it
|
||||
# moves behind the library's rootfs API
|
||||
dirs = "6"
|
||||
tempfile = "3"
|
||||
@@ -1,37 +1,17 @@
|
||||
mod chroot;
|
||||
mod cli;
|
||||
mod config;
|
||||
mod distro;
|
||||
mod download;
|
||||
mod extract;
|
||||
mod kernel;
|
||||
mod mount;
|
||||
mod namespace;
|
||||
mod qemu;
|
||||
mod qemu_vm;
|
||||
mod utils;
|
||||
mod verbose;
|
||||
|
||||
/// Print to stderr only when --verbose / -v is active.
|
||||
#[macro_export]
|
||||
macro_rules! veprintln {
|
||||
($($arg:tt)*) => {
|
||||
if $crate::verbose::is_verbose() {
|
||||
eprintln!($($arg)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Parser;
|
||||
|
||||
use cli::Args;
|
||||
use config::Config;
|
||||
use distro::{
|
||||
use ecr::chroot;
|
||||
use ecr::config::Config;
|
||||
use ecr::distro::{
|
||||
map_arch, parse_image_ref, resolve_distro_url, resolve_distro_version, Distro, ImageSource,
|
||||
};
|
||||
use download::{digest_sidecar, download_image, fetch_oci_digest};
|
||||
use extract::extract_tarball;
|
||||
use ecr::download::{digest_sidecar, download_image, fetch_oci_digest};
|
||||
use ecr::extract::extract_tarball;
|
||||
use ecr::{kernel, mount, namespace, qemu, qemu_vm, utils, veprintln, verbose};
|
||||
|
||||
fn main() -> Result<()> {
|
||||
let args = Args::parse();
|
||||
@@ -227,7 +207,7 @@ fn namespace_mode(args: Args, rootfs: std::path::PathBuf, config: Config) -> Res
|
||||
&rootfs_clone,
|
||||
&bind_paths_clone,
|
||||
&bind_rw_paths_clone,
|
||||
&args_clone,
|
||||
args_clone.no_bind,
|
||||
)?;
|
||||
|
||||
// Write resolv.conf with DNS from config
|
||||
@@ -0,0 +1,43 @@
|
||||
[package]
|
||||
name = "ecr"
|
||||
description = "Ephemeral chroot environments with Linux namespaces"
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
license.workspace = true
|
||||
authors.workspace = true
|
||||
|
||||
[dependencies]
|
||||
# Config parsing
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_yaml = "0.9"
|
||||
|
||||
# HTTP downloads
|
||||
reqwest = { version = "0.13", features = ["blocking", "stream"] }
|
||||
|
||||
# Tarball extraction
|
||||
tar = "0.4"
|
||||
flate2 = "1"
|
||||
xz2 = "0.1"
|
||||
zstd = "0.13"
|
||||
|
||||
# Unix syscall bindings
|
||||
nix = { version = "0.31", features = ["fs", "mount", "sched", "signal", "user", "process", "hostname"] }
|
||||
|
||||
# Temp directories
|
||||
tempfile = "3"
|
||||
|
||||
# Error handling
|
||||
anyhow = "1"
|
||||
|
||||
# Utilities
|
||||
dirs = "6"
|
||||
which = "7"
|
||||
cpio = "0.4"
|
||||
base64 = "0.22"
|
||||
tokio = { version = "1", features = ["rt-multi-thread", "macros", "io-util"] }
|
||||
futures-util = "0.3"
|
||||
indicatif = "0.18"
|
||||
serde_json = "1"
|
||||
libc = "0.2"
|
||||
users = "0.11"
|
||||
@@ -0,0 +1,30 @@
|
||||
//! ecr — ephemeral chroot environments with Linux namespaces.
|
||||
//!
|
||||
//! This crate is the library behind the `ecr` CLI. It resolves distro and
|
||||
//! OCI image references, downloads them through a content cache, extracts
|
||||
//! the rootfs into a scratch directory, and runs commands inside
|
||||
//! unprivileged user/PID/mount/UTS namespaces — or boots it in a QEMU VM
|
||||
//! (see the `qemu_vm` module).
|
||||
|
||||
pub mod chroot;
|
||||
pub mod config;
|
||||
pub mod distro;
|
||||
pub mod download;
|
||||
pub mod extract;
|
||||
pub mod kernel;
|
||||
pub mod mount;
|
||||
pub mod namespace;
|
||||
pub mod qemu;
|
||||
pub mod qemu_vm;
|
||||
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)*);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -21,15 +21,13 @@ fn escape_overlay_path(path: &Path) -> Result<String> {
|
||||
Ok(s.replace('\\', "\\\\").replace(',', "\\,"))
|
||||
}
|
||||
|
||||
use crate::cli::Args;
|
||||
|
||||
/// Setup all required mounts inside the chroot
|
||||
/// Returns a TempDir that must be kept alive for the duration of the chroot
|
||||
pub fn setup_mounts(
|
||||
rootfs: &Path,
|
||||
bind_paths: &[std::path::PathBuf],
|
||||
bind_rw_paths: &[std::path::PathBuf],
|
||||
args: &Args,
|
||||
no_bind: bool,
|
||||
) -> Result<Vec<TempDir>> {
|
||||
// Keep all overlay temp dirs alive
|
||||
let mut overlay_temps: Vec<TempDir> = Vec::new();
|
||||
@@ -60,7 +58,7 @@ pub fn setup_mounts(
|
||||
}
|
||||
|
||||
// Setup overlay mounts for bind paths (read-only via overlay)
|
||||
if !args.no_bind {
|
||||
if !no_bind {
|
||||
for bind_path in bind_paths {
|
||||
// Skip if this path is also in bind_rw (bind_rw takes precedence)
|
||||
if !bind_rw_paths.contains(bind_path) {
|
||||
@@ -42,6 +42,9 @@ pub enum Arch {
|
||||
|
||||
impl Arch {
|
||||
/// Get the architecture from a string (any common naming convention)
|
||||
/// Not a FromStr impl: unrecognized names map to Arch::Unknown rather
|
||||
/// than an error, by design.
|
||||
#[allow(clippy::should_implement_trait)]
|
||||
pub fn from_str(s: &str) -> Self {
|
||||
match s {
|
||||
"amd64" | "x86_64" | "x64" => Arch::Amd64,
|
||||
Reference in New Issue
Block a user