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
This commit is contained in:
Christoph Herzog
2024-04-09 22:31:26 +02:00
parent 4368155959
commit c6ab1e5f56
13 changed files with 274 additions and 197 deletions

View File

@ -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),

View File

@ -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)]

View File

@ -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)

View File

@ -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<u128>,
/// The name of the [`BinaryPackageCommand`] which is this package's
/// entrypoint.
@ -69,7 +69,6 @@ pub struct BinaryPackage {
pub webc_fs: Arc<dyn FileSystem + Send + Sync>,
pub commands: Vec<BinaryPackageCommand>,
pub uses: Vec<String>,
pub version: Version,
pub file_system_memory_footprint: u64,
}
@ -83,10 +82,10 @@ impl BinaryPackage {
) -> Result<Self, anyhow::Error> {
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())
}
})
}

View File

@ -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;

View File

@ -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<Mutex<HashSet<String>>>,
pub has_unioned: Arc<Mutex<HashSet<PackageId>>>,
// 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(());

View File

@ -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,

View File

@ -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<String, Vec<PackageSummary>>,
named_packages: BTreeMap<String, Vec<PackageSummary>>,
hash_packages: HashMap<String, PackageSummary>,
}
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<String, Vec<PackageSummary>> {
&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<Vec<PackageSummary>, 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::<Vec<_>>(),
["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(),

View File

@ -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<Self, Self::Err> {
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(),
}
})
}
}

View File

@ -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},

View File

@ -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<String>, 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),
}
}
}

View File

@ -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::<Vec<_>>()
.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<ResolvedPackage
if let Some(entry) = &pkg.entrypoint {
tracing::trace!(
entrypoint = entry.as_str(),
parent.name=id.package_name.as_str(),
parent.version=%id.version,
parent=%id,
"Inheriting the entrypoint",
);
@ -327,16 +321,14 @@ fn resolve_package(dependency_graph: &DependencyGraph) -> Result<ResolvedPackage
entry.insert(resolved);
tracing::trace!(
command.name=cmd.name.as_str(),
pkg.name=id.package_name.as_str(),
pkg.version=%id.version,
pkg=%id,
"Discovered command",
);
}
std::collections::btree_map::Entry::Occupied(_) => {
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, &registry)
.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, &registry)
.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, &registry)
.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, &registry)
.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, &registry)
.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, &registry).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, &registry)
.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, &registry)
.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, &registry)
.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, &registry)
.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, &registry)
.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, &registry)
.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(),
}]
);
}

View File

@ -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",