From c6ab1e5f56ae54ed8ff59f901b776de358557904 Mon Sep 17 00:00:00 2001 From: Christoph Herzog Date: Tue, 9 Apr 2024 22:31:26 +0200 Subject: [PATCH] feat: Partial support for unnamed packages * Change PackageId to support both named and unnamed package * Update the package resolver logic with unnamed package support * Update the "package download" command --- lib/api/tests/function_env.rs | 2 +- lib/backend-api/src/types.rs | 1 + lib/cli/src/commands/package/download.rs | 59 ++-- lib/wasix/src/bin_factory/binary_package.rs | 15 +- lib/wasix/src/bin_factory/exec.rs | 5 +- lib/wasix/src/fs/mod.rs | 7 +- .../package_loader/load_package_tree.rs | 3 +- .../src/runtime/resolver/in_memory_source.rs | 37 ++- lib/wasix/src/runtime/resolver/inputs.rs | 20 +- lib/wasix/src/runtime/resolver/mod.rs | 2 +- lib/wasix/src/runtime/resolver/outputs.rs | 51 +++- lib/wasix/src/runtime/resolver/resolve.rs | 263 +++++++++--------- lib/wasix/src/state/env.rs | 6 +- 13 files changed, 274 insertions(+), 197 deletions(-) diff --git a/lib/api/tests/function_env.rs b/lib/api/tests/function_env.rs index 87e1904ae..2c5c52545 100644 --- a/lib/api/tests/function_env.rs +++ b/lib/api/tests/function_env.rs @@ -21,7 +21,7 @@ fn data_and_store_mut() -> Result<(), String> { ); let mut envmut = env.into_mut(&mut store); - let (mut data, mut storemut) = envmut.data_and_store_mut(); + let (data, mut storemut) = envmut.data_and_store_mut(); assert_eq!( data.global.ty(&storemut), diff --git a/lib/backend-api/src/types.rs b/lib/backend-api/src/types.rs index 9a8851c21..5633bf890 100644 --- a/lib/backend-api/src/types.rs +++ b/lib/backend-api/src/types.rs @@ -92,6 +92,7 @@ mod queries { pub updated_at: DateTime, pub tag: String, pub is_archived: bool, + pub webc_url: String, } #[derive(cynic::QueryFragment, Debug, Clone, Serialize)] diff --git a/lib/cli/src/commands/package/download.rs b/lib/cli/src/commands/package/download.rs index d0dd430a6..c7d06c21d 100644 --- a/lib/cli/src/commands/package/download.rs +++ b/lib/cli/src/commands/package/download.rs @@ -93,18 +93,48 @@ impl PackageDownload { step_num += 1; - let (full_name, version, api_endpoint, token) = match &self.package { + let (download_url, token) = match &self.package { PackageSpecifier::Registry { full_name, version } => { let endpoint = self.env.registry_endpoint()?; let version = version.to_string(); let version = if version == "*" { None } else { Some(version) }; + let token = self.env.get_token_opt().map(|x| x.to_string()); - ( - full_name, - version, - endpoint, - self.env.get_token_opt().map(|x| x.to_string()), + let package = wasmer_registry::query_package_from_registry( + endpoint.as_str(), + &full_name, + version.as_deref(), + token.as_deref(), ) + .with_context(|| { + format!( + "could not retrieve package information for package '{}' from registry '{}'", + full_name, endpoint, + ) + })?; + + let download_url = package + .pirita_url + .context("registry does provide a container download container download URL")?; + + (download_url, token) + } + PackageSpecifier::HashSha256(hash) => { + let endpoint = self.env.registry_endpoint()?; + let token = self.env.get_token_opt().map(|x| x.to_string()); + + let client = wasmer_api::WasmerClient::new(endpoint, "wasmer-cli")?; + let client = if let Some(token) = &token { + client.with_auth_token(token.clone()) + } else { + client + }; + + let rt = tokio::runtime::Runtime::new()?; + let pkg = rt.block_on(wasmer_api::query::get_package_release(&client, &hash))? + .with_context(|| format!("Package with sha256:{hash} does not exist in the registry, or is not accessible"))?; + + (pkg.webc_url, token) } PackageSpecifier::Url(url) => { bail!("cannot download a package from a URL: '{}'", url); @@ -114,23 +144,6 @@ impl PackageDownload { } }; - let package = wasmer_registry::query_package_from_registry( - api_endpoint.as_str(), - full_name, - version.as_deref(), - token.as_deref(), - ) - .with_context(|| { - format!( - "could not retrieve package information for package '{}' from registry '{}'", - full_name, api_endpoint, - ) - })?; - - let download_url = package - .pirita_url - .context("registry does provide a container download container download URL")?; - let client = reqwest::blocking::Client::new(); let mut b = client .get(download_url) diff --git a/lib/wasix/src/bin_factory/binary_package.rs b/lib/wasix/src/bin_factory/binary_package.rs index 28e562710..7e2f1670f 100644 --- a/lib/wasix/src/bin_factory/binary_package.rs +++ b/lib/wasix/src/bin_factory/binary_package.rs @@ -3,14 +3,13 @@ use std::sync::Arc; use anyhow::Context; use derivative::*; use once_cell::sync::OnceCell; -use semver::Version; use virtual_fs::FileSystem; use webc::{compat::SharedBytes, Container}; use crate::{ runtime::{ module_cache::ModuleHash, - resolver::{PackageId, PackageInfo, PackageSpecifier, ResolveError}, + resolver::{PackageId, PackageIdent, PackageInfo, PackageSpecifier, ResolveError}, }, Runtime, }; @@ -60,7 +59,8 @@ impl BinaryPackageCommand { #[derive(Derivative, Clone)] #[derivative(Debug)] pub struct BinaryPackage { - pub package_name: String, + pub id: PackageId, + pub when_cached: Option, /// The name of the [`BinaryPackageCommand`] which is this package's /// entrypoint. @@ -69,7 +69,6 @@ pub struct BinaryPackage { pub webc_fs: Arc, pub commands: Vec, pub uses: Vec, - pub version: Version, pub file_system_memory_footprint: u64, } @@ -83,10 +82,10 @@ impl BinaryPackage { ) -> Result { let source = rt.source(); let root = PackageInfo::from_manifest(container.manifest())?; - let root_id = PackageId { - package_name: root.name.clone(), + let root_id = PackageId::Named(PackageIdent { + name: root.name.clone(), version: root.version.clone(), - }; + }); let resolution = crate::runtime::resolver::resolve(&root_id, &root, &*source).await?; let pkg = rt @@ -145,7 +144,7 @@ impl BinaryPackage { if let Some(entry) = self.entrypoint_bytes() { ModuleHash::hash(entry) } else { - ModuleHash::hash(self.package_name.as_bytes()) + ModuleHash::hash(self.id.to_string()) } }) } diff --git a/lib/wasix/src/bin_factory/exec.rs b/lib/wasix/src/bin_factory/exec.rs index 21829b548..70b77dede 100644 --- a/lib/wasix/src/bin_factory/exec.rs +++ b/lib/wasix/src/bin_factory/exec.rs @@ -22,7 +22,7 @@ use wasmer_wasix_types::wasi::Errno; use super::{BinFactory, BinaryPackage}; use crate::{Runtime, WasiEnv, WasiFunctionEnv}; -#[tracing::instrument(level = "trace", skip_all, fields(%name, %binary.package_name))] +#[tracing::instrument(level = "trace", skip_all, fields(%name, package_id=%binary.id))] pub async fn spawn_exec( binary: BinaryPackage, name: &str, @@ -37,8 +37,7 @@ pub async fn spawn_exec( } else { tracing::error!( command=name, - pkg.name=%binary.package_name, - pkg.version=%binary.version, + pkg=%binary.id, "Unable to spawn a command because its package has no entrypoint", ); env.on_exit(Some(Errno::Noexec.into())).await; diff --git a/lib/wasix/src/fs/mod.rs b/lib/wasix/src/fs/mod.rs index c10420cf0..edcd7af13 100644 --- a/lib/wasix/src/fs/mod.rs +++ b/lib/wasix/src/fs/mod.rs @@ -17,6 +17,7 @@ use std::{ use crate::{ net::socket::InodeSocketKind, + runtime::resolver::PackageId, state::{Stderr, Stdin, Stdout}, }; use futures::{future::BoxFuture, Future, TryStreamExt}; @@ -492,7 +493,7 @@ pub struct WasiFs { #[cfg_attr(feature = "enable-serde", serde(skip, default))] pub root_fs: WasiFsRoot, pub root_inode: InodeGuard, - pub has_unioned: Arc>>, + pub has_unioned: Arc>>, // TODO: remove // using an atomic is a hack to enable customization after construction, @@ -565,9 +566,7 @@ impl WasiFs { &self, binary: &BinaryPackage, ) -> Result<(), virtual_fs::FsError> { - let package_name = binary.package_name.clone(); - - let needs_to_be_unioned = self.has_unioned.lock().unwrap().insert(package_name); + let needs_to_be_unioned = self.has_unioned.lock().unwrap().insert(binary.id.clone()); if !needs_to_be_unioned { return Ok(()); diff --git a/lib/wasix/src/runtime/package_loader/load_package_tree.rs b/lib/wasix/src/runtime/package_loader/load_package_tree.rs index 63b2c2622..c742a5505 100644 --- a/lib/wasix/src/runtime/package_loader/load_package_tree.rs +++ b/lib/wasix/src/runtime/package_loader/load_package_tree.rs @@ -47,8 +47,7 @@ pub async fn load_package_tree( let file_system_memory_footprint = count_file_system(&fs, Path::new("/")); let loaded = BinaryPackage { - package_name: root.package_name.clone(), - version: root.version.clone(), + id: root.clone(), when_cached: crate::syscalls::platform_clock_time_get( wasmer_wasix_types::wasi::Snapshot0Clockid::Monotonic, 1_000_000, diff --git a/lib/wasix/src/runtime/resolver/in_memory_source.rs b/lib/wasix/src/runtime/resolver/in_memory_source.rs index 0de44ace5..035473d8b 100644 --- a/lib/wasix/src/runtime/resolver/in_memory_source.rs +++ b/lib/wasix/src/runtime/resolver/in_memory_source.rs @@ -1,20 +1,22 @@ use std::{ - collections::{BTreeMap, VecDeque}, + collections::{BTreeMap, HashMap, VecDeque}, fs::File, path::{Path, PathBuf}, }; use anyhow::{Context, Error}; -use semver::Version; use crate::runtime::resolver::{PackageSpecifier, PackageSummary, QueryError, Source}; +use super::PackageId; + /// A [`Source`] that tracks packages in memory. /// /// Primarily used during testing. #[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct InMemorySource { - packages: BTreeMap>, + named_packages: BTreeMap>, + hash_packages: HashMap, } impl InMemorySource { @@ -62,7 +64,10 @@ impl InMemorySource { /// Add a new [`PackageSummary`] to the [`InMemorySource`]. pub fn add(&mut self, summary: PackageSummary) { - let summaries = self.packages.entry(summary.pkg.name.clone()).or_default(); + let summaries = self + .named_packages + .entry(summary.pkg.name.clone()) + .or_default(); summaries.push(summary); summaries.sort_by(|left, right| left.pkg.version.cmp(&right.pkg.version)); summaries.dedup_by(|left, right| left.pkg.version == right.pkg.version); @@ -76,12 +81,13 @@ impl InMemorySource { } pub fn packages(&self) -> &BTreeMap> { - &self.packages + &self.named_packages } - pub fn get(&self, package_name: &str, version: &Version) -> Option<&PackageSummary> { - let summaries = self.packages.get(package_name)?; - summaries.iter().find(|s| s.pkg.version == *version) + pub fn get(&self, id: &PackageId) -> Option<&PackageSummary> { + let ident = id.as_named()?; + let summaries = self.named_packages.get(&ident.name)?; + summaries.iter().find(|s| s.pkg.version == ident.version) } } @@ -91,7 +97,7 @@ impl Source for InMemorySource { async fn query(&self, package: &PackageSpecifier) -> Result, QueryError> { match package { PackageSpecifier::Registry { full_name, version } => { - match self.packages.get(full_name) { + match self.named_packages.get(full_name) { Some(summaries) => { let matches: Vec<_> = summaries .iter() @@ -117,6 +123,13 @@ impl Source for InMemorySource { None => Err(QueryError::NotFound), } } + PackageSpecifier::HashSha256(hash) => self + .hash_packages + .get(hash) + .map(|x| vec![x.clone()]) + .ok_or_else(|| QueryError::NoMatches { + archived_versions: Vec::new(), + }), PackageSpecifier::Url(_) | PackageSpecifier::Path(_) => Err(QueryError::Unsupported), } } @@ -153,15 +166,15 @@ mod tests { assert_eq!( source - .packages + .named_packages .keys() .map(|k| k.as_str()) .collect::>(), ["python", "sharrattj/bash", "sharrattj/coreutils"] ); - assert_eq!(source.packages["sharrattj/coreutils"].len(), 2); + assert_eq!(source.named_packages["sharrattj/coreutils"].len(), 2); assert_eq!( - source.packages["sharrattj/bash"][0], + source.named_packages["sharrattj/bash"][0], PackageSummary { pkg: PackageInfo { name: "sharrattj/bash".to_string(), diff --git a/lib/wasix/src/runtime/resolver/inputs.rs b/lib/wasix/src/runtime/resolver/inputs.rs index a232c46c1..a2141d525 100644 --- a/lib/wasix/src/runtime/resolver/inputs.rs +++ b/lib/wasix/src/runtime/resolver/inputs.rs @@ -6,7 +6,7 @@ use std::{ str::FromStr, }; -use anyhow::{Context, Error}; +use anyhow::{bail, Context, Error}; use semver::{Version, VersionReq}; use sha2::{Digest, Sha256}; use url::Url; @@ -17,6 +17,8 @@ use webc::{ use crate::runtime::resolver::PackageId; +use super::outputs::PackageIdent; + /// A reference to *some* package somewhere that the user wants to run. /// /// # Security Considerations @@ -32,6 +34,7 @@ pub enum PackageSpecifier { full_name: String, version: VersionReq, }, + HashSha256(String), Url(Url), /// A `*.webc` file on disk. Path(PathBuf), @@ -47,6 +50,14 @@ impl FromStr for PackageSpecifier { type Err = anyhow::Error; fn from_str(s: &str) -> Result { + if s.starts_with("sha256:") { + let rest = &s[7..]; + if rest.len() != 64 { + bail!("Invalid sha256:{rest} package hash: not a valid sha256 hash, expected 64 characters"); + } + return Ok(Self::HashSha256(rest.to_string())); + } + // There is no function in std for checking if a string is a valid path // and we can't do Path::new(s).exists() because that assumes the // package being specified is on the local filesystem, so let's make a @@ -112,6 +123,7 @@ impl Display for PackageSpecifier { } PackageSpecifier::Url(url) => Display::fmt(url, f), PackageSpecifier::Path(path) => write!(f, "{}", path.display()), + PackageSpecifier::HashSha256(hash) => write!(f, "sha256:{hash}"), } } } @@ -231,10 +243,10 @@ impl PackageInfo { } pub fn id(&self) -> PackageId { - PackageId { - package_name: self.name.clone(), + PackageId::Named(PackageIdent { + name: self.name.clone(), version: self.version.clone(), - } + }) } } diff --git a/lib/wasix/src/runtime/resolver/mod.rs b/lib/wasix/src/runtime/resolver/mod.rs index 2b7df9292..c83090e33 100644 --- a/lib/wasix/src/runtime/resolver/mod.rs +++ b/lib/wasix/src/runtime/resolver/mod.rs @@ -18,7 +18,7 @@ pub use self::{ }, multi_source::{MultiSource, MultiSourceStrategy}, outputs::{ - DependencyGraph, Edge, ItemLocation, Node, PackageId, Resolution, + DependencyGraph, Edge, ItemLocation, Node, PackageId, PackageIdent, Resolution, ResolvedFileSystemMapping, ResolvedPackage, }, resolve::{resolve, ResolveError}, diff --git a/lib/wasix/src/runtime/resolver/outputs.rs b/lib/wasix/src/runtime/resolver/outputs.rs index 028fc0c14..1e6f55e69 100644 --- a/lib/wasix/src/runtime/resolver/outputs.rs +++ b/lib/wasix/src/runtime/resolver/outputs.rs @@ -27,20 +27,55 @@ pub struct ItemLocation { pub package: PackageId, } -/// An identifier for a package within a dependency graph. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PackageId { - pub package_name: String, +pub struct PackageIdent { + pub name: String, pub version: Version, } +impl Display for PackageIdent { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!(f, "{}@{}", self.name, self.version) + } +} + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum PackageId { + Named(PackageIdent), + HashSha256(String), +} + +impl PackageId { + pub fn new_named(name: impl Into, version: Version) -> Self { + Self::Named(PackageIdent { + name: name.into(), + version, + }) + } + + pub fn as_named(&self) -> Option<&PackageIdent> { + if let Self::Named(v) = self { + Some(v) + } else { + None + } + } + + pub fn as_hash_sha256(&self) -> Option<&String> { + if let Self::HashSha256(v) = self { + Some(v) + } else { + None + } + } +} + impl Display for PackageId { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let PackageId { - package_name, - version, - } = self; - write!(f, "{package_name}@{version}") + match self { + Self::Named(ident) => ident.fmt(f), + Self::HashSha256(hash) => write!(f, "sha256:{}", hash), + } } } diff --git a/lib/wasix/src/runtime/resolver/resolve.rs b/lib/wasix/src/runtime/resolver/resolve.rs index 9407cd7ec..8c70d70e8 100644 --- a/lib/wasix/src/runtime/resolver/resolve.rs +++ b/lib/wasix/src/runtime/resolver/resolve.rs @@ -59,6 +59,9 @@ fn registry_error_message(specifier: &PackageSpecifier) -> String { PackageSpecifier::Registry { full_name, version } => { format!("Unable to find \"{full_name}@{version}\" in the registry") } + PackageSpecifier::HashSha256(hash) => { + format!("Unable to find package \"{hash}\" in the registry") + } PackageSpecifier::Url(url) => format!("Unable to resolve \"{url}\""), PackageSpecifier::Path(path) => { format!("Unable to load \"{}\" from disk", path.display()) @@ -78,14 +81,7 @@ impl ResolveError { fn print_cycle(packages: &[PackageId]) -> String { packages .iter() - .map(|pkg_id| { - let PackageId { - package_name, - version, - .. - } = pkg_id; - format!("{package_name}@{version}") - }) + .map(|pkg_id| pkg_id.to_string()) .collect::>() .join(" → ") } @@ -257,15 +253,14 @@ where { let mut package_versions: BTreeMap<&str, HashSet<&Version>> = BTreeMap::new(); - for PackageId { - package_name, - version, - } in package_ids - { + for id in package_ids { + let Some(id) = id.as_named() else { + continue; + }; package_versions - .entry(package_name) + .entry(&id.name) .or_default() - .insert(version); + .insert(&id.version); } for (package_name, versions) in package_versions { @@ -304,8 +299,7 @@ fn resolve_package(dependency_graph: &DependencyGraph) -> Result Result { tracing::trace!( command.name=cmd.name.as_str(), - pkg.name=id.package_name.as_str(), - pkg.version=%id.version, + pkg=%id, "Ignoring duplicate command", ); } @@ -438,11 +430,15 @@ mod tests { registry } - fn get(&self, package: &str, version: &str) -> &PackageSummary { - let version = version.parse().unwrap(); - self.0.get(package, &version).unwrap() + fn get(&self, id: &PackageId) -> &PackageSummary { + self.0.get(id).unwrap() } + // fn get_named(&self, name: &str, version: &str) -> &PackageSummary { + // let id = PackageId::new_named(name, version.parse().unwrap()); + // self.get(&id) + // } + fn start_dependency_graph(&self) -> DependencyGraphBuilder<'_> { DependencyGraphBuilder { dependencies: BTreeMap::new(), @@ -542,16 +538,11 @@ mod tests { } impl<'source> DependencyGraphBuilder<'source> { - fn insert( - &mut self, - package: &str, - version: &str, - ) -> DependencyGraphEntryBuilder<'source, '_> { - let version = version.parse().unwrap(); - let pkg_id = self.source.get(package, &version).unwrap().package_id(); + fn insert(&mut self, id: PackageId) -> DependencyGraphEntryBuilder<'source, '_> { + let _ = self.source.get(&id).unwrap(); DependencyGraphEntryBuilder { builder: self, - pkg_id, + pkg_id: id, dependencies: BTreeMap::new(), } } @@ -562,16 +553,14 @@ mod tests { /// Using the dependency mapping that we've been building up, construct /// a dependency graph using the specified root package. - fn graph(self, root_name: &str, version: &str) -> DependencyGraph { - let version = version.parse().unwrap(); - let root_id = self.source.get(root_name, &version).unwrap().package_id(); + fn graph(self, root_id: PackageId) -> DependencyGraph { + let _ = self.source.get(&root_id).unwrap(); let mut graph = DiGraph::new(); let mut nodes = BTreeMap::new(); for id in self.dependencies.keys() { - let PackageSummary { pkg, dist } = - self.source.get(&id.package_name, &id.version).unwrap(); + let PackageSummary { pkg, dist } = self.source.get(id).unwrap(); let index = graph.add_node(Node { id: pkg.id(), pkg: pkg.clone(), @@ -608,18 +597,13 @@ mod tests { } impl<'source, 'builder> DependencyGraphEntryBuilder<'source, 'builder> { - fn with_dependency(&mut self, name: &str, version: &str) -> &mut Self { - self.with_aliased_dependency(name, name, version) + fn with_dependency(&mut self, id: &PackageId) -> &mut Self { + let name = &id.as_named().unwrap().name; + self.with_aliased_dependency(name, id) } - fn with_aliased_dependency(&mut self, alias: &str, name: &str, version: &str) -> &mut Self { - let version = version.parse().unwrap(); - let dep_id = self - .builder - .source - .get(name, &version) - .unwrap() - .package_id(); + fn with_aliased_dependency(&mut self, alias: &str, id: &PackageId) -> &mut Self { + let dep_id = self.builder.source.get(id).unwrap().package_id(); self.dependencies.insert(alias.to_string(), dep_id); self } @@ -667,14 +651,15 @@ mod tests { let mut builder = RegistryBuilder::new(); builder.register("root", "1.0.0"); let registry = builder.finish(); - let root = builder.get("root", "1.0.0"); + let id = PackageId::new_named("root", Version::parse("1.0.0").unwrap()); + let root = builder.get(&id); let resolution = resolve(&root.package_id(), &root.pkg, ®istry) .await .unwrap(); let mut dependency_graph = builder.start_dependency_graph(); - dependency_graph.insert("root", "1.0.0"); + dependency_graph.insert(id); assert_eq!(deps(&resolution), dependency_graph.finish()); assert_eq!( resolution.package, @@ -692,14 +677,15 @@ mod tests { let mut builder = RegistryBuilder::new(); builder.register("root", "1.0.0").with_command("asdf"); let registry = builder.finish(); - let root = builder.get("root", "1.0.0"); + let id = PackageId::new_named("root", "1.0.0".parse().unwrap()); + let root = builder.get(&id); let resolution = resolve(&root.package_id(), &root.pkg, ®istry) .await .unwrap(); let mut dependency_graph = builder.start_dependency_graph(); - dependency_graph.insert("root", "1.0.0"); + dependency_graph.insert(id.clone()); assert_eq!(deps(&resolution), dependency_graph.finish()); assert_eq!( resolution.package, @@ -725,17 +711,17 @@ mod tests { .with_dependency("dep", "=1.0.0"); builder.register("dep", "1.0.0"); let registry = builder.finish(); - let root = builder.get("root", "1.0.0"); + let id = PackageId::new_named("root", "1.0.0".parse().unwrap()); + let root = builder.get(&id); let resolution = resolve(&root.package_id(), &root.pkg, ®istry) .await .unwrap(); + let dep_id = PackageId::new_named("dep", "1.0.0".parse().unwrap()); let mut dependency_graph = builder.start_dependency_graph(); - dependency_graph - .insert("root", "1.0.0") - .with_dependency("dep", "1.0.0"); - dependency_graph.insert("dep", "1.0.0"); + dependency_graph.insert(id.clone()).with_dependency(&dep_id); + dependency_graph.insert(dep_id.clone()); assert_eq!(deps(&resolution), dependency_graph.finish()); assert_eq!( resolution.package, @@ -750,6 +736,10 @@ mod tests { #[tokio::test] async fn linear_dependency_chain() { + let first_id = PackageId::new_named("first", "1.0.0".parse().unwrap()); + let second_id = PackageId::new_named("second", "1.0.0".parse().unwrap()); + let third_id = PackageId::new_named("third", "1.0.0".parse().unwrap()); + let mut builder = RegistryBuilder::new(); builder .register("first", "1.0.0") @@ -759,7 +749,7 @@ mod tests { .with_dependency("third", "=1.0.0"); builder.register("third", "1.0.0"); let registry = builder.finish(); - let root = builder.get("first", "1.0.0"); + let root = builder.get(&first_id); let resolution = resolve(&root.package_id(), &root.pkg, ®istry) .await @@ -767,12 +757,12 @@ mod tests { let mut dependency_graph = builder.start_dependency_graph(); dependency_graph - .insert("first", "1.0.0") - .with_dependency("second", "1.0.0"); + .insert(first_id.clone()) + .with_dependency(&second_id); dependency_graph - .insert("second", "1.0.0") - .with_dependency("third", "1.0.0"); - dependency_graph.insert("third", "1.0.0"); + .insert(second_id.clone()) + .with_dependency(&third_id); + dependency_graph.insert(third_id.clone()); assert_eq!(deps(&resolution), dependency_graph.finish()); assert_eq!( resolution.package, @@ -787,6 +777,7 @@ mod tests { #[tokio::test] async fn pick_the_latest_dependency_when_multiple_are_possible() { + let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap()); let mut builder = RegistryBuilder::new(); builder .register("root", "1.0.0") @@ -795,17 +786,18 @@ mod tests { builder.register("dep", "1.0.1"); builder.register("dep", "1.0.2"); let registry = builder.finish(); - let root = builder.get("root", "1.0.0"); + let root = builder.get(&root_id); let resolution = resolve(&root.package_id(), &root.pkg, ®istry) .await .unwrap(); + let dep_id = PackageId::new_named("dep", "1.0.2".parse().unwrap()); let mut dependency_graph = builder.start_dependency_graph(); dependency_graph - .insert("root", "1.0.0") - .with_dependency("dep", "1.0.2"); - dependency_graph.insert("dep", "1.0.2"); + .insert(root_id.clone()) + .with_dependency(&dep_id); + dependency_graph.insert(dep_id.clone()); assert_eq!(deps(&resolution), dependency_graph.finish()); assert_eq!( resolution.package, @@ -820,6 +812,7 @@ mod tests { #[tokio::test] async fn version_merging_isnt_implemented_yet() { + let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap()); let mut builder = RegistryBuilder::new(); builder .register("root", "1.0.0") @@ -836,7 +829,7 @@ mod tests { builder.register("common", "1.2.0"); builder.register("common", "1.5.0"); let registry = builder.finish(); - let root = builder.get("root", "1.0.0"); + let root = builder.get(&root_id); let result = resolve(&root.package_id(), &root.pkg, ®istry).await; @@ -861,6 +854,11 @@ mod tests { #[tokio::test] #[ignore = "Version merging isn't implemented"] async fn merge_compatible_versions() { + let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap()); + let first_id = PackageId::new_named("first", "1.0.0".parse().unwrap()); + let second_id = PackageId::new_named("second", "1.0.0".parse().unwrap()); + let common_id = PackageId::new_named("common", "1.2.0".parse().unwrap()); + let mut builder = RegistryBuilder::new(); builder .register("root", "1.0.0") @@ -877,7 +875,7 @@ mod tests { builder.register("common", "1.2.0"); builder.register("common", "1.5.0"); let registry = builder.finish(); - let root = builder.get("root", "1.0.0"); + let root = builder.get(&root_id); let resolution = resolve(&root.package_id(), &root.pkg, ®istry) .await @@ -885,16 +883,16 @@ mod tests { let mut dependency_graph = builder.start_dependency_graph(); dependency_graph - .insert("root", "1.0.0") - .with_dependency("first", "1.0.0") - .with_dependency("second", "1.0.0"); + .insert(root_id.clone()) + .with_dependency(&first_id) + .with_dependency(&second_id); dependency_graph - .insert("first", "1.0.0") - .with_dependency("common", "1.2.0"); + .insert(first_id.clone()) + .with_dependency(&common_id); dependency_graph - .insert("second", "1.0.0") - .with_dependency("common", "1.2.0"); - dependency_graph.insert("common", "1.2.0"); + .insert(second_id.clone()) + .with_dependency(&common_id); + dependency_graph.insert(common_id.clone()); assert_eq!(deps(&resolution), dependency_graph.finish()); assert_eq!( resolution.package, @@ -909,6 +907,9 @@ mod tests { #[tokio::test] async fn commands_from_dependencies_end_up_in_the_package() { + let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap()); + let first_id = PackageId::new_named("first", "1.0.0".parse().unwrap()); + let second_id = PackageId::new_named("second", "1.0.0".parse().unwrap()); let mut builder = RegistryBuilder::new(); builder .register("root", "1.0.0") @@ -921,7 +922,7 @@ mod tests { .register("second", "1.0.0") .with_command("second-command"); let registry = builder.finish(); - let root = builder.get("root", "1.0.0"); + let root = builder.get(&root_id); let resolution = resolve(&root.package_id(), &root.pkg, ®istry) .await @@ -929,11 +930,11 @@ mod tests { let mut dependency_graph = builder.start_dependency_graph(); dependency_graph - .insert("root", "1.0.0") - .with_dependency("first", "1.0.0") - .with_dependency("second", "1.0.0"); - dependency_graph.insert("first", "1.0.0"); - dependency_graph.insert("second", "1.0.0"); + .insert(root_id.clone()) + .with_dependency(&first_id) + .with_dependency(&second_id); + dependency_graph.insert(first_id.clone()); + dependency_graph.insert(second_id.clone()); assert_eq!(deps(&resolution), dependency_graph.finish()); assert_eq!( resolution.package, @@ -942,11 +943,11 @@ mod tests { commands: map! { "first-command" => ItemLocation { name: "first-command".to_string(), - package: builder.get("first", "1.0.0").package_id(), + package: builder.get(&first_id).package_id(), }, "second-command" => ItemLocation { name: "second-command".to_string(), - package: builder.get("second", "1.0.0").package_id(), + package: builder.get(&second_id).package_id(), }, }, entrypoint: None, @@ -958,6 +959,8 @@ mod tests { #[tokio::test] #[ignore = "TODO: Re-order the way commands are resolved"] async fn commands_in_root_shadow_their_dependencies() { + let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap()); + let dep_id = PackageId::new_named("dep", "1.0.0".parse().unwrap()); let mut builder = RegistryBuilder::new(); builder .register("root", "1.0.0") @@ -965,7 +968,7 @@ mod tests { .with_command("command"); builder.register("dep", "1.0.0").with_command("command"); let registry = builder.finish(); - let root = builder.get("root", "1.0.0"); + let root = builder.get(&root_id); let resolution = resolve(&root.package_id(), &root.pkg, ®istry) .await @@ -973,9 +976,9 @@ mod tests { let mut dependency_graph = builder.start_dependency_graph(); dependency_graph - .insert("root", "1.0.0") - .with_dependency("dep", "1.0.0"); - dependency_graph.insert("dep", "1.0.0"); + .insert(root_id.clone()) + .with_dependency(&dep_id); + dependency_graph.insert(dep_id.clone()); assert_eq!(deps(&resolution), dependency_graph.finish()); assert_eq!( resolution.package, @@ -984,7 +987,7 @@ mod tests { commands: map! { "command" => ItemLocation { name: "command".to_string(), - package: builder.get("root", "1.0.0").package_id(), + package: builder.get(&root_id).package_id(), }, }, entrypoint: None, @@ -995,6 +998,9 @@ mod tests { #[tokio::test] async fn cyclic_dependencies() { + let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap()); + let dep_id = PackageId::new_named("dep", "1.0.0".parse().unwrap()); + let mut builder = RegistryBuilder::new(); builder .register("root", "1.0.0") @@ -1003,7 +1009,7 @@ mod tests { .register("dep", "1.0.0") .with_dependency("root", "=1.0.0"); let registry = builder.finish(); - let root = builder.get("root", "1.0.0"); + let root = builder.get(&root_id); let err = resolve(&root.package_id(), &root.pkg, ®istry) .await @@ -1013,15 +1019,18 @@ mod tests { assert_eq!( cycle, [ - builder.get("root", "1.0.0").package_id(), - builder.get("dep", "1.0.0").package_id(), - builder.get("root", "1.0.0").package_id(), + builder.get(&root_id).package_id(), + builder.get(&dep_id).package_id(), + builder.get(&root_id).package_id(), ] ); } #[tokio::test] async fn entrypoint_is_inherited() { + let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap()); + let dep_id = PackageId::new_named("dep", "1.0.0".parse().unwrap()); + let mut builder = RegistryBuilder::new(); builder .register("root", "1.0.0") @@ -1031,7 +1040,7 @@ mod tests { .with_command("entry") .with_entrypoint("entry"); let registry = builder.finish(); - let root = builder.get("root", "1.0.0"); + let root = builder.get(&root_id); let resolution = resolve(&root.package_id(), &root.pkg, ®istry) .await @@ -1044,7 +1053,7 @@ mod tests { commands: map! { "entry" => ItemLocation { name: "entry".to_string(), - package: builder.get("dep", "1.0.0").package_id(), + package: builder.get(&dep_id).package_id(), }, }, entrypoint: Some("entry".to_string()), @@ -1055,6 +1064,7 @@ mod tests { #[tokio::test] async fn infer_entrypoint_if_unspecified_and_only_one_command_in_root_package() { + let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap()); let mut builder = RegistryBuilder::new(); builder .register("root", "1.0.0") @@ -1062,7 +1072,7 @@ mod tests { .with_dependency("dep", "=1.0.0"); builder.register("dep", "1.0.0").with_command("entry"); let registry = builder.finish(); - let root = builder.get("root", "1.0.0"); + let root = builder.get(&root_id); let resolution = resolve(&root.package_id(), &root.pkg, ®istry) .await @@ -1074,18 +1084,9 @@ mod tests { #[test] fn cyclic_error_message() { let cycle = [ - PackageId { - package_name: "root".to_string(), - version: "1.0.0".parse().unwrap(), - }, - PackageId { - package_name: "dep".to_string(), - version: "1.0.0".parse().unwrap(), - }, - PackageId { - package_name: "root".to_string(), - version: "1.0.0".parse().unwrap(), - }, + PackageId::new_named("root", "1.0.0".parse().unwrap()), + PackageId::new_named("dep", "1.0.0".parse().unwrap()), + PackageId::new_named("root", "1.0.0".parse().unwrap()), ]; let message = print_cycle(&cycle); @@ -1095,11 +1096,12 @@ mod tests { #[test] fn filesystem_with_one_package_and_no_fs_tables() { + let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap()); let mut builder = RegistryBuilder::new(); builder.register("root", "1.0.0"); let mut dep_builder = builder.start_dependency_graph(); - dep_builder.insert("root", "1.0.0"); - let graph = dep_builder.graph("root", "1.0.0"); + dep_builder.insert(root_id.clone()); + let graph = dep_builder.graph(root_id.clone()); let pkg = resolve_package(&graph).unwrap(); @@ -1108,13 +1110,14 @@ mod tests { #[test] fn filesystem_with_one_package_and_one_fs_tables() { + let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap()); let mut builder = RegistryBuilder::new(); builder .register("root", "1.0.0") .with_fs_mapping("atom", "/publisher/lib", "/lib"); let mut dep_builder = builder.start_dependency_graph(); - dep_builder.insert("root", "1.0.0"); - let graph = dep_builder.graph("root", "1.0.0"); + dep_builder.insert(root_id.clone()); + let graph = dep_builder.graph(root_id.clone()); let pkg = resolve_package(&graph).unwrap(); @@ -1124,13 +1127,17 @@ mod tests { mount_path: PathBuf::from("/lib"), original_path: "/publisher/lib".to_string(), volume_name: "atom".to_string(), - package: builder.get("root", "1.0.0").package_id(), + package: builder.get(&root_id).package_id(), }] ); } #[test] fn merge_fs_mappings_from_multiple_packages() { + let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap()); + let first_id = PackageId::new_named("first", "1.0.0".parse().unwrap()); + let second_id = PackageId::new_named("second", "1.0.0".parse().unwrap()); + let mut builder = RegistryBuilder::new(); builder .register("root", "1.0.0") @@ -1149,12 +1156,12 @@ mod tests { ); let mut dep_builder = builder.start_dependency_graph(); dep_builder - .insert("root", "1.0.0") - .with_dependency("first", "1.0.0") - .with_dependency("second", "1.0.0"); - dep_builder.insert("first", "1.0.0"); - dep_builder.insert("second", "1.0.0"); - let graph = dep_builder.graph("root", "1.0.0"); + .insert(root_id.clone()) + .with_dependency(&first_id) + .with_dependency(&second_id); + dep_builder.insert(first_id.clone()); + dep_builder.insert(second_id.clone()); + let graph = dep_builder.graph(root_id.clone()); let pkg = resolve_package(&graph).unwrap(); @@ -1165,19 +1172,19 @@ mod tests { mount_path: PathBuf::from("/root"), original_path: "/root".to_string(), volume_name: "atom".to_string(), - package: builder.get("root", "1.0.0").package_id(), + package: builder.get(&root_id).package_id(), }, ResolvedFileSystemMapping { mount_path: PathBuf::from("/usr/local/lib/second"), original_path: "/usr/local/lib/second".to_string(), volume_name: "atom".to_string(), - package: builder.get("second", "1.0.0").package_id(), + package: builder.get(&second_id).package_id(), }, ResolvedFileSystemMapping { mount_path: PathBuf::from("/usr/local/lib/first"), volume_name: "atom".to_string(), original_path: "/usr/local/lib/first".to_string(), - package: builder.get("first", "1.0.0").package_id(), + package: builder.get(&first_id).package_id(), } ] ); @@ -1185,6 +1192,8 @@ mod tests { #[test] fn use_fs_mapping_from_dependency() { + let root_id = PackageId::new_named("root", "1.0.0".parse().unwrap()); + let dep_id = PackageId::new_named("dep", "1.0.0".parse().unwrap()); let mut builder = RegistryBuilder::new(); builder .register("root", "1.0.0") @@ -1192,11 +1201,9 @@ mod tests { .with_fs_mapping_from_dependency("dep-volume", "/root", "/root", "dep"); builder.register("dep", "1.0.0"); let mut dep_builder = builder.start_dependency_graph(); - dep_builder - .insert("root", "1.0.0") - .with_dependency("dep", "1.0.0"); - dep_builder.insert("dep", "1.0.0"); - let graph = dep_builder.graph("root", "1.0.0"); + dep_builder.insert(root_id.clone()).with_dependency(&dep_id); + dep_builder.insert(dep_id.clone()); + let graph = dep_builder.graph(root_id.clone()); let pkg = resolve_package(&graph).unwrap(); @@ -1206,7 +1213,7 @@ mod tests { mount_path: PathBuf::from("/root"), original_path: "/root".to_string(), volume_name: "dep-volume".to_string(), - package: builder.get("dep", "1.0.0").package_id(), + package: builder.get(&dep_id).package_id(), }] ); } diff --git a/lib/wasix/src/state/env.rs b/lib/wasix/src/state/env.rs index 6f7237e2c..289e26c57 100644 --- a/lib/wasix/src/state/env.rs +++ b/lib/wasix/src/state/env.rs @@ -1043,7 +1043,7 @@ impl WasiEnv { /// [cmd-atom]: crate::bin_factory::BinaryPackageCommand::atom() /// [pkg-fs]: crate::bin_factory::BinaryPackage::webc_fs pub fn use_package(&self, pkg: &BinaryPackage) -> Result<(), WasiStateCreationError> { - tracing::trace!(packagae=%pkg.package_name, "merging package dependency into wasi environment"); + tracing::trace!(package=%pkg.id, "merging package dependency into wasi environment"); let root_fs = &self.state.fs.root_fs; // We first need to copy any files in the package over to the @@ -1089,7 +1089,7 @@ impl WasiEnv { { tracing::debug!( "failed to add package [{}] command [{}] - {}", - pkg.package_name, + pkg.id, command.name(), err ); @@ -1115,7 +1115,7 @@ impl WasiEnv { .set_binary(path.as_os_str().to_string_lossy().as_ref(), package); tracing::debug!( - package=%pkg.package_name, + package=%pkg.id, command_name=command.name(), path=%path.display(), "Injected a command into the filesystem",