Skip to content

Commit

Permalink
Packages using sharrattj/static-web-server work (fixes #3748)
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael-F-Bryan authored and theduke committed May 25, 2023
1 parent 093ff38 commit 771c9cc
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 46 deletions.
20 changes: 17 additions & 3 deletions lib/cli/src/commands/run/wasi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use wasmer::{
};
use wasmer_registry::WasmerConfig;
use wasmer_wasix::{
bin_factory::BinaryPackage,
default_fs_backing, get_wasi_versions,
http::HttpClient,
os::{tty_sys::SysTty, TtyBridge},
Expand All @@ -21,7 +22,7 @@ use wasmer_wasix::{
runtime::{
module_cache::{FileSystemCache, ModuleCache},
package_loader::{BuiltinLoader, PackageLoader},
resolver::{InMemorySource, MultiSourceRegistry, Registry, WapmSource},
resolver::{InMemorySource, MultiSourceRegistry, PackageSpecifier, Registry, WapmSource},
task_manager::tokio::TokioTaskManager,
},
types::__WASI_STDIN_FILENO,
Expand All @@ -30,7 +31,9 @@ use wasmer_wasix::{
WasiRuntime, WasiVersion,
};

use crate::utils::{parse_envvar, parse_mapdir};
use crate::{
utils::{parse_envvar, parse_mapdir},
};

use super::RunWithPathBuf;

Expand Down Expand Up @@ -165,11 +168,22 @@ impl Wasi {
.prepare_runtime(engine)
.context("Unable to prepare the wasi runtime")?;

let mut uses = Vec::new();
for name in &self.uses {
let specifier = PackageSpecifier::parse(name)
.with_context(|| format!("Unable to parse \"{name}\" as a package specifier"))?;
let pkg = rt
.task_manager()
.block_on(BinaryPackage::from_registry(&specifier, &rt))
.with_context(|| format!("Unable to load \"{name}\""))?;
uses.push(pkg);
}

let builder = WasiEnv::builder(program_name)
.runtime(Arc::new(rt))
.args(args)
.envs(self.env_vars.clone())
.uses(self.uses.clone())
.uses(uses)
.map_commands(map_commands);

let mut builder = if wasmer_wasix::is_wasix_module(module) {
Expand Down
7 changes: 3 additions & 4 deletions lib/cli/src/commands/run_unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,12 +325,11 @@ fn infer_webc_entrypoint(pkg: &BinaryPackage) -> Result<&str, Error> {
[] => anyhow::bail!("The WEBC file doesn't contain any executable commands"),
[one] => Ok(one.name()),
[..] => {
let mut commands: Vec<_> = pkg.commands.iter().map(|cmd| cmd.name()).collect();
commands.sort();
anyhow::bail!(
"Unable to determine the WEBC file's entrypoint. Please choose one of {:?}",
pkg.commands
.iter()
.map(|cmd| cmd.name())
.collect::<Vec<_>>(),
commands,
);
}
}
Expand Down
7 changes: 4 additions & 3 deletions lib/wasi/src/runners/wasi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use std::sync::Arc;

use anyhow::{Context, Error};
use serde::{Deserialize, Serialize};
use virtual_fs::FileSystem;
use webc::metadata::{annotations::Wasi, Command};

use crate::{
Expand Down Expand Up @@ -104,13 +103,15 @@ impl WasiRunner {
&self,
program_name: &str,
wasi: &Wasi,
container_fs: Arc<dyn FileSystem + Send + Sync>,
pkg: &BinaryPackage,
runtime: Arc<dyn WasiRuntime + Send + Sync>,
) -> Result<WasiEnvBuilder, anyhow::Error> {
let mut builder = WasiEnvBuilder::new(program_name);
let container_fs = Arc::clone(&pkg.webc_fs);
self.wasi
.prepare_webc_env(&mut builder, container_fs, wasi)?;

builder.add_webc(pkg.clone());
builder.set_runtime(runtime);

Ok(builder)
Expand Down Expand Up @@ -142,7 +143,7 @@ impl crate::runners::Runner for WasiRunner {
let module = crate::runners::compile_module(cmd.atom(), &*runtime)?;
let mut store = runtime.new_store();

self.prepare_webc_env(command_name, &wasi, Arc::clone(&pkg.webc_fs), runtime)?
self.prepare_webc_env(command_name, &wasi, pkg, runtime)?
.run_with_store(module, &mut store)?;

Ok(())
Expand Down
6 changes: 6 additions & 0 deletions lib/wasi/src/runtime/resolver/inputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ pub enum PackageSpecifier {
Path(PathBuf),
}

impl PackageSpecifier {
pub fn parse(s: &str) -> Result<Self, anyhow::Error> {
s.parse()
}
}

impl FromStr for PackageSpecifier {
type Err = anyhow::Error;

Expand Down
25 changes: 14 additions & 11 deletions lib/wasi/src/state/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use wasmer_wasix_types::wasi::Errno;
#[cfg(feature = "sys")]
use crate::PluggableRuntime;
use crate::{
bin_factory::BinFactory,
bin_factory::{BinFactory, BinaryPackage},
capabilities::Capabilities,
fs::{WasiFs, WasiFsRoot, WasiInodes},
os::task::control_plane::{ControlPlaneConfig, ControlPlaneError, WasiControlPlane},
Expand Down Expand Up @@ -62,7 +62,7 @@ pub struct WasiEnvBuilder {
pub(super) runtime: Option<Arc<dyn crate::WasiRuntime + Send + Sync + 'static>>,

/// List of webc dependencies to be injected.
pub(super) uses: Vec<String>,
pub(super) uses: Vec<BinaryPackage>,

/// List of host commands to map into the WASI instance.
pub(super) map_commands: HashMap<String, PathBuf>,
Expand Down Expand Up @@ -270,22 +270,25 @@ impl WasiEnvBuilder {
}

/// Adds a container this module inherits from
pub fn use_webc<Name>(mut self, webc: Name) -> Self
where
Name: AsRef<str>,
{
self.uses.push(webc.as_ref().to_string());
pub fn use_webc(mut self, pkg: BinaryPackage) -> Self {
self.add_webc(pkg);
self
}

/// Adds a container this module inherits from
pub fn add_webc(&mut self, pkg: BinaryPackage) -> &mut Self {
self.uses.push(pkg);
self
}

/// Adds a list of other containers this module inherits from
pub fn uses<I>(mut self, uses: I) -> Self
where
I: IntoIterator<Item = String>,
I: IntoIterator<Item = BinaryPackage>,
{
uses.into_iter().for_each(|inherit| {
self.uses.push(inherit);
});
for pkg in uses {
self.add_webc(pkg);
}
self
}

Expand Down
14 changes: 9 additions & 5 deletions lib/wasi/src/state/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ impl WasiInstanceHandles {
pub struct WasiEnvInit {
pub(crate) state: WasiState,
pub runtime: Arc<dyn WasiRuntime + Send + Sync>,
pub webc_dependencies: Vec<String>,
pub webc_dependencies: Vec<BinaryPackage>,
pub mapped_commands: HashMap<String, PathBuf>,
pub bin_factory: BinFactory,
pub capabilities: Capabilities,
Expand Down Expand Up @@ -424,7 +424,9 @@ impl WasiEnv {
env.owned_handles.push(thread);

// TODO: should not be here - should be callers responsibility!
env.uses(init.webc_dependencies)?;
for pkg in &init.webc_dependencies {
env.use_package(pkg)?;
}

#[cfg(feature = "sys")]
env.map_commands(init.mapped_commands.clone())?;
Expand Down Expand Up @@ -885,7 +887,7 @@ impl WasiEnv {
}
}

todo!();
Ok(())
}

/// Given a list of packages, load them from the registry and make them
Expand All @@ -897,11 +899,13 @@ impl WasiEnv {
let rt = self.runtime();

for package_name in uses {
let specifier: PackageSpecifier = package_name.parse().unwrap();
let specifier = package_name
.parse::<PackageSpecifier>()
.map_err(|e| WasiStateCreationError::WasiIncludePackageError(e.to_string()))?;
let pkg = rt
.task_manager()
.block_on(BinaryPackage::from_registry(&specifier, rt))
.unwrap();
.map_err(|e| WasiStateCreationError::WasiIncludePackageError(e.to_string()))?;
self.use_package(&pkg)?;
}

Expand Down
97 changes: 77 additions & 20 deletions tests/integration/cli/tests/run_unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::{
use assert_cmd::{assert::Assert, prelude::OutputAssertExt};
use once_cell::sync::Lazy;
use predicates::str::contains;
use rand::Rng;
use reqwest::{blocking::Client, IntoUrl};
use tempfile::TempDir;
use wasmer_integration_tests_cli::get_wasmer_path;
Expand Down Expand Up @@ -41,7 +42,6 @@ fn wasmer_run_unstable() -> std::process::Command {

mod webc_on_disk {
use super::*;
use rand::Rng;

#[test]
#[cfg_attr(
Expand Down Expand Up @@ -105,14 +105,26 @@ mod webc_on_disk {
)]
fn wasi_runner_with_dependencies() {
let mut cmd = wasmer_run_unstable();
cmd.arg(fixtures::hello()).arg("--").arg("--help");
let child = JoinableChild::spawn(cmd);

std::thread::sleep(std::time::Duration::from_secs(30));
let port = random_port();
cmd.arg(fixtures::hello())
.arg(format!("--env=SERVER_PORT={port}"))
.arg("--net")
.arg("--")
.arg("--log-level=info");
let mut child = JoinableChild::spawn(cmd);
child.wait_for_stderr("listening");

let assert = child.join();
// Make sure we get the page we want
let html = reqwest::blocking::get(format!("http://localhost:{port}/"))
.unwrap()
.text()
.unwrap();
assert!(html.contains("<title>Hello World</title>"), "{html}");

assert.success().stdout(contains("Hello, World!"));
// and make sure our request was logged
child
.join()
.stderr(contains("incoming request: method=GET uri=/"));
}

#[test]
Expand All @@ -123,7 +135,7 @@ mod webc_on_disk {
fn webc_files_with_multiple_commands_require_an_entrypoint_flag() {
let assert = wasmer_run_unstable().arg(fixtures::wabt()).assert();

let msg = r#"Unable to determine the WEBC file's entrypoint. Please choose one of ["wat2wasm", "wast2json", "wasm2wat", "wasm-interp", "wasm-validate", "wasm-strip"]"#;
let msg = r#"Unable to determine the WEBC file's entrypoint. Please choose one of ["wasm-interp", "wasm-strip", "wasm-validate", "wasm2wat", "wast2json", "wat2wasm"]"#;
assert.failure().stderr(contains(msg));
}

Expand Down Expand Up @@ -152,7 +164,7 @@ mod webc_on_disk {
)]
fn wcgi_runner() {
// Start the WCGI server in the background
let port = rand::thread_rng().gen_range(10_000_u16..u16::MAX);
let port = random_port();
let mut cmd = wasmer_run_unstable();
cmd.arg(format!("--addr=127.0.0.1:{port}"))
.arg(fixtures::static_server());
Expand Down Expand Up @@ -188,7 +200,7 @@ mod webc_on_disk {
let temp = TempDir::new().unwrap();
std::fs::write(temp.path().join("file.txt"), "Hello, World!").unwrap();
// Start the WCGI server in the background
let port = rand::thread_rng().gen_range(10_000_u16..u16::MAX);
let port = random_port();
let mut cmd = wasmer_run_unstable();
cmd.arg(format!("--addr=127.0.0.1:{port}"))
.arg(format!("--mapdir=/path/to:{}", temp.path().display()))
Expand Down Expand Up @@ -346,6 +358,24 @@ mod remote_webc {

assert.success().stdout(contains("Hello, World!"));
}

#[test]
#[cfg_attr(
all(target_env = "musl", target_os = "linux"),
ignore = "wasmer run-unstable segfaults on musl"
)]
fn bash_using_coreutils() {
let assert = wasmer_run_unstable()
.arg("https://wapm.io/sharrattj/bash")
.arg("--entrypoint=bash")
.arg("--use=https://wapm.io/sharrattj/bash")
.arg("--")
.arg("-c")
.arg("ls /bin")
.assert();

assert.success().stdout(contains("Hello, World!"));
}
}

mod fixtures {
Expand Down Expand Up @@ -415,23 +445,25 @@ impl JoinableChild {
/// Keep reading lines from the child's stdout until a line containing the
/// desired text is found.
fn wait_for_stdout(&mut self, text: &str) -> String {
let stderr = self
let stdout = self
.0
.as_mut()
.and_then(|child| child.stdout.as_mut())
.unwrap();

let mut all_output = String::new();
wait_for(text, stdout)
}

loop {
let line = read_line(stderr).unwrap();
let found = line.contains(text);
all_output.push_str(&line);
/// Keep reading lines from the child's stderr until a line containing the
/// desired text is found.
fn wait_for_stderr(&mut self, text: &str) -> String {
let stderr = self
.0
.as_mut()
.and_then(|child| child.stderr.as_mut())
.unwrap();

if found {
return all_output;
}
}
wait_for(text, stderr)
}

/// Kill the underlying [`std::process::Child`] and get an [`Assert`] we
Expand All @@ -443,6 +475,27 @@ impl JoinableChild {
}
}

fn wait_for(text: &str, reader: &mut dyn Read) -> String {
let mut all_output = String::new();

loop {
let line = read_line(reader).unwrap();

if line.is_empty() {
eprintln!("=== All Output === ");
eprintln!("{all_output}");
panic!("EOF before \"{text}\" was found");
}

let found = line.contains(text);
all_output.push_str(&line);

if found {
return all_output;
}
}
}

fn read_line(reader: &mut dyn Read) -> Result<String, std::io::Error> {
let mut line = Vec::new();

Expand Down Expand Up @@ -511,3 +564,7 @@ fn http_get(url: impl IntoUrl) -> Result<String, reqwest::Error> {

panic!("Didn't receive a response from \"{url}\" within the allocated time");
}

fn random_port() -> u16 {
rand::thread_rng().gen_range(10_000_u16..u16::MAX)
}

0 comments on commit 771c9cc

Please sign in to comment.